Merge remote-tracking branch 'origin/staging' into origin/production
This commit is contained in:
@@ -1,35 +1,30 @@
|
||||
import MuiDialog from "@/components/MuiDialog";
|
||||
import { Autocomplete, Button, Card, Checkbox, DialogActions, Grid, TextField, Typography } from "@mui/material";
|
||||
import { Button, Card, Checkbox, DialogActions, Grid, TextField, TextareaAutosize, Typography } from "@mui/material";
|
||||
import { Paper } from "@mui/material";
|
||||
import { Stack } from '@mui/material';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ClaimRequest, Files } from '@/@types/claims';
|
||||
import { fDateOnly, fDateTimesecond, toTitleCase } from "@/utils/formatTime";
|
||||
import { DetailClaimRequest } from "../Model/Types";
|
||||
import { fDateTimesecond, toTitleCase } from "@/utils/formatTime";
|
||||
import axios from "@/utils/axios";
|
||||
import { enqueueSnackbar, useSnackbar } from "notistack";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useNavigate } from "react-router";
|
||||
import * as Yup from 'yup';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { RHFTextField } from "@/components/hook-form";
|
||||
|
||||
|
||||
type DialogConfirmationType = {
|
||||
openDialog: boolean;
|
||||
setOpenDialog: any;
|
||||
onSubmit?: void;
|
||||
approve: string;
|
||||
claimRequest: ClaimRequest|undefined;
|
||||
requestLog: DetailClaimRequest|undefined;
|
||||
}
|
||||
|
||||
export default function DialogConfirmation({claimRequest, setOpenDialog, openDialog, approve, onSubmit} : DialogConfirmationType ) {
|
||||
|
||||
export default function DialogConfirmation({requestLog, setOpenDialog, openDialog, approve, onSubmit} : DialogConfirmationType ) {
|
||||
const navigate = useNavigate();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
date: claimRequest?.date,
|
||||
id: claimRequest?.id,
|
||||
reason: claimRequest?.reason
|
||||
|
||||
});
|
||||
|
||||
const handleChange = (field, value) => {
|
||||
setFormData((prevData) => ({
|
||||
...prevData,
|
||||
@@ -38,24 +33,28 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
|
||||
|
||||
};
|
||||
|
||||
const handleApprove = () => {
|
||||
setFormData((prevData) => ({
|
||||
...prevData,
|
||||
status: approve,
|
||||
}));
|
||||
handleSubmit();
|
||||
};
|
||||
|
||||
|
||||
const handleSubmit = () => {
|
||||
axios
|
||||
.post(`customer-service/request/final-log`, formData)
|
||||
.post(`claim-requests/${requestLog?.id}/submition`, formData)
|
||||
.then((response) => {
|
||||
enqueueSnackbar('Verification Request LOG Success', { variant: 'success' });
|
||||
enqueueSnackbar('Submition Claim Request Success', { variant: 'success' });
|
||||
setOpenDialog(false);
|
||||
if (requestLog?.service_type == 'Inpatient'){
|
||||
navigate('/case_management/inpatient_monitoring');
|
||||
} else {
|
||||
navigate('/custormer-service/final-log');
|
||||
}
|
||||
navigate('/claim-requests')
|
||||
})
|
||||
.catch(({ response }) => {
|
||||
enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const style1 = {
|
||||
color: '#919EAB',
|
||||
width: '30%'
|
||||
@@ -66,34 +65,57 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
|
||||
const marginBottom1 = {
|
||||
marginBottom: 1,
|
||||
}
|
||||
|
||||
const marginBottom2 = {
|
||||
marginBottom: 2,
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
status: approve,
|
||||
no_identitas: requestLog?.no_identitas ?? '',
|
||||
keterangan: '',
|
||||
hak_kamar_pasien: '',
|
||||
penempatan_kamar: '',
|
||||
});
|
||||
};
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
setOpenDialog(false);
|
||||
resetForm();
|
||||
}
|
||||
|
||||
|
||||
const handleNumericInput = (input: any) => {
|
||||
const numericInput = input.replace(/\D/g, '');
|
||||
return numericInput;
|
||||
};
|
||||
|
||||
const handleKeyPress = (e:any) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
// Menghentikan default "Enter" (tidak membuat baris baru)
|
||||
e.preventDefault();
|
||||
|
||||
// Menambahkan karakter baris baru
|
||||
handleChange('keterangan', `${formData.keterangan}\n`);
|
||||
}
|
||||
};
|
||||
|
||||
console.log(approve, 'test')
|
||||
const getContent = () => (
|
||||
<Stack spacing={1} marginTop={2}>
|
||||
<Typography variant="subtitle2">Are you sure to {approve == 'approved' ? 'approve' : 'deciline'} this final log ?</Typography>
|
||||
<Typography variant="subtitle1">Are you sure to submit this claim ?</Typography>
|
||||
<Grid item xs={12} md={12} marginTop={4}>
|
||||
<Card sx={{padding:2, marginTop:2}} >
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Member ID</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.member_id}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Policy Number</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.policy_number}</Typography>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Code</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.code}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Name</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.name}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Submission Date</Typography>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Date Submission</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
@@ -105,58 +127,10 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.service_type}</Typography>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
<Card sx={{padding:2, marginTop:2}} >
|
||||
<Stack direction='row' spacing={2} sx={marginBottom2}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Discharge Date</Typography>
|
||||
<TextField
|
||||
label="Discharge Date"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
type="date"
|
||||
value={formData.discharge_date ? fDateOnly(formData.discharge_date) : ''}
|
||||
onChange={(e) => handleChange('discharge_date', e.target.value)}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom2}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Catatan</Typography>
|
||||
<TextField
|
||||
label="Catatan"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={formData.catatan}
|
||||
onChange={(e) => handleChange('catatan', e.target.value)}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom2}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Diagnosis ICD - X</Typography>
|
||||
<Autocomplete
|
||||
multiple
|
||||
options={icdOptions}
|
||||
getOptionLabel={(option) => option.label}
|
||||
fullWidth
|
||||
value={icdOptions.filter((icd) => formData.icdCodes.includes(icd.value))}
|
||||
onChange={(e, newValues) => handleChange('icdCodes', newValues.map((value) => value.value))}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="Diagnosis ICD - X"
|
||||
variant="outlined"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Grid>
|
||||
<DialogActions>
|
||||
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialog}>Cancel</Button>
|
||||
|
||||
{approve == 'approved' ? (
|
||||
<Button color="primary" variant="contained" onClick={() => handleApprove()}>Approve</Button>
|
||||
) : (
|
||||
<Button color="error" variant="contained" onClick={() => handleApprove()}>Decline</Button>
|
||||
) }
|
||||
|
||||
<Button color="primary" variant="contained" onClick={() => handleApprove()}>Submit</Button>
|
||||
</DialogActions>
|
||||
</Stack>
|
||||
);
|
||||
@@ -168,7 +142,7 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
content={getContent()}
|
||||
maxWidth="xl"
|
||||
maxWidth="md"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -78,15 +78,13 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
|
||||
);
|
||||
|
||||
|
||||
const [date, setDate] = useState(currentClaim?.submission_date)
|
||||
const [date, setDate] = useState(currentClaim?.submission_date ? fDateTimesecond(currentClaim?.submission_date) : null)
|
||||
const id = currentClaim?.id
|
||||
|
||||
useEffect(() => {
|
||||
setDate(currentClaim?.submission_date)
|
||||
}, [currentClaim]);
|
||||
|
||||
console.log(date);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit && currentClaim) {
|
||||
reset(defaultValues);
|
||||
@@ -254,7 +252,6 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
|
||||
</Stack>
|
||||
)
|
||||
|
||||
|
||||
return (
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
|
||||
@@ -310,10 +307,14 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
|
||||
<RHFDateTimePicker
|
||||
{...field}
|
||||
label="Date of Submission"
|
||||
value={field.value || null}
|
||||
onChange={() => setDate(field.value)}
|
||||
value={field.value}
|
||||
onChange={() =>
|
||||
setDate(field.value)
|
||||
}
|
||||
dateFormat='dd MMM yyyy HH:mm:ss'
|
||||
/>
|
||||
)}
|
||||
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function ClaimsCreateUpdate() {
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit) {
|
||||
axios.get('/claim-requests/' + id).then((res) => {
|
||||
axios.get('/claim-requests/' + id + '/show').then((res) => {
|
||||
setCurrentClaim(res.data.data);
|
||||
});
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ import DialogDeleteFileLog from './Components/DialogDeleteFileLog';
|
||||
import DialogBenefit from '../CustomerService/FinalLog/Components/DialogBenefit';
|
||||
import DialogDeleteBenefit from '../CustomerService/FinalLog/Components/DialogDeleteBenefit';
|
||||
import DialogEditBenefit from '../CustomerService/FinalLog/Components/DialogEditBenefit';
|
||||
import DialogConfirmation from './Components/DialogConfirmation';
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -49,6 +51,8 @@ export default function Detail() {
|
||||
const navigate = useNavigate();
|
||||
const { themeStretch } = useSettings();
|
||||
const [claimRequests, setClaimRequest] = useState<DetailClaimRequest>();
|
||||
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
|
||||
const [isReversal, setIsReversal] = useState(false);
|
||||
|
||||
const { id } = useParams();
|
||||
|
||||
@@ -57,6 +61,7 @@ export default function Detail() {
|
||||
.get('claim-requests/detail/'+id)
|
||||
.then((response) => {
|
||||
setClaimRequest(response.data.data)
|
||||
setIsReversal(response.data.data.is_reversal)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
@@ -223,13 +228,18 @@ export default function Detail() {
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Document </Typography>
|
||||
</Stack>
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogUploadFileLog(true)
|
||||
}} >
|
||||
<Typography variant="button" display="block">File</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogUploadFileLog(true)
|
||||
}} >
|
||||
<Typography variant="button" display="block">File</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
) : null
|
||||
}
|
||||
|
||||
</Stack>
|
||||
{claimRequests?.files?.map((documentType, index) => (
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{marginBottom: 2}} key={index}>
|
||||
@@ -250,14 +260,19 @@ export default function Detail() {
|
||||
<Typography variant="body1" gutterBottom>{documentType.original_name ? documentType.original_name : '-'}</Typography>
|
||||
</a>
|
||||
</Stack>
|
||||
<Stack direction="column" spacing={2}>
|
||||
<IconButton onClick={() => {
|
||||
setDialogDeleteFileLog(true)
|
||||
setPathFile(documentType.path)
|
||||
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
|
||||
<Delete color='error' fontSize="small" />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Stack direction="column" spacing={2}>
|
||||
<IconButton onClick={() => {
|
||||
setDialogDeleteFileLog(true)
|
||||
setPathFile(documentType.path)
|
||||
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
|
||||
<Delete color='error' fontSize="small" />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
) : null
|
||||
}
|
||||
|
||||
</Stack>
|
||||
))}
|
||||
|
||||
@@ -281,11 +296,15 @@ export default function Detail() {
|
||||
<Card sx={{padding:2}} >
|
||||
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Benefit</Typography>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogBenefit(true);
|
||||
}} >
|
||||
<Typography variant="button" display="block">Benefit</Typography>
|
||||
</Button>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogBenefit(true);
|
||||
}} >
|
||||
<Typography variant="button" display="block">Benefit</Typography>
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
</Stack>
|
||||
|
||||
<Box sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '24px', py: '20px', marginBottom: '24px', borderRadius: '12px'}}>
|
||||
@@ -298,29 +317,34 @@ export default function Detail() {
|
||||
{item.benefit?.description}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
|
||||
<MoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => {
|
||||
setDialogEditBenefit(true)
|
||||
setIdBenefitData(item.id)
|
||||
setBenefitConfigurationData(item)
|
||||
}}
|
||||
>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
setIdBenefitData(item.id)
|
||||
setDialogDeleteBenefit(true)
|
||||
}}
|
||||
>
|
||||
<Delete color='error'/>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Grid>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
|
||||
<MoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => {
|
||||
setDialogEditBenefit(true)
|
||||
setIdBenefitData(item.id)
|
||||
setBenefitConfigurationData(item)
|
||||
}}
|
||||
>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
setIdBenefitData(item.id)
|
||||
setDialogDeleteBenefit(true)
|
||||
}}
|
||||
>
|
||||
<Delete color='error'/>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Grid>
|
||||
) : null
|
||||
}
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} py={2}>
|
||||
@@ -421,7 +445,7 @@ export default function Detail() {
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" sx={{ fontWeight: 'bold'}}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 'bold'}}>
|
||||
Total Benefit
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -436,12 +460,12 @@ export default function Detail() {
|
||||
<Grid item xs={2}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="subtitle1">
|
||||
Amount Incurred
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
|
||||
{totalAmountIncurred ? fNumber(totalAmountIncurred) : '0'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -452,12 +476,12 @@ export default function Detail() {
|
||||
<Grid item xs={2}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="subtitle1">
|
||||
Amount Approved
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
|
||||
{totalAmountApproved ? fNumber(totalAmountApproved) : '0'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -468,12 +492,12 @@ export default function Detail() {
|
||||
<Grid item xs={3}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="subtitle1">
|
||||
Amount Not Approved
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
|
||||
{totalAmountNotApproved ? fNumber(totalAmountNotApproved) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -484,12 +508,12 @@ export default function Detail() {
|
||||
<Grid item xs={2}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="subtitle1">
|
||||
Excess Paid
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
|
||||
{totalExcessPaid ? fNumber(totalExcessPaid) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -497,6 +521,23 @@ export default function Detail() {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Grid container spacing={1} marginY={2}>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="subtitle1">
|
||||
Biaya disetujui
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6} textAlign="right">
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>
|
||||
{totalAmountApproved ? fNumber(totalAmountApproved) : '0'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle2">
|
||||
Nominal diatas 1 juta akan menunggu persetujuan senior analyst
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
@@ -504,9 +545,25 @@ export default function Detail() {
|
||||
: (
|
||||
null
|
||||
)}
|
||||
|
||||
|
||||
</Box>
|
||||
</Card>
|
||||
|
||||
{ claimRequests?.status === 'declined' ? (
|
||||
<Card sx={{padding:2, marginTop:2}} >
|
||||
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Reason</Typography>
|
||||
</Stack>
|
||||
<Box sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '24px', py: '20px', marginBottom: '24px', borderRadius: '12px'}}>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Reason Decline</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{claimRequests?.reason_decline}</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Card>
|
||||
) : null }
|
||||
|
||||
{/* PR Buat pindahin ke componen */}
|
||||
{/* <CardBenefit
|
||||
requestLog={requestLog}
|
||||
@@ -542,6 +599,42 @@ export default function Detail() {
|
||||
requestLog={requestLog}
|
||||
openDialog={openDialogEditDetail}
|
||||
/> */}
|
||||
|
||||
{(claimRequests?.status === 'requested') || (claimRequests?.status === 'declined') ? (
|
||||
<Grid item xs={12} md={12}>
|
||||
<Stack direction="row" padding={4} sx={{ justifyContent: 'flex-end' }}>
|
||||
<div style={{ marginRight: '10px' }}> {/* Perubahan sintaksis disini */}
|
||||
<Button
|
||||
variant="outlined"
|
||||
color='inherit'
|
||||
onClick={() => {
|
||||
navigate('/claim-requests')
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setOpenDialogSubmit(true);
|
||||
setApprove('approved');
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
<DialogConfirmation
|
||||
setOpenDialog={setOpenDialogSubmit}
|
||||
requestLog={claimRequests}
|
||||
openDialog={openDialogSubmit}
|
||||
approve={approve}
|
||||
>
|
||||
</DialogConfirmation>
|
||||
</Stack>
|
||||
</Grid>
|
||||
) : null}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -123,6 +123,11 @@ export default function List() {
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
onChange={handleSearchChange}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSearchSubmit(event);
|
||||
}
|
||||
}}
|
||||
value={searchText}
|
||||
placeholder='Search Code or Name...'
|
||||
/>
|
||||
@@ -152,7 +157,7 @@ export default function List() {
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DesktopDatePicker
|
||||
label="End Date"
|
||||
inputFormat="MM/dd/yyyy"
|
||||
inputFormat="dd/MM/yyyy"
|
||||
value={searchParams.get('end_date')}
|
||||
onChange={(value) => {
|
||||
try {
|
||||
@@ -274,7 +279,7 @@ export default function List() {
|
||||
}
|
||||
|
||||
const handleGetData = (type :string) => {
|
||||
axios.get(`corporates/${corporate_id}/data-plan-benefit`)
|
||||
axios.get(`claim-requests/export`)
|
||||
.then((response) => {
|
||||
const link = document.createElement('a');
|
||||
link.href = response.data.data.file_url;
|
||||
@@ -361,14 +366,32 @@ export default function List() {
|
||||
</Stack>
|
||||
)}
|
||||
{importResult && (
|
||||
// <Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
|
||||
// <Box sx={{ color: 'text.secondary' }}>
|
||||
// Last Import Result Report :{' '}
|
||||
// <a href={importResult.result_file?.url ?? '#'}>
|
||||
// {importResult.result_file?.name ?? '-'}
|
||||
// </a>
|
||||
// </Box>
|
||||
// </Stack>
|
||||
|
||||
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
|
||||
<Box sx={{ color: 'text.secondary' }}>
|
||||
Last Import Result Report :{' '}
|
||||
Last Import Result :{' '}
|
||||
<Box sx={{ color: 'success.main', display: 'inline' }}>
|
||||
{importResult.result_file?.total_success_row ?? 0}
|
||||
</Box>{' '}
|
||||
Row Processed,{' '}
|
||||
<Box sx={{ color: 'error.main', display: 'inline' }}>
|
||||
{importResult.result_file?.total_failed_row}
|
||||
</Box>{' '}
|
||||
Failed, Report :{' '}
|
||||
<a href={importResult.result_file?.url ?? '#'}>
|
||||
{importResult.result_file?.name ?? '-'}
|
||||
</a>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -393,7 +416,7 @@ export default function List() {
|
||||
setDataTableLoading(true);
|
||||
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
|
||||
const response = await axios.get('/claim-requests', { params: filter });
|
||||
console.log(response.data);
|
||||
|
||||
setDataTableLoading(false);
|
||||
|
||||
|
||||
@@ -468,8 +491,12 @@ export default function List() {
|
||||
<TableCell align="left">{row.service_name}</TableCell>
|
||||
<TableCell align="left">{row.payment_type_name}</TableCell>
|
||||
<TableCell align="left">
|
||||
{ row.status == "requested" ?
|
||||
{ row.status == "requested" ?
|
||||
(<Label variant='ghost' color='primary'>{capitalizeFirstLetter(row.status)}</Label>) :
|
||||
row.status == "submission" ?
|
||||
(<Label color='info'> {capitalizeFirstLetter(row.status)}</Label>) :
|
||||
row.status == "declined" ?
|
||||
(<Label color='error'> {capitalizeFirstLetter(row.status)}</Label>) :
|
||||
(<Label color='success'> {capitalizeFirstLetter(row.status)}</Label>)
|
||||
}
|
||||
</TableCell>
|
||||
|
||||
@@ -51,6 +51,7 @@ export type DetailClaimRequest = {
|
||||
benefit_data : BenefitData[],
|
||||
diagnosis : Diagnosis[],
|
||||
request_log : RequestLogType | undefined,
|
||||
reason_decline : string,
|
||||
}
|
||||
|
||||
export type RequestLogType = {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ import List from "./List";
|
||||
|
||||
export default function Claims() {
|
||||
|
||||
const pageTitle = 'Claim';
|
||||
const pageTitle = 'Claim Management';
|
||||
return (
|
||||
<Page title={ pageTitle } sx={{ mx: 2}}>
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function Claims() {
|
||||
links={[
|
||||
{ name: 'Dashboard', href: '/dashboard' },
|
||||
{
|
||||
name: 'Claim',
|
||||
name: 'Claim Management',
|
||||
href: '/claims',
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @mui
|
||||
import {
|
||||
Box,
|
||||
Grid,
|
||||
Button,
|
||||
Card,
|
||||
Collapse,
|
||||
@@ -17,12 +18,24 @@ import {
|
||||
ButtonGroup,
|
||||
Tooltip,
|
||||
TableHead,
|
||||
Checkbox,
|
||||
InputAdornment,
|
||||
TableSortLabel,
|
||||
FormControl
|
||||
} from '@mui/material';
|
||||
import { visuallyHidden } from '@mui/utils';
|
||||
|
||||
import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers';
|
||||
import { fDateOnly } from '@/utils/formatTime';
|
||||
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
|
||||
import AssessmentIcon from '@mui/icons-material/Assessment';
|
||||
// hooks
|
||||
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
// components
|
||||
import axios from '../../utils/axios';
|
||||
import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../@types/paginated-data';
|
||||
@@ -32,23 +45,146 @@ import EditRoundedIcon from '@mui/icons-material/EditRounded';
|
||||
import { Chip } from '@mui/material';
|
||||
import Iconify from '@/components/Iconify';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { fDate } from '../../utils/formatTime';
|
||||
import { fDate, fDateTime } from '../../utils/formatTime';
|
||||
import { Claims } from '@/@types/claims';
|
||||
import Label from '@/components/Label';
|
||||
import { capitalizeFirstLetter } from '@/utils/formatString';
|
||||
import TableMoreMenu from '@/components/table/TableMoreMenu';
|
||||
import Edit from '@mui/icons-material/Edit';
|
||||
import { Download } from '@mui/icons-material';
|
||||
import { Add, Search } from '@mui/icons-material';
|
||||
import Autocomplete from '@mui/material/Autocomplete';
|
||||
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
|
||||
import UploadIcon from '@mui/icons-material/Upload';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
|
||||
|
||||
export default function List() {
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const [providers, setProviders] = useState(null);
|
||||
// const [searchText, setSearchText] = useState('');
|
||||
const [order, setOrder] = useState<Order>('desc');
|
||||
const [orderBy, setOrderBy] = useState('created_at');
|
||||
const [perPage, setPerPage] = useState<number>(0);
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
// Jika newValue tidak undefined, atur nilai dataProvider
|
||||
if (newValue !== undefined) {
|
||||
setDataProvider(newValue.service_code);
|
||||
} else {
|
||||
// Jika tidak ada yang dipilih, set dataProvider menjadi string kosong
|
||||
setDataProvider(null);
|
||||
}
|
||||
};
|
||||
// Dummy data
|
||||
const dummyServices = [
|
||||
{ service_code: '1', name: 'Service 1' },
|
||||
{ service_code: '2', name: 'Service 2' },
|
||||
{ service_code: '3', name: 'Service 3' },
|
||||
// tambahkan data lain sesuai kebutuhan
|
||||
];
|
||||
|
||||
|
||||
|
||||
const handleSelectAll = () => {
|
||||
setSelectAll(!selectAll);
|
||||
if (!selectAll) {
|
||||
const requestedIds = dataTableData.data
|
||||
.filter(row => row.status === 'received') // Memfilter baris dengan status 'requested'
|
||||
.map(row => row.id); // Mengambil hanya ID dari baris-baris yang memenuhi kondisi
|
||||
setSelectedRows(requestedIds);
|
||||
} else {
|
||||
setSelectedRows([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRowSelect = (id) => {
|
||||
if (selectedRows.includes(id)) {
|
||||
setSelectedRows(selectedRows.filter(rowId => rowId !== id));
|
||||
} else {
|
||||
setSelectedRows([...selectedRows, id]);
|
||||
}
|
||||
};
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [importResult, setImportResult] = useState(null);
|
||||
const [startDate, setStartDate] = useState(null);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [endDate, setEndDate] = useState(null);
|
||||
const navigate = useNavigate();
|
||||
const [dataProvider, setDataProvider] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (startDate !== null || endDate !== null || dataProvider !== null
|
||||
|| order !== null || orderBy !== null || perPage !== 0) {
|
||||
loadDataTableData();
|
||||
getProvider();
|
||||
}
|
||||
}, [startDate, endDate, dataProvider, order, orderBy, perPage]);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLoadingImport, setIsLoadingImport] = useState(false);
|
||||
const handleExportReport = async () => {
|
||||
|
||||
|
||||
const year = startDate?.getFullYear();
|
||||
const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate = year && month && day ? `${year}-${month}-${day}` : '';
|
||||
|
||||
const year1 = endDate?.getFullYear();
|
||||
const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : '';
|
||||
|
||||
|
||||
|
||||
var filter = Object.fromEntries([...searchParams.entries()]);
|
||||
setIsLoading(true)
|
||||
await axios
|
||||
.get('/claims/export-claim-management',{
|
||||
params: {
|
||||
search: searchText,
|
||||
start_date: formattedDate ? formattedDate : null,
|
||||
end_date:formattedDate1,
|
||||
provider: dataProvider,
|
||||
order: order,
|
||||
orderBy: orderBy,
|
||||
page: perPage,
|
||||
}
|
||||
})
|
||||
.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' },
|
||||
})
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
function SearchInput(props: any) {
|
||||
// SEARCH
|
||||
const searchInput = useRef<HTMLInputElement>(null);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
|
||||
|
||||
const handleSearchChange = (event: any) => {
|
||||
const newSearchText = event.target.value ?? '';
|
||||
@@ -73,7 +209,7 @@ export default function List() {
|
||||
|
||||
useEffect(() => {
|
||||
// Trigger First Search
|
||||
setSearchText(searchParams.get('search') ?? '');
|
||||
// setSearchText(searchParams.get('search') ?? '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -126,41 +262,331 @@ export default function List() {
|
||||
);
|
||||
}
|
||||
|
||||
const searchInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
|
||||
//handle search
|
||||
const handleSearchChange = (event: any) => {
|
||||
const newSearchText = event.target.value ?? '';
|
||||
setSearchText(newSearchText);
|
||||
};
|
||||
|
||||
const handleSearchSubmit = (event: any) => {
|
||||
event.preventDefault();
|
||||
loadDataTableData();
|
||||
};
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Trigger First Search
|
||||
//setSearchText(searchText);
|
||||
}, []);
|
||||
|
||||
const item = [
|
||||
{
|
||||
id: '',
|
||||
value: '',
|
||||
name: 'Semua',
|
||||
},
|
||||
];
|
||||
|
||||
// const handleClick = () => {
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Dummy Default Data
|
||||
const [dataTableIsLoading, setDataTableLoading] = useState(true);
|
||||
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>(
|
||||
LaravelPaginatedDataDefault
|
||||
);
|
||||
|
||||
|
||||
|
||||
const loadDataTableData = async (appliedFilter: any | null = null) => {
|
||||
setDataTableLoading(true);
|
||||
const year = startDate?.getFullYear();
|
||||
const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate = year && month && day ? `${year}-${month}-${day}` : '';
|
||||
|
||||
const year1 = endDate?.getFullYear();
|
||||
const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : '';
|
||||
|
||||
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
|
||||
const response = await axios.get('/claims', { params: filter });
|
||||
// console.log(response.data);
|
||||
const response = await axios.get('/claims', {
|
||||
params: {
|
||||
search: searchText,
|
||||
start_date: formattedDate ? formattedDate : null,
|
||||
end_date:formattedDate1,
|
||||
provider: dataProvider,
|
||||
order: order,
|
||||
orderBy: orderBy,
|
||||
page: perPage,
|
||||
}
|
||||
});
|
||||
|
||||
setDataTableLoading(false);
|
||||
|
||||
setDataTableData(response.data);
|
||||
};
|
||||
|
||||
const getProvider = async () => {
|
||||
const response = await axios.get('/claims/get-provider');
|
||||
setProviders(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);
|
||||
setPerPage(value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, []);
|
||||
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
|
||||
const handleCloseDialogSubmit = () => {
|
||||
setOpenDialogSubmit(false);
|
||||
}
|
||||
|
||||
function toTitleCase(str: string | null) {
|
||||
return str.replace(/\w\S*/g, function(txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
const [approve, setApprove] = useState('');
|
||||
|
||||
const [reasonDecline, setReasonDecline] = useState('');
|
||||
|
||||
const handleReasonDeclineChange = (event) => {
|
||||
setReasonDecline(event.target.value);
|
||||
// Tambahkan logika yang diperlukan di sini
|
||||
};
|
||||
|
||||
const handleSubmitData = () => {
|
||||
//approve or decline
|
||||
if (!reasonDecline && approve == 'decline') {
|
||||
enqueueSnackbar('Mohon isi alasan', { variant: 'warning' });
|
||||
return false;
|
||||
}
|
||||
Promise.all(selectedRows.map(send_bulk))
|
||||
.then(() => {
|
||||
enqueueSnackbar('All requests processed successfully', { variant: 'success' });
|
||||
setOpenDialogSubmit(false);
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000); // Reload the page after 5 seconds
|
||||
})
|
||||
.catch((error) => {
|
||||
enqueueSnackbar(error.response?.data?.message ?? 'Something went wrong!', { variant: 'error' });
|
||||
});
|
||||
};
|
||||
|
||||
function send_bulk(id) {
|
||||
return axios.post(`claims/${id}/${approve}`, { reasonDecline: reasonDecline });
|
||||
}
|
||||
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const createMenu = Boolean(anchorEl);
|
||||
const importHospital = useRef<HTMLInputElement>(null);
|
||||
const [currentImportFileName, setCurrentImportFileName] = useState(null);
|
||||
const [importLoading, setImportLoading] = useState(false);
|
||||
const [importResult, setImportResult] = useState(null);
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
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(() => {
|
||||
// loadDataTableData();
|
||||
// getProvider();
|
||||
// }, []);
|
||||
|
||||
const headStyle = {
|
||||
fontWeight: 'bold',
|
||||
};
|
||||
const headCells = [
|
||||
{
|
||||
id: 'code',
|
||||
align: 'left',
|
||||
label: 'Code',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
align: 'left',
|
||||
label: 'Name',
|
||||
isSort: false,
|
||||
},
|
||||
|
||||
{
|
||||
id: 'member_id',
|
||||
align: 'left',
|
||||
label: 'Member ID',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'created_at',
|
||||
align: 'left',
|
||||
label: 'Date Submission',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'plan_code',
|
||||
align: 'left',
|
||||
label: 'Plan ID',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'service_code',
|
||||
align: 'left',
|
||||
label: 'Service',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'corporate_policies',
|
||||
align: 'left',
|
||||
label: 'Policy Number',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'provider',
|
||||
align: 'left',
|
||||
label: 'Provider',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'tot_bill',
|
||||
align: 'left',
|
||||
label: 'Total Billing',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
align: 'left',
|
||||
label: 'Status',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'action',
|
||||
align: 'left',
|
||||
label: '',
|
||||
isSort: false,
|
||||
},
|
||||
];
|
||||
|
||||
const orders = {
|
||||
order: order,
|
||||
setOrder: setOrder,
|
||||
orderBy: orderBy,
|
||||
setOrderBy: setOrderBy,
|
||||
};
|
||||
const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
|
||||
handleRequestSort(event, property);
|
||||
};
|
||||
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
|
||||
const isAsc = orders?.orderBy === property && orders?.order === 'asc';
|
||||
|
||||
orders?.setOrder(isAsc ? 'desc' : 'asc');
|
||||
orders?.setOrderBy(property);
|
||||
};
|
||||
// Called on every row to map the data to the columns
|
||||
function createData(data: Claims): Claims {
|
||||
return {
|
||||
@@ -171,10 +597,18 @@ export default function List() {
|
||||
{
|
||||
/* ------------------ TABLE ROW ------------------ */
|
||||
}
|
||||
function Row(props: { row: ReturnType<typeof createData> }) {
|
||||
const { row } = props;
|
||||
function Row(props: { row: ReturnType<typeof createData>, isSelected: boolean, onSelect: (id: string) => void }) {
|
||||
const { row, isSelected, onSelect } = props;
|
||||
// Memperbaiki destrukturisasi props
|
||||
|
||||
const handleRowCheckboxChange = () => {
|
||||
onSelect(row.id); // Panggil fungsi onSelect dari komponen induk dengan id baris saat checkbox di baris diklik
|
||||
};
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const test = 1000;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
|
||||
@@ -183,16 +617,22 @@ export default function List() {
|
||||
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
|
||||
</IconButton>
|
||||
</TableCell> */}
|
||||
<TableCell align="left">{row.claim_request?.code}</TableCell>
|
||||
<TableCell align="left">
|
||||
{row?.status == 'received' ? (
|
||||
<Checkbox checked={isSelected} onChange={handleRowCheckboxChange} />
|
||||
):''}
|
||||
</TableCell>
|
||||
<TableCell align="left">{row?.code}</TableCell>
|
||||
{/* <TableCell align="left">{row.code}</TableCell> */}
|
||||
<TableCell align="left">{row.member?.current_plan?.code}</TableCell>
|
||||
<TableCell align="left">{row.member?.current_corporate?.payor_id}</TableCell>
|
||||
<TableCell align="left">{row.member?.current_corporate?.code}</TableCell>
|
||||
<TableCell align="left">{row.member?.current_corporate?.current_policy?.code}</TableCell>
|
||||
<TableCell align="left">{row.member?.member_id}</TableCell>
|
||||
<TableCell align="left">{row.benefit_desc}</TableCell>
|
||||
|
||||
<TableCell align="center">
|
||||
<TableCell align="left">{row?.name}</TableCell>
|
||||
<TableCell align="left">{row?.member_id}</TableCell>
|
||||
<TableCell align="left">{row?.created_at ? fDateTime(row?.created_at) : ''}</TableCell>
|
||||
<TableCell align="left">{row?.plan_code}</TableCell>
|
||||
<TableCell align="left">{row?.service_code}</TableCell>
|
||||
<TableCell align="left">{row?.corporate_policies}</TableCell>
|
||||
<TableCell align="left">{row?.provider}</TableCell>
|
||||
<TableCell align="left">Rp. {row?.tot_bill?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".")}</TableCell>
|
||||
<TableCell align="left">
|
||||
{row.status == 'draft' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
{row.status == 'requested' && (<Label color='primary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
{row.status == 'received' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
@@ -202,18 +642,17 @@ export default function List() {
|
||||
{row.status == 'declined' && (<Label color='error' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
</TableCell>
|
||||
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() =>navigate(`/claims/edit/${row.id}`) }>
|
||||
<Edit />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => navigate('/claims/detail/'+row.id+'') }>
|
||||
<FindInPageOutlinedIcon />
|
||||
Detail
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
<TableCell align="left">
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => navigate('/claims/detail/'+row.id_log+'/'+row.id+'') }>
|
||||
<FindInPageOutlinedIcon />
|
||||
Detail
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</TableCell>
|
||||
|
||||
|
||||
|
||||
</TableRow>
|
||||
@@ -236,40 +675,77 @@ export default function List() {
|
||||
/* ------------------ END TABLE ROW ------------------ */
|
||||
}
|
||||
|
||||
|
||||
|
||||
function TableContent() {
|
||||
return (
|
||||
<Table aria-label="collapsible table">
|
||||
{/* ------------------ TABLE HEADER ------------------ */}
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{/* <TableCell style={headStyle} align="left" /> */}
|
||||
<TableCell style={headStyle} align="left">
|
||||
Code
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Plan ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Payor ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Corporate ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Policy Number
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Member ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Benefit Desc
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Status
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
|
||||
</TableCell>
|
||||
{selectedRows.length > 0 ? (
|
||||
<>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={2}>
|
||||
<Stack direction="row">
|
||||
<Checkbox checked={selectAll} onChange={handleSelectAll} />
|
||||
{selectedRows.length > 0 ? selectedRows.length : '0'} <Typography variant='subtitle2'>Selected</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={6}>
|
||||
|
||||
</TableCell>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="right" colSpan={2}>
|
||||
<Button variant="text" color="error" startIcon={<CancelIcon />} onClick={() => {setOpenDialogSubmit(true);
|
||||
setApprove('decline');}}>
|
||||
<Typography variant='subtitle2'>Decline</Typography>
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={2}>
|
||||
<Button variant="text" color="primary" startIcon={<CheckCircleIcon />} onClick={() => {setOpenDialogSubmit(true);
|
||||
setApprove('approve');}}>
|
||||
<Typography variant='subtitle2'>Approve</Typography>
|
||||
</Button>
|
||||
</TableCell>
|
||||
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TableCell style={headStyle} align="left">
|
||||
<Checkbox checked={selectAll} onChange={handleSelectAll} />
|
||||
</TableCell>
|
||||
{headCells &&
|
||||
headCells.map((headCell, index) => (
|
||||
<TableCell
|
||||
key={index}
|
||||
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
|
||||
// @ts-ignore
|
||||
align={headCell.align}
|
||||
sx={{ padding: 2 }}
|
||||
width={headCell.width ? headCell.width : 'auto'}
|
||||
>
|
||||
{headCell.isSort ? (
|
||||
<TableSortLabel
|
||||
active={orders?.orderBy === headCell.id}
|
||||
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
|
||||
onClick={createSortHandler(headCell.id)}
|
||||
>
|
||||
{headCell.label}
|
||||
{orders?.orderBy === headCell.id ? (
|
||||
<Box component="span" sx={visuallyHidden}>
|
||||
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||
</Box>
|
||||
) : null}
|
||||
</TableSortLabel>
|
||||
) : (
|
||||
headCell.label
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</>
|
||||
|
||||
)}
|
||||
|
||||
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
{/* ------------------ END TABLE HEADER ------------------ */}
|
||||
@@ -278,7 +754,7 @@ export default function List() {
|
||||
{dataTableIsLoading ? (
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} align="center">
|
||||
<TableCell colSpan={11} align="center">
|
||||
Loading
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -286,7 +762,7 @@ export default function List() {
|
||||
) : dataTableData.data.length === 0 ? (
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} align="center">
|
||||
<TableCell colSpan={11} align="center">
|
||||
No Data
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -294,7 +770,7 @@ export default function List() {
|
||||
) : (
|
||||
<TableBody>
|
||||
{dataTableData.data.map((row) => (
|
||||
<Row key={row.id} row={row} />
|
||||
<Row key={row.id} row={row} isSelected={selectedRows.includes(row.id)} onSelect={handleRowSelect} />
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
@@ -305,7 +781,212 @@ export default function List() {
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<ImportForm />
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
sx={{ p: 2, justifyContent: 'space-between', alignItems: 'center' }}
|
||||
>
|
||||
<Grid item xs={12} md={12} lg={12}>
|
||||
<form style={{ width: '100%' }}>
|
||||
<Grid container spacing={1} sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={importHospital}
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleImportChange}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
|
||||
/>
|
||||
{!currentImportFileName && (
|
||||
<>
|
||||
<Grid item xs={12} md={3}>
|
||||
<TextField
|
||||
id="search-input"
|
||||
ref={searchInput}
|
||||
variant="outlined"
|
||||
value={searchText}
|
||||
fullWidth
|
||||
onChange={handleSearchChange}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSearchSubmit(event);
|
||||
}
|
||||
}}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Search />
|
||||
</InputAdornment>
|
||||
),
|
||||
placeholder: 'Search Code or Name',
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4} display="flex" sx={{ gap: '16px' }}>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DesktopDatePicker
|
||||
value={startDate}
|
||||
inputFormat="dd/MM/yyyy"
|
||||
onChange={(value) => {
|
||||
|
||||
// loadDataTableData();
|
||||
setStartDate(value);
|
||||
}}
|
||||
renderInput={(params) => <TextField {...params} fullWidth label="Start" />}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DesktopDatePicker
|
||||
value={endDate}
|
||||
inputFormat="dd/MM/yyyy"
|
||||
onChange={(value) => {
|
||||
setEndDate(value);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
fullWidth
|
||||
label="End"
|
||||
// error={!!error}
|
||||
// helperText={error?.message}
|
||||
// {...other}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
{
|
||||
providers && (
|
||||
<Autocomplete
|
||||
id="provider"
|
||||
options={providers}
|
||||
getOptionLabel={(option) => option.name || ''}
|
||||
value={providers.find((item) => item.id === dataProvider) || null}
|
||||
onChange={(event, value) => {
|
||||
if (value) {
|
||||
setDataProvider(value.id);
|
||||
} else {
|
||||
setDataProvider(null);
|
||||
}
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="Provider"
|
||||
fullWidth
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Grid>
|
||||
<Grid item xs={12} md={3} display="flex" sx={{ gap: '16px' }}>
|
||||
<FormControl >
|
||||
<LoadingButton
|
||||
id="upload-button"
|
||||
variant="outlined"
|
||||
startIcon={<UploadIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
loading={isLoadingImport}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Typography variant="inherit" sx={{ marginLeft: 1 }}>
|
||||
Import
|
||||
</Typography>
|
||||
</LoadingButton>
|
||||
<Menu
|
||||
id="import-button"
|
||||
anchorEl={anchorEl}
|
||||
open={createMenu}
|
||||
onClose={handleClose}
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'basic-button',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleImportButton}>
|
||||
<Typography variant='body2'>Import</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleGetTemplate();
|
||||
}}
|
||||
>
|
||||
<Typography variant='body2'> Download Template</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</FormControl>
|
||||
<FormControl >
|
||||
<LoadingButton
|
||||
id="upload-button"
|
||||
variant="contained"
|
||||
startIcon={<Download />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={handleExportReport}
|
||||
loading={isLoading}
|
||||
>
|
||||
<Typography variant="inherit" sx={{ marginLeft: 1 }}>
|
||||
Export
|
||||
</Typography>
|
||||
</LoadingButton>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
{currentImportFileName && (
|
||||
<Grid item xs={12} md={12}>
|
||||
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
|
||||
<ButtonGroup variant="outlined" aria-label="outlined button group" fullWidth>
|
||||
<Button onClick={handleImportButton} fullWidth>
|
||||
{currentImportFileName ?? 'No File Selected'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCancelImportButton}
|
||||
size="small"
|
||||
fullWidth={false}
|
||||
sx={{ p: 1.8 }}
|
||||
>
|
||||
<CancelIcon color="error" />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
<LoadingButton
|
||||
id="upload-button"
|
||||
variant="outlined"
|
||||
startIcon={<UploadIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={handleUpload}
|
||||
loading={importLoading}
|
||||
>
|
||||
Upload
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</Grid>
|
||||
)}
|
||||
{importResult && (
|
||||
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
|
||||
<Box sx={{ color: 'text.secondary' }}>
|
||||
Last Import Result :{' '}
|
||||
<Box sx={{ color: 'success.main', display: 'inline' }}>
|
||||
{importResult.data.total_success_row ?? 0}
|
||||
</Box>{' '}
|
||||
Row Processed,{' '}
|
||||
<Box sx={{ color: 'error.main', display: 'inline' }}>
|
||||
{importResult.data.total_failed_row}
|
||||
</Box>{' '}
|
||||
Failed
|
||||
{/* {importResult.data.failed_rows.map((row, index) => (
|
||||
<Typography variant='body' key={index} color="error"> [Code={row.code ? row.code : 'Required'}]</Typography>
|
||||
))} */}
|
||||
<u onClick={handleExportReportFiled} style={{cursor:'pointer'}}>Download Data Filed</u>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<DataTable
|
||||
isLoading={dataTableIsLoading}
|
||||
@@ -314,6 +995,43 @@ export default function List() {
|
||||
handlePageChange={handlePageChange}
|
||||
TableContent={<TableContent />}
|
||||
/>
|
||||
<Dialog open={openDialogSubmit} onClose={handleCloseDialogSubmit} fullWidth={true}>
|
||||
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between">
|
||||
<Stack direction="row" alignItems='center' spacing={1}>
|
||||
<Typography variant="h6">Confirmation</Typography>
|
||||
</Stack>
|
||||
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogSubmit}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
|
||||
<Stack spacing={2} padding={2}>
|
||||
<Typography variant='body1'>Are you sure to {toTitleCase(approve)} this claim ?</Typography>
|
||||
{approve == "decline" ? (
|
||||
<Stack direction='row' spacing={2} marginTop={2}>
|
||||
<TextField
|
||||
id="outlined-multiline-static"
|
||||
label="Reason decline"
|
||||
multiline
|
||||
rows={4} // Tentukan jumlah baris yang diinginkan
|
||||
defaultValue=""
|
||||
onChange={handleReasonDeclineChange}
|
||||
variant="outlined"
|
||||
sx={{width:'100%'}}
|
||||
// fullWidth // Gunakan ini jika Anda ingin input memenuhi lebar Stack
|
||||
/>
|
||||
</Stack>
|
||||
): ''}
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogSubmit}>Cancel</Button>
|
||||
<Button sx={{backgroundColor: (approve === 'decline' ? '' : '#19BBBB'), color: (approve === 'decline' ? '#FF4842' : ''), borderColor: '#FF4842'}} onClick={handleSubmitData} variant={(approve === 'decline' ? 'outlined' : 'contained')}>{(approve === "decline" ? 'Decline' : 'Approve')}</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -145,51 +145,62 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
|
||||
const methods = useForm<any>({
|
||||
resolver: yupResolver(validationSchema),
|
||||
defaultValues
|
||||
defaultValues,
|
||||
reValidateMode: "onChange"
|
||||
});
|
||||
|
||||
let width = claimInput ? 2 : 2.36;
|
||||
|
||||
const {fields, append, remove} = useFieldArray({name: 'benefit_data',control: methods.control});
|
||||
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods
|
||||
|
||||
// Buat total data
|
||||
let totalAmountIncurred = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_incurred || 0);
|
||||
}, 0);
|
||||
let totalAmountApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_approved || 0);
|
||||
}, 0);
|
||||
let totalAmountNotApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_not_approved || 0);
|
||||
}, 0);
|
||||
let totalExcessPaid = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.excess_paid || 0);
|
||||
}, 0);
|
||||
const {fields, append, remove} = useFieldArray({name: 'benefit_data',control: methods.control,});
|
||||
const { handleSubmit, reset, watch, setValue, setError, clearErrors, formState: { isDirty, isSubmitting, errors,isValid } } = methods
|
||||
|
||||
const errorsExist = errors ? Object.keys(errors).length > 0 : false;
|
||||
// Calculate
|
||||
const benefitData = watch('benefit_data');
|
||||
const [isDisableSave, setDisableSave] = useState(false)
|
||||
|
||||
benefitData?.map((item, index) => {
|
||||
totalAmountIncurred += parseFloat(item.amount_incurred);
|
||||
totalAmountApproved += parseFloat(item.amount_approved);
|
||||
totalAmountNotApproved += parseFloat(item.amount_not_approved);
|
||||
totalExcessPaid += parseFloat(item.excess_paid);
|
||||
const totalAll = () => {
|
||||
let totalAmountIncurred = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_incurred || 0);
|
||||
}, 0);
|
||||
let totalAmountApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_approved || 0);
|
||||
}, 0);
|
||||
let totalAmountNotApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_not_approved || 0);
|
||||
}, 0);
|
||||
let totalExcessPaid = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.excess_paid || 0);
|
||||
}, 0);
|
||||
|
||||
if (totalAmountApproved != 0 && totalAmountIncurred != 0) {
|
||||
if (totalAmountApproved > totalAmountIncurred){
|
||||
setValue(`benefit_data.${index}.amount_approved`, 0)
|
||||
alert('Total Amount Approved tidak boleh lebih dari Total Incurred')
|
||||
}
|
||||
}
|
||||
benefitData?.map((item, index) => {
|
||||
totalAmountIncurred += parseFloat(item.amount_incurred);
|
||||
totalAmountApproved += parseFloat(item.amount_approved);
|
||||
totalAmountNotApproved += parseFloat(item.amount_not_approved);
|
||||
totalExcessPaid += parseFloat(item.excess_paid);
|
||||
});
|
||||
|
||||
return {
|
||||
totalAmountIncurred,
|
||||
totalAmountApproved,
|
||||
totalAmountNotApproved,
|
||||
totalExcessPaid
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnChangeNominal = (key) => {
|
||||
if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){
|
||||
// setValue(`benefit_data.${key}.amount_approved`, 0);
|
||||
setError(`benefit_data.${key}.amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'});
|
||||
} else {
|
||||
clearErrors(`benefit_data.${key}.amount_approved`);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Submit Form
|
||||
// =====================================
|
||||
const submitHandler = async (data: BenefitConfigurationListType) => {
|
||||
const mapData = data.benefit_data.map((item) => ({
|
||||
...item,
|
||||
reason: item.reason.value
|
||||
reason: item.reason ? item.reason.value : null
|
||||
}));
|
||||
|
||||
const newData = {
|
||||
@@ -206,8 +217,6 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate
|
||||
|
||||
const getContent = () => !addBenefit ? (
|
||||
<Stack spacing={2} sx={{marginTop: 2, padding: 2}} direction="column">
|
||||
<Stack direction="row" spacing={2}>
|
||||
@@ -281,6 +290,10 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
name={`benefit_data.${index}.amount_incurred`}
|
||||
placeholder='Amount Incurred'
|
||||
required
|
||||
onChange={(event) => {
|
||||
setValue(`benefit_data.${index}.amount_incurred`, event.target.value)
|
||||
handleOnChangeNominal(index)}
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -301,6 +314,10 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
name={`benefit_data.${index}.amount_approved`}
|
||||
placeholder='Amount Approved'
|
||||
required
|
||||
onChange={(event) => {
|
||||
setValue(`benefit_data.${index}.amount_approved`, event.target.value)
|
||||
handleOnChangeNominal(index)}
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -434,7 +451,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountIncurred)}
|
||||
{fNumber(totalAll().totalAmountIncurred)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -450,7 +467,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountApproved)}
|
||||
{fNumber(totalAll().totalAmountApproved)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -466,7 +483,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountNotApproved)}
|
||||
{fNumber(totalAll().totalAmountNotApproved)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -482,7 +499,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalExcessPaid)}
|
||||
{fNumber(totalAll().totalExcessPaid)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -499,7 +516,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
<DialogActions>
|
||||
<Stack direction="row" sx={{marginTop:3}} alignItems="center" justifyContent="space-between" spacing={2}>
|
||||
<Button variant="outlined" onClick={handleCloseDialogBenefit}><Typography>Cancel</Typography></Button>
|
||||
<LoadingButton disabled={isDisableSave} type="submit" variant="contained" loading={isSubmitting}>
|
||||
<LoadingButton disabled={errorsExist} type="submit" variant="contained" loading={isSubmitting}>
|
||||
Save
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
|
||||
@@ -62,23 +62,36 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
defaultValues
|
||||
});
|
||||
|
||||
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods;
|
||||
const { handleSubmit, reset, watch, setValue, setError, clearErrors, formState: { isDirty, isSubmitting, errors } } = methods;
|
||||
const errorsExist = errors ? Object.keys(errors).length > 0 : false;
|
||||
const totalAll = () => {
|
||||
// Ambil nilai dari form menggunakan watch
|
||||
const amountIncurred = parseFloat(watch('amount_incurred'));
|
||||
const amountApproved = parseFloat(watch('amount_approved'));
|
||||
const amountNotApproved = parseFloat(watch('amount_not_approved'));
|
||||
const excessPaid = parseFloat(watch('excess_paid'));
|
||||
|
||||
// Hitung total baru
|
||||
const totalAmountIncurred = total.totalAmountIncurred - data?.amount_incurred + amountIncurred;
|
||||
const totalAmountApproved = total.totalAmountApproved - data?.amount_approved + amountApproved;
|
||||
const totalAmountNotApproved = total.totalAmountNotApproved - data?.amount_not_approved + amountNotApproved;
|
||||
const totalExcessPaid = total.totalExcessPaid - data?.excess_paid + excessPaid;
|
||||
|
||||
// Ambil nilai dari form menggunakan watch
|
||||
const amountIncurred = parseFloat(watch('amount_incurred'));
|
||||
const amountApproved = parseFloat(watch('amount_approved'));
|
||||
const amountNotApproved = parseFloat(watch('amount_not_approved'));
|
||||
const excessPaid = parseFloat(watch('excess_paid'));
|
||||
return {
|
||||
totalAmountIncurred,
|
||||
totalAmountApproved,
|
||||
totalAmountNotApproved,
|
||||
totalExcessPaid
|
||||
}
|
||||
}
|
||||
|
||||
// Hitung total baru
|
||||
const totalAmountIncurred = total.totalAmountIncurred - data?.amount_incurred + amountIncurred;
|
||||
const totalAmountApproved = total.totalAmountApproved - data?.amount_approved + amountApproved;
|
||||
const totalAmountNotApproved = total.totalAmountNotApproved - data?.amount_not_approved + amountNotApproved;
|
||||
const totalExcessPaid = total.totalExcessPaid - data?.excess_paid + excessPaid;
|
||||
|
||||
if (totalAmountApproved > totalAmountIncurred) {
|
||||
alert('Total Approve tidak boleh melebihi Total Incurred')
|
||||
setValue('amount_approved', data?.amount_approved)
|
||||
const handleOnChangeNominal = (key) => {
|
||||
if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){
|
||||
// setValue(`benefit_data.${key}.amount_approved`, 0);
|
||||
setError(`amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'});
|
||||
} else {
|
||||
clearErrors(`amount_approved`);
|
||||
}
|
||||
}
|
||||
|
||||
// if (totalAmountIncurred !== (totalAmountApproved+totalAmountNotApproved)){
|
||||
@@ -142,6 +155,10 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
name={`amount_incurred`}
|
||||
placeholder='Amount Incurred'
|
||||
required
|
||||
onChange={(event) => {
|
||||
setValue(`amount_incurred`, event.target.value)
|
||||
handleOnChangeNominal(id)}
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -162,6 +179,10 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
name={`amount_approved`}
|
||||
placeholder='Amount Approved'
|
||||
required
|
||||
onChange={(event) => {
|
||||
setValue(`amount_approved`, event.target.value)
|
||||
handleOnChangeNominal(id)}
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -275,7 +296,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{totalAmountIncurred ? fNumber(totalAmountIncurred) : 0}
|
||||
{totalAll().totalAmountIncurred ? fNumber(totalAll().totalAmountIncurred) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -291,7 +312,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountApproved)}
|
||||
{totalAll().totalAmountApproved ? fNumber(totalAll().totalAmountApproved) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -307,7 +328,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountNotApproved)}
|
||||
{totalAll().totalAmountNotApproved ? fNumber(totalAll().totalAmountNotApproved) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -323,7 +344,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalExcessPaid)}
|
||||
{totalAll().totalExcessPaid ? fNumber(totalAll().totalExcessPaid) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -337,7 +358,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
<DialogActions>
|
||||
<Stack direction="row" sx={{marginTop:3}} alignItems="center" justifyContent="space-between" spacing={2}>
|
||||
<Button variant="outlined" onClick={handleCloseDialog}><Typography>Cancel</Typography></Button>
|
||||
<LoadingButton type="submit" variant="contained" loading={isSubmitting}>
|
||||
<LoadingButton disabled={errorsExist} type="submit" variant="contained" loading={isSubmitting}>
|
||||
Save
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
|
||||
@@ -68,6 +68,7 @@ export default function Detail() {
|
||||
const navigate = useNavigate();
|
||||
const { themeStretch } = useSettings();
|
||||
const [requestLog, setRequestLog] = useState<DetailFinalLogType>();
|
||||
const [isReversal, setIsReversal] = useState(false);
|
||||
|
||||
|
||||
const { id } = useParams();
|
||||
@@ -77,6 +78,7 @@ export default function Detail() {
|
||||
.get('customer-service/request/'+id)
|
||||
.then((response) => {
|
||||
setRequestLog(response.data.data)
|
||||
setIsReversal(response.data.data.is_reversal)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
@@ -304,11 +306,15 @@ export default function Detail() {
|
||||
<Card sx={{padding:2}} >
|
||||
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Benefit</Typography>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogBenefit(true);
|
||||
}} >
|
||||
<Typography variant="button" display="block">Benefit</Typography>
|
||||
</Button>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogBenefit(true);
|
||||
}} >
|
||||
<Typography variant="button" display="block">Benefit</Typography>
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
</Stack>
|
||||
|
||||
{requestLog?.benefit_data?.map((item, index) => (
|
||||
@@ -321,29 +327,33 @@ export default function Detail() {
|
||||
{item.benefit?.description}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
|
||||
<MoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => {
|
||||
setDialogEditBenefit(true)
|
||||
setIdBenefitData(item.id)
|
||||
setBenefitConfigurationData(item)
|
||||
}}
|
||||
>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
setIdBenefitData(item.id)
|
||||
setDialogDeleteBenefit(true)
|
||||
}}
|
||||
>
|
||||
<Delete color='error'/>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Grid>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
|
||||
<MoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => {
|
||||
setDialogEditBenefit(true)
|
||||
setIdBenefitData(item.id)
|
||||
setBenefitConfigurationData(item)
|
||||
}}
|
||||
>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
setIdBenefitData(item.id)
|
||||
setDialogDeleteBenefit(true)
|
||||
}}
|
||||
>
|
||||
<Delete color='error'/>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Grid>
|
||||
) : null
|
||||
}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
@@ -619,13 +629,16 @@ export default function Detail() {
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Files </Typography>
|
||||
</Stack>
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogUploadFileLog(true)
|
||||
}} >
|
||||
<Typography variant="button" display="block">Files</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
{ !isReversal ? (
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogUploadFileLog(true)
|
||||
}} >
|
||||
<Typography variant="button" display="block">Files</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
) : null }
|
||||
|
||||
</Stack>
|
||||
{requestLog?.files?.map((documentType, index) => (
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{marginBottom: 2}} key={index}>
|
||||
@@ -638,14 +651,17 @@ export default function Detail() {
|
||||
<Typography variant="body2" gutterBottom>{documentType.original_name ? documentType.original_name : '-'}</Typography>
|
||||
</a>
|
||||
</Stack>
|
||||
<Stack direction="column" spacing={2}>
|
||||
<IconButton onClick={() => {
|
||||
setDialogDeleteFileLog(true)
|
||||
setPathFile(documentType.path)
|
||||
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
|
||||
<Delete color='error' fontSize="small" />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
{ !isReversal ? (
|
||||
<Stack direction="column" spacing={2}>
|
||||
<IconButton onClick={() => {
|
||||
setDialogDeleteFileLog(true)
|
||||
setPathFile(documentType.path)
|
||||
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
|
||||
<Delete color='error' fontSize="small" />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
) : null }
|
||||
|
||||
</Stack>
|
||||
))}
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@ export default function Router() {
|
||||
element: <ClaimsCreate />,
|
||||
},
|
||||
{
|
||||
path: 'claims/detail/:id',
|
||||
path: 'claims/detail/:id/:id_claim',
|
||||
element: <ClaimsDetail />,
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user