diff --git a/Modules/Internal/Http/Controllers/Api/ClaimController.php b/Modules/Internal/Http/Controllers/Api/ClaimController.php index 4165c2ff..832cddc3 100644 --- a/Modules/Internal/Http/Controllers/Api/ClaimController.php +++ b/Modules/Internal/Http/Controllers/Api/ClaimController.php @@ -26,6 +26,9 @@ use Box\Spout\Writer\Common\Creator\Style\StyleBuilder; use Box\Spout\Common\Entity\Style\CellAlignment; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\View; +use Maatwebsite\Excel\Facades\Excel; +use Illuminate\Support\Facades\Storage; +use App\Exports\FailedRowsExport; // Import class export Excel use Modules\Internal\Transformers\RequestLogResource; @@ -119,6 +122,94 @@ class ClaimController extends Controller return response()->json(Helper::paginateResources($results)); } + public function downloadTemplate() + { + return Helper::responseJson([ + 'file_name' => "Template - Claim - Management.xlsx", + "file_url" => url('files/Template - Claim - Management.xlsx') + ]); + } + + public function import(Request $request) + { + if ($request->hasFile('file')) { + $file = $request->file('file'); + $data = Excel::toArray([], $file); + + $processedData = $this->processCategoryNames($data); + + $importedRows = 0; + $failedRows = []; + + foreach ($processedData as $row) { + try { + $affectedRows = DB::table('claim_requests') + ->where('code','=', $row['code']) + ->where('claim_management','=', 1) + ->update([ + 'status_claim_management' => $row['qc'] == 'Y' ? 'approved' : 'declined', + 'reason_decline' => $row['reason'] ? $row['reason'] : null, + 'approval_by_claim_management' => auth()->user()->id, + 'approval_date_claim_management' => date('Y-m-d H:i:s'), + ]); + + if ($affectedRows === 0) { + $failedRows[] = $row; + } else { + $importedRows += $affectedRows; + } + } catch (\Exception $e) { + $failedRows[] = $row; + } + } + + $response = [ + 'message' => 'File uploaded and data saved to database', + 'data' => [ + 'total_success_row' => $importedRows, + 'total_failed_row' => count($failedRows), + 'failed_rows' => $failedRows, + ], + ]; + + return response()->json($response); + } + + return response()->json(['error' => 'No file uploaded.']); + } + + private function processCategoryNames($data) + { + $header = []; + $row = []; + for ($i = 1; $i < count($data[0]); $i++) { + $row[] = $data[0][$i]; + $header[] = $data[0][0]; + } + + $filed = []; + foreach ($header[0] as $value) + { + $modelColumn = strtolower(preg_replace('/\s+/', '_', trim($value))); + $modelColumn = str_replace(['*', ' '], '', $modelColumn); + if($modelColumn) + { + $filed[] = $modelColumn; + } + } + + $result = []; + foreach ($row as $subarray) { + $trimmedSubarray = []; + for ($i = 0; $i < count($filed); $i++) { + $trimmedSubarray[$filed[$i]] = $subarray[$i] ? $subarray[$i] : null; + } + + $result[] = $trimmedSubarray; + } + return $result; + } + public function exportClaimManagement(Request $request) { $start_date = $request->input('start_date') ? $request->input('start_date') : 'all'; @@ -280,6 +371,72 @@ class ClaimController extends Controller "file_url" => url('files/Report-Data-Claim-Management-'. $start_date.'-'.$end_date.'.xlsx') ]); } + + public function exportFiled(Request $request) + { + $writer = WriterEntityFactory::createXLSXWriter(); + $writer->openToFile(public_path('files/Report-Data-Filed-Import.xlsx')); + $header = [ + 'Code*', + 'QC*', + 'Reason' + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $headerRow = WriterEntityFactory::createRowFromArray($header, $style); + $writer->addRow($headerRow); + // ============================ + + foreach($request->params as $item) + { + + $rowData = [ + $item['code'], + $item['qc'], + $item['reason'] + ]; + $style = (new StyleBuilder()) + //->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + $row = WriterEntityFactory::createRowFromArray($rowData, $style); + $writer->addRow($row); + } + $footer = [ + '', + '', + '', + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $footerRow = WriterEntityFactory::createRowFromArray($footer, $style); + $writer->addRow($footerRow); + + $writer->close(); + + return Helper::responseJson([ + 'file_name' => 'Report-Data-Filed-Import', + "file_url" => url('files/Report-Data-Filed-Import.xlsx') + ]); + } public function getProvider(Request $request) { $providers = DB::table('organizations') diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 5df7062b..b9353597 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -235,6 +235,9 @@ Route::prefix('internal')->group(function () { Route::post('claims/{claim_id}/set-final-encounter', [ClaimEncounterController::class, 'setFinalEncounter']); Route::get('claims', [ClaimController::class, 'index']); + Route::get('claims/download-template', [ClaimController::class, 'downloadTemplate']); + Route::post('claims/import', [ClaimController::class, 'import']); + Route::post('claims/exportFiled/', [ClaimController::class, 'exportFiled']); Route::get('claims/export-claim-management', [ClaimController::class, 'exportClaimManagement']); Route::get('claims/get-provider', [ClaimController::class, 'getProvider']); Route::post('claims/{id}/update-items', [ClaimController::class, 'updateItems'])->name('claim.update-items'); diff --git a/frontend/dashboard/src/pages/Claims/List.tsx b/frontend/dashboard/src/pages/Claims/List.tsx index 1da79aca..5de5511c 100644 --- a/frontend/dashboard/src/pages/Claims/List.tsx +++ b/frontend/dashboard/src/pages/Claims/List.tsx @@ -291,9 +291,9 @@ const dummyServices = [ }, ]; - const handleClick = () => { + // const handleClick = () => { - } + // } @@ -393,6 +393,105 @@ const dummyServices = [ function send_bulk(id) { return axios.post(`claims/${id}/${approve}`, { reasonDecline: reasonDecline }); } + + + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + const importHospital = useRef(null); + const [currentImportFileName, setCurrentImportFileName] = useState(null); + const [importLoading, setImportLoading] = useState(false); + const [importResult, setImportResult] = useState(null); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const handleImportButton = () => { + if (importHospital?.current) { + handleClose(); + importHospital.current ? importHospital.current.click() : console.log('No File selected'); + } else { + alert('No file selected'); + } + }; + const handleCancelImportButton = () => { + if(importHospital.current) + { + importHospital.current.value = ''; + importHospital.current.dispatchEvent(new Event('change', { bubbles: true })); + } +}; + const handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name); + } else { + setCurrentImportFileName(null); + } + }; + const handleUpload = () => { + if(importHospital.current && importHospital.current.files) + { + if (importHospital.current?.files.length) { + const formData = new FormData(); + formData.append('file', importHospital.current?.files[0]); + setImportLoading(true); + axios + .post('claims/import', formData) + .then((response) => { + handleCancelImportButton(); + loadDataTableData(); + setImportResult(response.data); + setImportLoading(false); + enqueueSnackbar('Success Import Hospitals', { variant: 'success' }); + }) + .catch((response) => { + enqueueSnackbar( + 'Looks like something went wrong. Please check your data and try again. ' + + response.message, + { variant: 'error' } + ); + setImportLoading(false); + }); + } else { + enqueueSnackbar('No File Selected', { variant: 'warning' }); + } + } +}; + const handleGetTemplate = () => { + axios.get('claims/download-template').then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + }); + + +}; + +const handleExportReportFiled = async () => { + + await axios + .post('claims/exportFiled', { params: importResult?.data.failed_rows }) + .then((res) => { + enqueueSnackbar('Data berhasil di Export', { + variant: 'success', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }); + setIsLoading(false) + + document.location.href = res.data.data.file_url; + }) + .catch((err) => + enqueueSnackbar('Data Gagal di Export', { + variant: 'error', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }) + + ); +}; // useEffect(() => { @@ -690,6 +789,16 @@ const dummyServices = [
+ + {!currentImportFileName && ( + <> } sx={{ p: 1.8 }} - // onClick={handleExportReport} loading={isLoadingImport} + onClick={handleClick} > Import + + + Import + + { + handleGetTemplate(); + }} + > + Download Template + + + + )} + {currentImportFileName && ( + + + + + + + + } + sx={{ p: 1.8 }} + onClick={handleUpload} + loading={importLoading} + > + Upload + + + + )} + {importResult && ( + + + Last Import Result :{' '} + + {importResult.data.total_success_row ?? 0} + {' '} + Row Processed,{' '} + + {importResult.data.total_failed_row} + {' '} + Failed + {/* {importResult.data.failed_rows.map((row, index) => ( + [Code={row.code ? row.code : 'Required'}] + ))} */} +  Download Data Filed + + + )} -
+
diff --git a/frontend/hospital-portal/public/lang/en-US.json b/frontend/hospital-portal/public/lang/en-US.json index 64b5efee..e11ef130 100644 --- a/frontend/hospital-portal/public/lang/en-US.json +++ b/frontend/hospital-portal/public/lang/en-US.json @@ -57,5 +57,7 @@ "txtCancel": "Cancel", "txtDecline": "Decline", "txtApprove": "Approve", - "txtDialogConfirmation": "Are you sure you want to proceed with this action?" + "txtDialogConfirmation": "Are you sure you want to proceed with this action?", + "txtStartDate": "Start Date", + "txtEndDate": "End Date" } diff --git a/frontend/hospital-portal/public/lang/id-ID.json b/frontend/hospital-portal/public/lang/id-ID.json index 96077fe4..d3f59306 100644 --- a/frontend/hospital-portal/public/lang/id-ID.json +++ b/frontend/hospital-portal/public/lang/id-ID.json @@ -57,5 +57,7 @@ "txtCancel": "Batal", "txtDecline": "Tolak", "txtApprove": "Terima", - "txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?" + "txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?", + "txtStartDate": "Tanggal Mulai", + "txtEndDate": "Tanggal Akhir" } diff --git a/frontend/hospital-portal/src/components/Table.tsx b/frontend/hospital-portal/src/components/Table.tsx index 6de87395..7334e85b 100644 --- a/frontend/hospital-portal/src/components/Table.tsx +++ b/frontend/hospital-portal/src/components/Table.tsx @@ -26,6 +26,10 @@ import { linearProgressClasses, } from '@mui/material'; import { visuallyHidden } from '@mui/utils'; + +import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers'; + +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; /* ---------------------------------- axios --------------------------------- */ import axios from '../utils/axios'; /* ---------------------------------- react --------------------------------- */ @@ -297,7 +301,7 @@ export default function Table({ {/* Start date */} {filterStartDate && filterStartDate.useFilter ? ( -
filterStartDate.handleStartDateChange(event)}> + {/* filterStartDate.handleStartDateChange(event)}> ({ shrink: true, }} /> - + */} + + { + filterStartDate.setStartDate( (newValue)); + }} + inputFormat="dd-MM-yyyy" + renderInput={(params) => } + /> +
) : null } @@ -317,7 +332,7 @@ export default function Table({ {filterEndDate && filterEndDate.useFilter ? ( -
filterEndDate.handleEndDateChange(event)}> + {/* filterEndDate.handleEndDateChange(event)}> ({ shrink: true, }} /> - + */} + + { + filterEndDate.setEndDate( (newValue)); + }} + inputFormat="dd-MM-yyyy" + renderInput={(params) => } + /> +
) : null } diff --git a/frontend/hospital-portal/src/sections/claim/TableList.tsx b/frontend/hospital-portal/src/sections/claim/TableList.tsx index dcd4994e..17dcf2c4 100644 --- a/frontend/hospital-portal/src/sections/claim/TableList.tsx +++ b/frontend/hospital-portal/src/sections/claim/TableList.tsx @@ -304,12 +304,12 @@ export default function TableList() { label: localeData.txtStatus, isSort: true, }, - { - id: 'action', - align: 'right', - label: '', - isSort: false, - }, + // { + // id: 'action', + // align: 'right', + // label: '', + // isSort: false, + // }, ]; @@ -363,15 +363,15 @@ export default function TableList() { {obj.submission_date ? fDateSuffix(obj.submission_date) : ''} , - action: - - navigate ('/claim/detail/'+obj.claim_request_id)}> - - View - - - } /> + // action: + // + // navigate ('/claim/detail/'+obj.claim_request_id)}> + // + // View + // + // + // } /> })) ); diff --git a/frontend/hospital-portal/src/sections/dashboard/TableListReqLog.tsx b/frontend/hospital-portal/src/sections/dashboard/TableListReqLog.tsx index 8cb70e4d..7fe20e82 100644 --- a/frontend/hospital-portal/src/sections/dashboard/TableListReqLog.tsx +++ b/frontend/hospital-portal/src/sections/dashboard/TableListReqLog.tsx @@ -239,7 +239,7 @@ export default function TableList() { }; // handle start date - const [startDateValue, setStartDateValue] = useState(''); + const [startDateValue, setStartDateValue] = useState(null); const handleStartDateChanges = async (event: React.FormEvent) => { event.preventDefault(); @@ -263,7 +263,7 @@ export default function TableList() { }; // handle end date - const [endDateValue, setEndDateValue] = useState(''); + const [endDateValue, setEndDateValue] = useState(null); const handleEndDateChanges = async (event: React.FormEvent) => { event.preventDefault(); @@ -494,8 +494,8 @@ export default function TableList() { searchs={searchs} filterStatus={filterStatus} selected={selected} - // filterStartDate={filterStartDate} - // filterEndDate={filterEndDate} + //filterStartDate={filterStartDate} + //filterEndDate={filterEndDate} />