From 1c71edd3ed726e7d60cde64181b3b61597146af9 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Thu, 26 Oct 2023 17:24:41 +0700 Subject: [PATCH] Detail Claim Management --- .../Http/Controllers/Api/ClaimController.php | 89 ++++ .../Api/ClaimRequestController.php | 3 +- Modules/Internal/Routes/api.php | 2 + .../src/pages/ClaimReport/Detail.tsx | 2 +- .../src/pages/ClaimRequests/Detail.tsx | 4 +- .../src/pages/ClaimRequests/List.tsx | 2 +- .../dashboard/src/pages/Claims/Detail.tsx | 424 ++++++++++++++++++ frontend/dashboard/src/pages/Claims/List.tsx | 2 +- frontend/dashboard/src/routes/index.tsx | 5 + 9 files changed, 527 insertions(+), 6 deletions(-) create mode 100644 frontend/dashboard/src/pages/Claims/Detail.tsx diff --git a/Modules/Internal/Http/Controllers/Api/ClaimController.php b/Modules/Internal/Http/Controllers/Api/ClaimController.php index 78c739ea..3e220e0b 100644 --- a/Modules/Internal/Http/Controllers/Api/ClaimController.php +++ b/Modules/Internal/Http/Controllers/Api/ClaimController.php @@ -18,6 +18,7 @@ use Modules\Internal\Transformers\ClaimShowResource; use Modules\Internal\Transformers\ClaimEditResource; use Box\Spout\Reader\Common\Creator\ReaderEntityFactory; use Box\Spout\Writer\Common\Creator\WriterEntityFactory; +use Illuminate\Support\Facades\DB; use PDF; @@ -526,4 +527,92 @@ class ClaimController extends Controller "file_url" => url('files/Benefit Usage Report.xlsx') ]); } + + public function getDetailClaims($claim_id) + { + $customer_data = DB::table('claim_requests') + ->leftJoin('claims', 'claim_requests.id', '=', 'claims.claim_request_id') + ->leftJoin('members', 'claim_requests.member_id', '=', 'members.id') + ->leftJoin('corporate_employees', 'members.id', '=', 'corporate_employees.member_id') + ->leftJoin('corporates', 'corporate_employees.corporate_id', '=', 'corporates.id') + ->where('claim_requests.id', '=', $claim_id) + ->select( + 'claim_requests.code', + 'claim_requests.submission_date', + 'claims.status', + 'members.name', + 'members.payor_id', + 'members.member_id', + 'claim_requests.payment_type', + 'corporates.name AS coporate_name', + ) + ->first(); + $results['customer_data'] = $customer_data; + + $documents = DB::table('files') + ->where('fileable_type', 'App\Models\ClaimRequest') + ->where('fileable_id', $claim_id) + ->select('original_name', \DB::raw("CONCAT('" . env('APP_URL') . "/storage/', path) as path"), 'type') + ->orderBy('id', 'desc') + ->get(); + $results['documents'] = $documents; + + $request_documents = DB::table('claim_request_files') + ->where('claim_request_id', $claim_id) + ->get(); + $results['request_documents'] = $request_documents; + + return Helper::responseJson($results); + } + + public function requestDocuments(Request $request) + { + $request->validate([ + 'claim_id' => 'required', + 'note' => 'required', + ]); + + $condition = $request->input('condition'); + $diagnosis = $request->input('diagnosis'); + $result = $request->input('result'); + $note = $request->input('note'); + + $dataToInsert = []; + if ($condition) { + $dataToInsert[] = [ + 'claim_request_id' => $request->claim_id, + 'date' => date('Y-m-d H:i:s'), + 'type' => 'claim-kondisi', + 'description' => $note, + 'created_by' =>auth()->user()->id, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]; + } + if ($diagnosis) { + $dataToInsert[] = [ + 'claim_request_id' => $request->claim_id, + 'date' => date('Y-m-d H:i:s'), + 'type' => 'claim-diagnosis', + 'description' => $note, + 'created_by' =>auth()->user()->id, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]; + } + if ($result) { + $dataToInsert[] = [ + 'claim_request_id' => $request->claim_id, + 'date' => date('Y-m-d H:i:s'), + 'type' => 'claim-result', + 'description' => $note, + 'created_by' =>auth()->user()->id, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]; + } + DB::table('claim_request_files')->insert($dataToInsert); + + return Helper::responseJson([]); + } } diff --git a/Modules/Internal/Http/Controllers/Api/ClaimRequestController.php b/Modules/Internal/Http/Controllers/Api/ClaimRequestController.php index 0b7a589a..1d70f809 100644 --- a/Modules/Internal/Http/Controllers/Api/ClaimRequestController.php +++ b/Modules/Internal/Http/Controllers/Api/ClaimRequestController.php @@ -379,7 +379,8 @@ class ClaimRequestController extends Controller ->leftJoin('corporate_divisions', 'corporate_employees.division_id', '=', 'corporate_divisions.id') ->where('claim_requests.id', '=', $claimRequestId) ->select( - 'claim_requests.submission_date', + 'claim_requests.submission_date', + 'claim_requests.code', DB::raw(' CASE WHEN claim_requests.status = "requested" THEN "requested" diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index a4a57e86..eea89110 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -197,6 +197,8 @@ Route::prefix('internal')->group(function () { Route::get('claims/{id}/edit', [ClaimController::class, 'edit']); Route::post('check-limit', [ClaimController::class, 'checkLimit']); Route::get('claims/1/data-claim', [ClaimController::class, 'dataClaimReport']); + Route::get('claims/detail/{id}', [ClaimController::class, 'getDetailClaims']); + Route::post('claims/request-documents', [ClaimController::class, 'requestDocuments']); Route::get('search-organizations', [OrganizationController::class, 'searchOrganization']); Route::get('search-specialities', [SpecialityController::class, 'searchSpeciality']); diff --git a/frontend/client-portal/src/pages/ClaimReport/Detail.tsx b/frontend/client-portal/src/pages/ClaimReport/Detail.tsx index 8fd44714..ec709358 100644 --- a/frontend/client-portal/src/pages/ClaimReport/Detail.tsx +++ b/frontend/client-portal/src/pages/ClaimReport/Detail.tsx @@ -20,7 +20,7 @@ import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; // ---------------------------------------------------------------------- -export default function UserProfile() { +export default function Detail() { const navigate = useNavigate(); const { themeStretch } = useSettings(); const [data, setData] = useState(); diff --git a/frontend/dashboard/src/pages/ClaimRequests/Detail.tsx b/frontend/dashboard/src/pages/ClaimRequests/Detail.tsx index cabc7119..3af03ad6 100644 --- a/frontend/dashboard/src/pages/ClaimRequests/Detail.tsx +++ b/frontend/dashboard/src/pages/ClaimRequests/Detail.tsx @@ -31,7 +31,7 @@ import { enqueueSnackbar } from 'notistack'; // ---------------------------------------------------------------------- -export default function UserProfile() { +export default function Detail() { const location = useLocation(); const queryParams = new URLSearchParams(location.search); const code = queryParams.get('code'); @@ -133,7 +133,7 @@ export default function UserProfile() { navigate(-1)} sx={{cursor:'pointer'}}/> - {code} + {(data && data.data) ? data.data.status.code : ''} {data ? ( Submission Date diff --git a/frontend/dashboard/src/pages/ClaimRequests/List.tsx b/frontend/dashboard/src/pages/ClaimRequests/List.tsx index 00d6057e..c04f8d04 100644 --- a/frontend/dashboard/src/pages/ClaimRequests/List.tsx +++ b/frontend/dashboard/src/pages/ClaimRequests/List.tsx @@ -368,7 +368,7 @@ export default function List() { Edit - navigate ('/claim-requests/detail/'+row.id+'/?code='+row.code)}> + navigate ('/claim-requests/detail/'+row.id+'')}> Detail diff --git a/frontend/dashboard/src/pages/Claims/Detail.tsx b/frontend/dashboard/src/pages/Claims/Detail.tsx new file mode 100644 index 00000000..22ff9ef1 --- /dev/null +++ b/frontend/dashboard/src/pages/Claims/Detail.tsx @@ -0,0 +1,424 @@ +// mui +import { Container, Grid, Stack, Typography, Card, TextField, Divider, ButtonBase, Box, IconButton } from '@mui/material'; +// components +import Page from '../../components/Page'; +// utils +import useSettings from '../../hooks/useSettings'; +// react +import { useNavigate, useParams, useLocation } from 'react-router-dom'; +import { useEffect, useState, useRef } from 'react'; +import axios from '../../utils/axios'; +// pages +import DetailTimeline from '../../pages/ClaimRequests/DetailTimeline'; +import DetailStepper from '../../pages/ClaimRequests/DetailStepper'; +import { format } from 'date-fns'; +import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; +import Button from '@mui/material/Button'; +import AddIcon from '@mui/icons-material/Add'; +import RemoveIcon from '@mui/icons-material/Remove'; +import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import Iconify from '@/components/Iconify'; +import { fPostFormat } from '@/utils/formatTime'; +import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile'; +import DownloadIcon from '@mui/icons-material/Download'; +import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import { fDateTimesecond } from '@/utils/formatTime'; +import { makeFormData } from '@/utils/jsonToFormData'; +import FormGroup from '@mui/material/FormGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; + +import { enqueueSnackbar } from 'notistack'; + +// ---------------------------------------------------------------------- + +export default function Detail() { + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); + const code = queryParams.get('code'); + + const navigate = useNavigate(); + const { themeStretch } = useSettings(); + const [data, setData] = useState(); + const [dataDialog, setDataDialog] = useState(); + const [customerData, setCustomerData] = useState(null); + const [documentData, setDocumentData] = useState(null); + const [requestDocumentData, setRequestDocumentData] = useState(null); + + const { id } = useParams(); + + useEffect(() => { + axios + .get('/claims/detail/'+id) + .then((response) => { + setCustomerData(response.data.data.customer_data); + setDocumentData(response.data.data.documents); + setRequestDocumentData(response.data.data.request_documents); + }) + .catch((error) => { + console.error(error); + }); + + }, []); + + const [isInvoiceVisible, setInvoiceVisibility] = useState(false); + + const handleInvoice = () => { + setInvoiceVisibility(!isInvoiceVisible); + } + const currentDate = new Date(); + const formattedCurrentDate = format(currentDate, 'dd MMM yyyy'); + const [dateInvoice, setDateInvoice] = useState(currentDate); + + const fileInvoiceInput = useRef(null); + const [fileInvoices, setFileInvoices] = useState([]); + + const handleInvoiceInputChange = (event) => { + if (event.target.files[0]) { + setFileInvoices([...fileInvoices, ...event.target.files]); + } else { + console.log('NO FILE'); + } + }; + const removeInvoiceFiles = (filesState, index) => { + setFileInvoices( + filesState.filter((file, fileIndex) => { + return fileIndex != index; + }) + ); + }; + const date = dateInvoice ? fPostFormat(dateInvoice, 'yyyy-MM-dd') : null; + + const [openDialogSubmit, setOpenDialogSubmit] = useState(false); + const handleCloseDialogSubmit = () => { + setOpenDialogSubmit(false); + } + const handleSubmitData = () => { + if(fileInvoices.length > 0) + { + //submit data + axios + .post('claim-requests/'+id+'/approve') + .then((response) => { + enqueueSnackbar('Success Submit Claim Request', { variant: 'success' }); + setOpenDialogSubmit(false); + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' }); + }); + //Upload file invoices + const formData = makeFormData({ + date:date, + invoice_files: fileInvoices, + }); + axios + .post('claim-requests/'+id+'/invoice-files', formData) + .then((response) => { + enqueueSnackbar(response.data.message ?? 'Success upload invoice', { variant: 'success' }); + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' }); + }); + } + else + { + enqueueSnackbar('Please upload file invoice, before submit', { variant: 'warning' }); + } + + setTimeout(() => + { + window.location.reload(); + }, 5000); + + }; + + function toTitleCase(str) { + return str.replace(/\w\S*/g, function(txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + } + + const style1 = { + color: '#919EAB', + width: '30%' + } + const style2 = { + width: '70%' + } + const marginBottom1 = { + marginBottom: 1, + } + + const [openDialogRequest, setOpenDialogRequest] = useState(false); + const handleCloseDialogUpdate = () => { + setOpenDialogRequest(false); + } + + const [conditionChecked, setConditionChecked] = useState(true); + const [diagnosisChecked, setDiagnosisChecked] = useState(false); + const [supportingResultChecked, setSupportingResultChecked] = useState(false); + + const handleConditionChange = (event) => { + setConditionChecked(event.target.checked); + }; + + const handleDiagnosisChange = (event) => { + setDiagnosisChecked(event.target.checked); + }; + + const handleSupportingResultChange = (event) => { + setSupportingResultChecked(event.target.checked); + }; + + const [noteField, setNoteField] = useState(''); + const [noteFieldError, setNoteFieldError] = useState(''); + const isRequiredFieldsFilled = () => { + return noteField.trim() !== ''; + }; + + const handelRequestDocument = () => { + const dataForm = { + claim_id: id, + condition: conditionChecked, + diagnosis: diagnosisChecked, + result: supportingResultChecked, + note: noteField, + } + axios + .post('/claims/request-documents', dataForm) + .then((response) => { + enqueueSnackbar('Success Request Document', { variant: 'success' }); + setOpenDialogRequest(false); + window.location.reload(); + }) + .catch((error) => { + enqueueSnackbar('Something Went Wrong', { variant: 'error' }); + }) + } + + return ( + + + + navigate(-1)} sx={{cursor:'pointer'}}/> + {(customerData && customerData.code ? customerData.code : '')} + {customerData ? ( + + + Status + {(customerData && customerData.status) ? toTitleCase(customerData.status) : ''} + + + Submission Date + {(customerData && customerData.submission_date) ? format(new Date(customerData.submission_date), "d MMM yyyy") : ''} + + + ) : ''} + + + {customerData ? ( + + + Summary of Customer Data + + Full Name + {customerData.name} + + + Policy Number + {customerData.payor_id} + + + Member ID + {customerData.member_id} + + + Claim Type + {toTitleCase(customerData.payment_type)} + + + Corporate Name + {toTitleCase(customerData.coporate_name)} + + + + ) : ''} + {documentData ? ( + + + + Additional Documents + + + + {documentData?.map((documentType, index) => ( + + + {documentType.type === 'claim-diagnosis' ? + 'Diagnosis' + : documentType.type === 'claim-kondisi' ? + 'Condition' + : documentType.type === 'claim-result' ? + 'Supporting Result' + : documentType.type === 'claim-invoice' ? + 'Invoice' + : ''} + + + + + {documentType.original_name ? documentType.original_name : '-'} + + + + ))} + + {requestDocumentData && requestDocumentData.length > 0 ? ( + Request Documents + ) : ''} + + {requestDocumentData?.map((documentType, index) => ( + + + + + + {documentType.type === 'claim-diagnosis' ? + 'Diagnosis' + : documentType.type === 'claim-kondisi' ? + 'Condition' + : documentType.type === 'claim-result' ? + 'Supporting Result' + : documentType.type === 'claim-invoice' ? + 'Invoice' + : ''} + + + + ))} + + + + + + Request Document + + + + + + + + + + } + label="Condition Document" + /> + } + label="Diagnosis Document" + /> + } + label="Supporting Result Document" + /> + + { + setNoteField(e.target.value); + setNoteFieldError(e.target.value.trim() === '' ? 'This field is required' : ''); + }} + fullWidth + inputProps={{ maxLength: 50 }} + error={!!noteFieldError} + helperText={noteFieldError} + /> + + + + + + + + + + ): ''} + + + + History of Hospital Care + + + + + + + + + + Diagnostic History + + + + + + + + + Diagnosis Summary + + + + + + + + + + Service + + + + + + + + + + Client Benefit Configuration + + + + + + + + <> + + + + + + + + + ); +} \ No newline at end of file diff --git a/frontend/dashboard/src/pages/Claims/List.tsx b/frontend/dashboard/src/pages/Claims/List.tsx index 91c0d5ae..5e1d01fd 100644 --- a/frontend/dashboard/src/pages/Claims/List.tsx +++ b/frontend/dashboard/src/pages/Claims/List.tsx @@ -208,7 +208,7 @@ export default function List() { Edit - setOpen(!open) }> + navigate('/claims/detail/'+row.id+'') }> Detail diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index 2b4a159b..ecb870c0 100644 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -393,6 +393,10 @@ export default function Router() { path: 'claims/edit/:id', element: , }, + { + path: 'claims/detail/:id', + element: , + }, { path: 'claims/:id', element: , @@ -559,6 +563,7 @@ const Profile = Loadable(lazy(() => import('../pages/Profile/Index'))); const Claims = Loadable(lazy(() => import('../pages/Claims/Index'))); const ClaimsCreate = Loadable(lazy(() => import('../pages/Claims/CreateUpdate'))); +const ClaimsDetail = Loadable(lazy(() => import('../pages/Claims/Detail'))); const ClaimShow = Loadable(lazy(() => import('../pages/Claims/Show'))); const ClaimRequests = Loadable(lazy(() => import('../pages/ClaimRequests/Index')));