From 39ccf2a0b576e2a77f1909cf256a8df96a7f2cd9 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Mon, 4 Mar 2024 09:54:07 +0700 Subject: [PATCH 1/2] Update Hospital portal --- .../hospital-portal/public/lang/en-US.json | 4 ++- .../hospital-portal/public/lang/id-ID.json | 4 ++- .../hospital-portal/src/components/Table.tsx | 34 ++++++++++++++++--- .../src/sections/claim/TableList.tsx | 30 ++++++++-------- .../sections/dashboard/TableListReqLog.tsx | 8 ++--- 5 files changed, 55 insertions(+), 25 deletions(-) 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} /> Date: Tue, 5 Mar 2024 10:36:01 +0700 Subject: [PATCH 2/2] Update import claim management --- .../Http/Controllers/Api/ClaimController.php | 157 +++++++++++++++ Modules/Internal/Routes/api.php | 3 + frontend/dashboard/src/pages/Claims/List.tsx | 188 +++++++++++++++++- .../files/Template - Claim - Management.xlsx | Bin 0 -> 8414 bytes 4 files changed, 344 insertions(+), 4 deletions(-) create mode 100644 public/files/Template - Claim - Management.xlsx 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/public/files/Template - Claim - Management.xlsx b/public/files/Template - Claim - Management.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..658920b24976d590b665d55667f29807fb80fb33 GIT binary patch literal 8414 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 N8nJSRjQ;1-{{XSeOfvug literal 0 HcmV?d00001