(null);
+
+ return (
+
+
+
+ {/* }
+ sx={{ p: 1.8 }}
+ onClick={() => {
+ navigate('/claims/create');
+ }}
+ >
+ Create
+ */}
+
+
+ );
+ }
+
+ // Dummy Default Data
+ const [dataTableIsLoading, setDataTableLoading] = useState(true);
+ const [dataTableData, setDataTableData] = useState(
+ LaravelPaginatedDataDefault
+ );
+
+ const loadDataTableData = async (appliedFilter: any | null = null) => {
+ setDataTableLoading(true);
+ const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
+ const response = await axios.get('/claims', { params: filter });
+ // console.log(response.data);
+ setDataTableLoading(false);
+
+ setDataTableData(response.data);
+ };
+
+ const applyFilter = async (searchFilter: { search: string }) => {
+ await loadDataTableData(searchFilter);
+ setSearchParams(searchFilter);
+ };
+
+ const handlePageChange = (event: ChangeEvent, value: number): void => {
+ const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]);
+ loadDataTableData(filter);
+ setSearchParams(filter);
+ };
+
+ useEffect(() => {
+ loadDataTableData();
+ }, []);
+
+ const headStyle = {
+ fontWeight: 'bold',
+ };
+
+ // Called on every row to map the data to the columns
+ function createData(data: any): any {
+ return {
+ ...data,
+ };
+ }
+
+ {
+ /* ------------------ TABLE ROW ------------------ */
+ }
+ function Row(props: { row: ReturnType }) {
+ const { row } = props;
+ const [open, setOpen] = React.useState(false);
+
+ return (
+
+ *': { borderBottom: 'unset' } }}>
+
+ setOpen(!open)}>
+ {open ? : }
+
+
+ {row.code}
+ {row.member?.full_name}
+ {row.plan?.code}
+ {row.claim_request?.service?.name}
+
+ ({row.diagnoses[0]?.icd?.code}) {row.diagnoses[0]?.icd?.name}
+
+ {fCurrency(row.total_claim)}
+
+ {row.status == 'draft' && ()}
+ {row.status == 'requested' && ()}
+ {row.status == 'received' && ()}
+ {row.status == 'approved' && ()}
+ {row.status == 'postpone' && ()}
+ {row.status == 'paid' && ()}
+ {row.status == 'declined' && ()}
+
+
+
+ {['approved', 'paid'].includes(row.status) && (
+ {
+ navigate('/claims/' + row.id);
+ }}>
+ )}
+ {!['approved', 'paid'].includes(row.status) && (
+ {
+ navigate('/claims/' + row.id);
+ }}>
+ )}
+
+
+ {/* COLLAPSIBLE ROW */}
+
+
+
+ {/*
+
+ Description : {row.description}
+
+ */}
+
+
+
+
+ );
+ }
+ {
+ /* ------------------ END TABLE ROW ------------------ */
+ }
+
+ function TableContent() {
+ return (
+
+ {/* ------------------ TABLE HEADER ------------------ */}
+
+
+
+
+ Code
+
+
+ Member Name
+
+
+ Plan
+
+
+ Benefit
+
+
+ Diagnosis
+
+
+ Total Claim
+
+
+ Status
+
+
+ Action
+
+
+
+ {/* ------------------ END TABLE HEADER ------------------ */}
+
+ {/* ------------------ TABLE ROW ------------------ */}
+ {dataTableIsLoading ? (
+
+
+
+ Loading
+
+
+
+ ) : dataTableData.data.length === 0 ? (
+
+
+
+ No Data
+
+
+
+ ) : (
+
+ {dataTableData.data.map((row) => (
+
+ ))}
+
+ )}
+ {/* ------------------ END TABLE ROW ------------------ */}
+
+ );
+ }
+
+ return (
+
+
+
+ }
+ />
+
+ );
+}
diff --git a/frontend/client-portal/src/pages/Claims/Show.tsx b/frontend/client-portal/src/pages/Claims/Show.tsx
new file mode 100644
index 00000000..de19c504
--- /dev/null
+++ b/frontend/client-portal/src/pages/Claims/Show.tsx
@@ -0,0 +1,458 @@
+import * as Yup from 'yup';
+import { yupResolver } from '@hookform/resolvers/yup';
+import {
+ Autocomplete,
+ Box,
+ Button,
+ Card,
+ Collapse,
+ Container,
+ Divider,
+ Grid,
+ InputAdornment,
+ Paper,
+ Stack,
+ Table,
+ TableBody,
+ TableCell,
+ TableRow,
+ TextField,
+ Typography,
+} from '@mui/material';
+import { Controller, useForm } from 'react-hook-form';
+import { useParams, useNavigate } from 'react-router-dom';
+import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
+import { FormProvider, RHFCheckbox, RHFSelect, RHFTextField } from '../../components/hook-form';
+import Page from '../../components/Page';
+import useSettings from '../../hooks/useSettings';
+import { useEffect, useMemo, useRef, useState } from 'react';
+import MemberSelectDialog from '../../components/dialogs/MemberSelectDialog';
+import { styled } from '@mui/system';
+import axios from '../../utils/axios';
+import { enqueueSnackbar } from 'notistack';
+import { LoadingButton } from '@mui/lab';
+import { fCurrency } from '../../utils/formatNumber';
+import Iconify from '../../components/Iconify';
+import Form from './Form';
+import Documents from './components/Documents';
+import DiagnosisHistory from './components/DiagnosisHistory';
+import ClaimItems from './components/ClaimItems';
+import DialogMemberBenefit from './components/DialogMemberBenefit';
+import AutocompleteDiagnosis from '@/components/autocomplete/AutocompleteDiagnosis';
+
+export default function ClaimsCreateUpdate() {
+ const { themeStretch } = useSettings();
+ const { id } = useParams();
+
+ const isEdit = id ? true : false;
+
+ const [currentClaim, setCurrentClaim] = useState();
+ const [documents, setDocuments] = useState([]);
+
+ const Item = styled(Paper)(({ theme }) => ({
+ backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
+ ...theme.typography.body2,
+ padding: theme.spacing(1),
+ textAlign: 'center',
+ color: theme.palette.text.secondary,
+ }));
+
+ // --------------------------------------------------------------
+ // Claim Item
+ const [claimItems, setClaimItems] = useState([]);
+ const [dialogAddClaimItemOpen, setDialogAddClaimItemOpen] = useState(false);
+ const [loadingClaimItems, setLoadingClaimItems] = useState(false);
+
+ const handleAddClaimItems = (items) => {
+ setClaimItems([...claimItems, ...items]);
+ };
+
+ const handleSaveClaimItems = () => {
+ console.log('Storing ', claimItems);
+ setLoadingClaimItems(true);
+ axios
+ .post(`claims/${id}/update-items`, {
+ benefit_items: claimItems.map((benefit) => {
+ return {
+ id: benefit.id,
+ biaya_diajukan: benefit.biaya_diajukan,
+ biaya_disetujui: benefit.biaya_disetujui,
+ };
+ }),
+ })
+ .then((res) => {
+ enqueueSnackbar(res.data.message, { variant: 'success' });
+ })
+ .catch((err) => {
+ setLoadingClaimItems(false);
+ enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
+ })
+ .then(() => {
+ setLoadingClaimItems(false);
+ });
+ };
+
+ // --------------------------------------------------------------
+ // Diagnosis
+ const [primaryDiagnosis, setPrimaryDiagnosis] = useState(null);
+ const [secondaryDiagnosis, setSecondaryDiagnosis] = useState(null);
+ const [loadingDiagnosis, setLoadingDiagnosis] = useState(false);
+
+ const handlePrimaryDiagnosisChange = ({ title, value }) => {
+ setPrimaryDiagnosis(value);
+ };
+
+ const handleSecondaryDiagnosisChange = ({ title, value }) => {
+ setSecondaryDiagnosis(value);
+ };
+
+ const handleSaveDiagnosis = () => {
+ setLoadingDiagnosis(true);
+
+ axios
+ .post(`claims/${id}/update-diagnosis`, {
+ primary: [primaryDiagnosis],
+ secondary: [secondaryDiagnosis],
+ })
+ .then((res) => {
+ enqueueSnackbar(res.data.message, { variant: 'success' });
+ })
+ .catch((err) => {
+ setLoadingDiagnosis(false);
+ enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
+ })
+ .then(() => {
+ setLoadingDiagnosis(false);
+ });
+ };
+
+ const handleDecline = () => {
+ axios
+ .post(`claims/${id}/decline`)
+ .then((res) => {
+ enqueueSnackbar(res.data.message, { variant: 'success' });
+ setCurrentClaim({ ...currentClaim, status: 'declined' });
+ })
+ .catch((err) => {
+ // setLoadingDiagnosis(false)
+ enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
+ })
+ .then(() => {
+ // setLoadingDiagnosis(false)
+ });
+ };
+
+ const handleApprove = () => {
+ axios
+ .post(`claims/${id}/approve`)
+ .then((res) => {
+ enqueueSnackbar(res.data.message, { variant: 'success' });
+ setCurrentClaim({ ...currentClaim, status: 'approved' });
+ })
+ .catch((err) => {
+ // setLoadingDiagnosis(false)
+ enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
+ })
+ .then(() => {
+ // setLoadingDiagnosis(false)
+ });
+ };
+
+ const handleReOpen = () => {
+ axios
+ .post(`claims/${id}/re-open`)
+ .then((res) => {
+ enqueueSnackbar(res.data.message, { variant: 'success' });
+ setCurrentClaim({ ...currentClaim, status: 'received' });
+ })
+ .catch((err) => {
+ // setLoadingDiagnosis(false)
+ enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
+ })
+ .then(() => {
+ // setLoadingDiagnosis(false)
+ });
+ };
+
+
+ // ---------------------------------------------------------------
+ // Initial LOG
+ const [loadingLog, setLoadingLog] = useState(false)
+
+ const handleDownloadLog = (claim_id) => {
+ setLoadingLog(true);
+ axios
+ .post(`generate-log/${claim_id}`, {
+ responseType: 'blob',
+ })
+ .then((response) => {
+ window.open(URL.createObjectURL(response.data));
+ setLoadingLog(false);
+ setOpenDialog(false);
+ })
+ .catch((response) => {
+ enqueueSnackbar(response.message, { variant: 'error' });
+ setLoadingLog(false);
+ });
+ }
+
+
+ // -------------------------------------------------
+ // Final LOG
+ const [loadingFinalLog, setLoadingFinalLog] = useState(false)
+ const handleDownloadFinalLog = (claim_id) => {
+ setLoadingFinalLog(true);
+ axios
+ .get(`final-log/${claim_id}`, {
+ responseType: 'blob',
+ })
+ .then((response) => {
+ window.open(URL.createObjectURL(response.data));
+ setLoadingFinalLog(false);
+ })
+ .catch((response) => {
+ enqueueSnackbar(response.message, { variant: 'error' });
+ setLoadingFinalLog(false);
+ });
+ }
+
+
+ useEffect(() => {
+ axios.get('/claims/' + id).then(({ data }) => {
+ const claim = data.data;
+ const allFiles = [...(claim.claim_request ? claim.claim_request.files : []), ...claim.files];
+
+ setCurrentClaim(claim);
+ setDocuments(allFiles);
+ setClaimItems(claim.benefit_items);
+ });
+ }, [id]);
+
+ return (
+
+
+
+
+
+ {/* Action Button */}
+
+ {(currentClaim?.status == 'requested' || currentClaim?.status == 'received') && (
+ <>
+ {
+ handleDecline();
+ }}
+ >
+ Decline
+
+ {
+ handleApprove();
+ }}
+ >
+ Approve
+
+ >
+ )}
+ {(currentClaim?.status == 'declined' || currentClaim?.status == 'approved') && (
+ {
+ handleReOpen();
+ }}
+ >
+ Re-Open
+
+ )}
+
+
+
+
+
+
+
+ Status : {currentClaim?.status}
+ { currentClaim?.status == 'approved' && (
+ {
+ handleDownloadFinalLog(currentClaim.id);
+ }}
+ >
+ Download Final LOG
+
+ )}
+
+
+
+
+
+
+ {/* Dokumen Tambahan */}
+
+
+ {/* Riwayat Diagnosa */}
+
+
+ {/* Ringkasan Data Member */}
+
+
+
+
+
+ Ringkasan Data Nasabah
+
+
+
+
+
+
+
+
+
+
+
+
+ Nama Lengkap
+
+
+ {currentClaim?.member?.full_name}
+
+
+
+
+
+
+ Nomor Polis
+
+ {currentClaim?.member?.full_name}
+
+
+
+
+ Member ID
+
+ {currentClaim?.member?.member_id}
+
+
+
+
+ Tipe Claim
+
+
+ {currentClaim?.claim_request?.payment_type_name}
+
+
+
+
+
+ Tipe Nasabah
+
+
+ {currentClaim?.member?.current_corporate?.name}
+
+
+
+
+
+
+
+
+
+
+ {/* Diagnosis */}
+
+
+
+
+
+
+
+
+ {(currentClaim?.status == 'requested' || currentClaim?.status == 'received') && (
+
+ {
+ handleSaveDiagnosis();
+}}
+>
+Simpan Claim Item
+
+ )}
+
+
+
+
+ Client Benefit Configuration
+ {
+ setDialogAddClaimItemOpen(true);
+ }}
+ >
+ + Add Benefit
+
+
+
+
+
+ {(currentClaim?.status == 'requested' || currentClaim?.status == 'received') && (
+
+ {
+ handleSaveClaimItems();
+ }}
+ >
+ Simpan Claim Item
+
+ )}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/client-portal/src/pages/Claims/components/ClaimItems.tsx b/frontend/client-portal/src/pages/Claims/components/ClaimItems.tsx
new file mode 100644
index 00000000..9a803a9c
--- /dev/null
+++ b/frontend/client-portal/src/pages/Claims/components/ClaimItems.tsx
@@ -0,0 +1,69 @@
+import Iconify from '@/components/Iconify';
+import { Divider, InputAdornment, Paper, Stack, TextField, Typography } from '@mui/material';
+import { useEffect, useState } from 'react';
+
+export default function ClaimItems({ items, setItems }) {
+
+ const handleChangeBiayaDiajukan = (event, itemIndex: Number) => {
+ setItems(items.map((item, index) => {
+ if (index == itemIndex) {
+ return {...item, biaya_diajukan : event.target.value}
+ } else {
+ return item;
+ }
+ }))
+ }
+
+ const handleChangeBiayaDisetujui = (event, itemIndex: Number) => {
+ setItems(items.map((item, index) => {
+ if (index == itemIndex) {
+ return {...item, biaya_disetujui : event.target.value}
+ } else {
+ return item;
+ }
+ }))
+ }
+
+ const calculateBiayaDitolak = (biayaDiajukan: Number | null, biayaDisetujui: Number | null) => {
+ return (biayaDiajukan ? biayaDiajukan : 0) - (biayaDisetujui ? biayaDisetujui : 0)
+ }
+
+ const handleDeleteItem = (itemIndex: Number) => {
+ setItems(items.filter((item, index) => index != itemIndex))
+ }
+
+ return (
+
+ {items.length > 0 ? (
+ items.map((item, index) => (
+
+
+ #{index+1} ({item.code}) {item.description}
+ {handleDeleteItem(index)}}>
+
+ }
+ >
+ {handleChangeBiayaDiajukan(event, index)}}>
+ IDR
+ {/* */}
+
+ {handleChangeBiayaDisetujui(event, index)}}>
+ IDR
+ {/* */}
+
+
+ IDR
+ {/* */}
+
+
+
+ ))
+ ) : (
+ No Benefit Item
+ )}
+
+ );
+}
diff --git a/frontend/client-portal/src/pages/Claims/components/DiagnosisHistory.tsx b/frontend/client-portal/src/pages/Claims/components/DiagnosisHistory.tsx
new file mode 100644
index 00000000..db44687f
--- /dev/null
+++ b/frontend/client-portal/src/pages/Claims/components/DiagnosisHistory.tsx
@@ -0,0 +1,54 @@
+import Iconify from '@/components/Iconify';
+import { Paper, Stack, Typography } from '@mui/material';
+import { useState } from 'react';
+
+export default function DiagnosisHistory({ diagnosis }) {
+ function DiagnosaItem({ item }) {
+ return (
+
+
+
+ Nama Penyakit
+
+ Claim Terakhir : 23 Januari 2023 08:00
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ Riwayat Diagnosa
+
+
+ {
+ setOpenDialogRequestDocument(true);
+ }}
+ >
+ View All
+
+
+
+
+ { diagnosis.length > 0 ? (
+
+ { diagnosis.map((diagnosa, index) => (
+
+ )) }
+
+ ) : (
+
+ Belum ada History Perawatan
+
+ ) }
+
+
+ );
+}
diff --git a/frontend/client-portal/src/pages/Claims/components/DialogMemberBenefit.tsx b/frontend/client-portal/src/pages/Claims/components/DialogMemberBenefit.tsx
new file mode 100644
index 00000000..757b3448
--- /dev/null
+++ b/frontend/client-portal/src/pages/Claims/components/DialogMemberBenefit.tsx
@@ -0,0 +1,59 @@
+import MuiDialog from "@/components/MuiDialog";
+import { Button, Checkbox, Typography } from "@mui/material";
+import { Paper } from "@mui/material";
+import { Stack } from '@mui/material';
+import { useState } from "react";
+
+
+export default function DialogMemberBenefit({member, setOpenDialog, openDialog, onSubmit}) {
+
+ const benefits = member?.current_plan?.benefits ?? [];
+ const [selectedBenefits, setSelectedBenefits] = useState([]);
+
+ const toggleBenefit = (benefit) => {
+ if (selectedBenefits.includes(benefit)) {
+ console.log('removing', benefit)
+ setSelectedBenefits(selectedBenefits.filter((throughBenefit) => benefit.id != throughBenefit.id))
+ } else {
+ console.log('adding', benefit)
+ setSelectedBenefits([...selectedBenefits, benefit])
+ }
+ }
+
+ const handleSubmit = () => {
+ onSubmit(selectedBenefits);
+ console.log ('submitting')
+ setOpenDialog(false);
+ setSelectedBenefits([]);
+ }
+
+ const getContent = () => (
+
+ { benefits.map((benefit, index) => (
+
+
+
+
+ {benefit.description}
+ {benefit.code}
+
+ { toggleBenefit(benefit) }}>
+
+
+ ))}
+
+
+
+ );
+
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/client-portal/src/pages/Claims/components/Documents.tsx b/frontend/client-portal/src/pages/Claims/components/Documents.tsx
new file mode 100644
index 00000000..fefe2a94
--- /dev/null
+++ b/frontend/client-portal/src/pages/Claims/components/Documents.tsx
@@ -0,0 +1,68 @@
+import Iconify from '@/components/Iconify';
+import { Paper, Stack, Typography } from '@mui/material';
+import { useState } from 'react';
+
+export default function Documents({ files }) {
+ // --------------------------------------------------------------
+ // Dialog Request Document
+ const [openDialogRequestDocument, setOpenDialogRequestDocument] = useState(false);
+
+ function FileItem({item}) {
+ function fileCategory(type: string) {
+ switch(type) {
+ case 'claim-result':
+ return 'Claim Result';
+ case 'claim-diagnosis':
+ return 'Claim Diagnosis';
+ case 'claim-condition':
+ return 'Claim Condition';
+ default:
+ return 'Other File';
+ }
+ }
+
+ return (
+
+
+
+ { fileCategory(item.type) }
+
+ { item.name }
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ Dokumen Tambahan
+
+ {
+ setOpenDialogRequestDocument(true);
+ }}
+ >
+ + Request Document
+
+
+
+
+ { files.length > 0 ? (
+
+ { files.map((file, index) => (
+
+ )) }
+
+ ) : (
+
+ Belum ada History Perawatan
+
+ )}
+
+
+ );
+}
diff --git a/frontend/client-portal/src/routes/index.tsx b/frontend/client-portal/src/routes/index.tsx
index 089595a9..704cdf95 100755
--- a/frontend/client-portal/src/routes/index.tsx
+++ b/frontend/client-portal/src/routes/index.tsx
@@ -108,6 +108,22 @@ export default function Router() {
},
],
},
+ {
+ path: '/claims',
+ element: (
+
+
+
+
+
+ ),
+ children: [
+ {
+ element: ,
+ index: true,
+ },
+ ],
+ },
{
path: '*',
element: ,
@@ -136,3 +152,4 @@ const AlarmCenterUserProfile = Loadable(lazy(() => import('../pages/AlarmCenter/
// Claim Report
const ClaimReport = Loadable(lazy(() => import('../pages/ClaimReport/Index')));
+const Claims = Loadable(lazy(() => import('../pages/Claims/Index')));
diff --git a/frontend/client-portal/vite.config.ts b/frontend/client-portal/vite.config.ts
index a56ef19d..557fde33 100755
--- a/frontend/client-portal/vite.config.ts
+++ b/frontend/client-portal/vite.config.ts
@@ -2,6 +2,7 @@ import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import svgrPlugin from 'vite-plugin-svgr'
import { VitePWA } from 'vite-plugin-pwa'
+import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
@@ -20,4 +21,7 @@ export default defineConfig({
},
}),
],
+ resolve: {
+ alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }],
+ }
})