Merge remote-tracking branch 'origin/staging' into origin/production
This commit is contained in:
109
frontend/dashboard/src/@types/claims.ts
Normal file
109
frontend/dashboard/src/@types/claims.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { Benefit } from "./corporates";
|
||||
import { Member } from "./member";
|
||||
|
||||
export type ClaimRequest = {
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
submission_date: string;
|
||||
payment_type: string;
|
||||
service_code: string;
|
||||
claim_method: string;
|
||||
service_type: string;
|
||||
code_provider: string;
|
||||
file_condition: Files;
|
||||
member: Member;
|
||||
claim: {
|
||||
organization: Organizations
|
||||
}
|
||||
};
|
||||
|
||||
export type Claims = {
|
||||
id: number;
|
||||
code: string;
|
||||
plan: Plan;
|
||||
payor_id: string;
|
||||
corporate_id: string;
|
||||
policy_number: string;
|
||||
benefit_desc: string;
|
||||
member: Member;
|
||||
benefit: Benefit | boolean;
|
||||
status: string;
|
||||
claim_request: ClaimRequest;
|
||||
};
|
||||
|
||||
export type ClaimsEdit = {
|
||||
id: number;
|
||||
plan_id: string;
|
||||
payor_id: string;
|
||||
corporate_id: string;
|
||||
policy_number: string;
|
||||
member_id: string;
|
||||
benefit_code: string;
|
||||
benefit_desc: string;
|
||||
amount_incurred: number;
|
||||
amount_approved: number;
|
||||
amount_not_approved: number;
|
||||
excess_paid: number;
|
||||
}
|
||||
|
||||
export type Files = {
|
||||
name: string;
|
||||
url: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export type Plan = {
|
||||
code: string;
|
||||
}
|
||||
|
||||
export type ClaimHistoryCare = {
|
||||
id: number;
|
||||
claim_id: number;
|
||||
service_code: string;
|
||||
admission_date: string;
|
||||
discharge_date: string;
|
||||
main_diagnosis_id: number;
|
||||
main_diagnosis_name: string;
|
||||
medical_record_number: string;
|
||||
organization_id: number;
|
||||
practitioner_id: number;
|
||||
organization_name: string;
|
||||
practitioner_name: string;
|
||||
secondary_diagnosis_id: number[];
|
||||
sign: string;
|
||||
symptoms: string;
|
||||
name: any;
|
||||
}
|
||||
|
||||
export type Organizations = {
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
address: string;
|
||||
type: string;
|
||||
lat: string;
|
||||
lng: string;
|
||||
phone: string;
|
||||
timezone: string;
|
||||
active: boolean | number;
|
||||
province_id: number;
|
||||
city_id: number;
|
||||
district_id: number;
|
||||
village_id: number;
|
||||
postal_code: string;
|
||||
description: string;
|
||||
technology: string;
|
||||
support_services: string;
|
||||
merchant_code: string;
|
||||
merchant_key: string;
|
||||
image_url: string;
|
||||
region_groups: string;
|
||||
};
|
||||
|
||||
export type Import = {
|
||||
result_file: {
|
||||
url: string,
|
||||
name: string,
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ export type Corporate = {
|
||||
code: string;
|
||||
name?: string;
|
||||
welcome_message?: string;
|
||||
payor_id: string;
|
||||
help_text?: string;
|
||||
logo?: any;
|
||||
logo_url?: string;
|
||||
@@ -12,6 +13,10 @@ export type Corporate = {
|
||||
divisions?: Division[];
|
||||
employees?: Employee[];
|
||||
current_policy?: Policy;
|
||||
corporate_plans_count: number;
|
||||
corporate_benefits_count: number;
|
||||
employees_count: number;
|
||||
|
||||
};
|
||||
|
||||
export type Division = {
|
||||
@@ -21,6 +26,14 @@ export type Division = {
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export type Hospital = {
|
||||
id: number;
|
||||
corporate_id: number;
|
||||
code: string;
|
||||
name?: string;
|
||||
active: number;
|
||||
}
|
||||
|
||||
export type Employee = {
|
||||
id: number;
|
||||
name: string;
|
||||
@@ -39,14 +52,19 @@ export type Policy = {
|
||||
minimal_stop_service_net: number;
|
||||
start: string | Date;
|
||||
end: string | Date;
|
||||
limit_balance: number;
|
||||
}
|
||||
|
||||
export type CorporatePlan = {
|
||||
id: number;
|
||||
corporate_id: number;
|
||||
code: string;
|
||||
service_code: string;
|
||||
limit_rules: number;
|
||||
corporate_plan_id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
type: number;
|
||||
active: boolean | number;
|
||||
}
|
||||
|
||||
@@ -101,6 +119,7 @@ export type Plan = {
|
||||
currency: string;
|
||||
max_surgery_reinstatement_days: string;
|
||||
max_surgery_periode_days: string;
|
||||
active: number
|
||||
}
|
||||
|
||||
export type CorporateBenefit = {
|
||||
@@ -113,6 +132,7 @@ export type CorporateBenefit = {
|
||||
}
|
||||
|
||||
export type Benefit = {
|
||||
id : number;
|
||||
service_code : string;
|
||||
plan_code : string;
|
||||
benefit_code : string;
|
||||
@@ -170,6 +190,11 @@ export type Benefit = {
|
||||
currency : string;
|
||||
show_benefit_item : string;
|
||||
show_benefit_value : string;
|
||||
plan : Plan;
|
||||
benefit: Benefit;
|
||||
corporate_benefit_code: string;
|
||||
active: number;
|
||||
limit_free_tc: number;
|
||||
}
|
||||
|
||||
export type CorporateService = {
|
||||
@@ -182,3 +207,14 @@ export type CorporateService = {
|
||||
status: string;
|
||||
configurations: any;
|
||||
}
|
||||
|
||||
export type MasterExclusion = {
|
||||
id?: string | number;
|
||||
name?: string;
|
||||
code: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export type CorporateId = {
|
||||
corporate_id?: number
|
||||
}
|
||||
@@ -5,9 +5,12 @@ export type Icd = {
|
||||
version?: string;
|
||||
code: string;
|
||||
name: string;
|
||||
service_code: string;
|
||||
active: number;
|
||||
description?: any;
|
||||
childs?: Icd[];
|
||||
status: string;
|
||||
rules:any
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
import { Corporate, Plan } from "./corporates";
|
||||
|
||||
export type Member = {
|
||||
id: string,
|
||||
member_id: string,
|
||||
@@ -18,4 +20,7 @@ export type Member = {
|
||||
relation_with_principal: string,
|
||||
bpjs_class: string,
|
||||
active: string,
|
||||
current_plans: Plan,
|
||||
current_corporate: Corporate,
|
||||
full_name: string,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export type Drug = {
|
||||
id: number;
|
||||
type: string;
|
||||
code: string;
|
||||
name: string;
|
||||
version:string;
|
||||
active: number;
|
||||
}
|
||||
@@ -10,12 +10,15 @@ import { ProgressBarStyle } from './components/ProgressBar';
|
||||
import ThemeColorPresets from './components/ThemeColorPresets';
|
||||
import MotionLazyContainer from './components/animate/MotionLazyContainer';
|
||||
import { SnackbarProvider } from 'notistack';
|
||||
import { Provider } from 'react-redux';
|
||||
import store from "./store"
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<Provider store={store}>
|
||||
<ThemeProvider>
|
||||
<SnackbarProvider>
|
||||
<ThemeColorPresets>
|
||||
<RtlLayout>
|
||||
@@ -29,5 +32,6 @@ export default function App() {
|
||||
</ThemeColorPresets>
|
||||
</SnackbarProvider>
|
||||
</ThemeProvider>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
208
frontend/dashboard/src/components/DialogUpdateStatus.tsx
Normal file
208
frontend/dashboard/src/components/DialogUpdateStatus.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
import * as Yup from 'yup';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Dialog, DialogTitle, DialogContent, Stack, Typography, IconButton, Grid } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { ReactElement } from 'react';
|
||||
import Iconify from './Iconify';
|
||||
import { Card } from '@mui/material';
|
||||
import { FormProvider, RHFTextField, RHFSwitch, RHFSelect } from './hook-form';
|
||||
import { Button } from '@mui/material';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import axios from '@/utils/axios';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type DataContent = {
|
||||
code: string;
|
||||
name: string;
|
||||
id: number;
|
||||
status: string
|
||||
};
|
||||
|
||||
type MuiDialogProps = {
|
||||
title?: {
|
||||
name?: string;
|
||||
icon?: string;
|
||||
};
|
||||
openDialog: boolean;
|
||||
setOpenDialog: Function;
|
||||
content?: ReactElement;
|
||||
maxWidth?: string;
|
||||
data?: DataContent | undefined;
|
||||
description: string;
|
||||
};
|
||||
|
||||
type FormValuesProps = {
|
||||
value: string;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const DialogUpdateStatus = ({ title, openDialog, setOpenDialog, data, maxWidth, content }: MuiDialogProps) => {
|
||||
const NewCorporateSchema = Yup.object().shape({
|
||||
reason: Yup.string().required('Corporate Status is required'),
|
||||
});
|
||||
|
||||
const methods = useForm<FormValuesProps>({
|
||||
resolver: yupResolver(NewCorporateSchema),
|
||||
});
|
||||
|
||||
const {
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting },
|
||||
} = methods;
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (openDialog === false) {
|
||||
reset();
|
||||
}
|
||||
}, [openDialog, reset]);
|
||||
|
||||
const handleClose = () => {
|
||||
setOpenDialog(false);
|
||||
};
|
||||
|
||||
const handleUpdate = (id: number, status: number) => {
|
||||
axios
|
||||
.put(`/corporates/${id}/activation`, {
|
||||
// service_code: service.service_code,
|
||||
active: status,
|
||||
})
|
||||
.then((res) => {
|
||||
handleClose()
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
// console.log('asdasd', error.response.data.message)
|
||||
enqueueSnackbar(
|
||||
error.response.data.message ?? error.message ?? 'Failed Processing Request',
|
||||
{ variant: 'error' }
|
||||
);
|
||||
});
|
||||
}
|
||||
let maxWidthDialog = 'md';
|
||||
|
||||
if (maxWidth) {
|
||||
maxWidthDialog = maxWidth;
|
||||
}
|
||||
|
||||
const onSubmit = async (row : any) => {
|
||||
console.log('test')
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={openDialog} onClose={handleClose} fullWidth={true} maxWidth={'sm'}>
|
||||
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between">
|
||||
{title?.icon ? (
|
||||
<Stack direction="row">
|
||||
<Iconify icon={title?.icon} width={25} height={25} sx={{ marginRight: '10px' }} />
|
||||
<Typography variant="h6">{title?.name}</Typography>
|
||||
</Stack>
|
||||
) : (
|
||||
<Typography variant="h6">{title?.name ? title?.name : ''}</Typography>
|
||||
)}
|
||||
<IconButton sx={{ color: '#FFF' }} onClick={handleClose}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ backgroundColor: '#F9FAFB' }}>
|
||||
|
||||
{/* <Stack paddingX={2} paddingY={2}>
|
||||
<Typography variant='subtitle1'>{description}</Typography>
|
||||
</Stack>
|
||||
<Card>
|
||||
<Grid container paddingX={2} paddingY={2}>
|
||||
<Grid item xs={4} md={4}>
|
||||
<Typography variant='inherit'>Code</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={8}>
|
||||
<Typography variant='subtitle1'>{data?.code}</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={4} md={4} marginTop={2}>
|
||||
<Typography variant='inherit'>Corporate Name</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={8} marginTop={2}>
|
||||
<Typography variant='subtitle1'>{data?.name}</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
|
||||
<Typography marginTop={5} marginBottom={3} variant='subtitle1'>
|
||||
Reason for update*
|
||||
</Typography>
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<RHFSelect
|
||||
name="reason"
|
||||
label="Reason for update"
|
||||
>
|
||||
<option value=""></option>
|
||||
<option value="Agreement changed">Agreement changed</option>
|
||||
<option value="Endorsement">Endorsement</option>
|
||||
<option value="Renewal">Renewal</option>
|
||||
<option value="Worng Setting">Worng Setting</option>
|
||||
</RHFSelect>
|
||||
</FormProvider>
|
||||
<Stack
|
||||
alignItems="center"
|
||||
justifyContent="flex-end"
|
||||
direction={{ xs: 'column', md: 'row' }}
|
||||
spacing={2}
|
||||
marginTop={5}
|
||||
>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<Button
|
||||
sx={{
|
||||
boxShadow: 'none',
|
||||
}}
|
||||
variant="outlined"
|
||||
size="medium"
|
||||
fullWidth={true}
|
||||
onClick={() => setOpenDialog(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
{data?.status == 1 ?
|
||||
<Button
|
||||
sx={{
|
||||
boxShadow: 'none',
|
||||
}}
|
||||
variant="contained"
|
||||
size="medium"
|
||||
fullWidth={true}
|
||||
color='error'
|
||||
onClick={() => handleUpdate(data?.id, 0)}
|
||||
>
|
||||
Inactive
|
||||
</Button>
|
||||
: <Button
|
||||
sx={{
|
||||
boxShadow: 'none',
|
||||
}}
|
||||
variant="contained"
|
||||
size="medium"
|
||||
fullWidth={true}
|
||||
color='success'
|
||||
onClick={() => handleUpdate(data?.id, 1)}
|
||||
>
|
||||
Active
|
||||
</Button> }
|
||||
</Stack>
|
||||
</Stack> */}
|
||||
{content}
|
||||
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default DialogUpdateStatus;
|
||||
98
frontend/dashboard/src/components/Label.tsx
Normal file
98
frontend/dashboard/src/components/Label.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
// @mui
|
||||
import { alpha, Theme, useTheme, styled } from '@mui/material/styles';
|
||||
import { BoxProps } from '@mui/material';
|
||||
// theme
|
||||
import { ColorSchema } from '../theme/palette';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type LabelColor = 'default' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error';
|
||||
|
||||
type LabelVariant = 'filled' | 'outlined' | 'ghost';
|
||||
|
||||
const RootStyle = styled('span')(
|
||||
({
|
||||
theme,
|
||||
ownerState,
|
||||
}: {
|
||||
theme: Theme;
|
||||
ownerState: {
|
||||
color: LabelColor;
|
||||
variant: LabelVariant;
|
||||
};
|
||||
}) => {
|
||||
const isLight = theme.palette.mode === 'light';
|
||||
const { color, variant } = ownerState;
|
||||
|
||||
const styleFilled = (color: ColorSchema) => ({
|
||||
color: theme.palette[color].contrastText,
|
||||
backgroundColor: theme.palette[color].main,
|
||||
});
|
||||
|
||||
const styleOutlined = (color: ColorSchema) => ({
|
||||
color: theme.palette[color].main,
|
||||
backgroundColor: 'transparent',
|
||||
border: `1px solid ${theme.palette[color].main}`,
|
||||
});
|
||||
|
||||
const styleGhost = (color: ColorSchema) => ({
|
||||
color: theme.palette[color][isLight ? 'dark' : 'light'],
|
||||
backgroundColor: alpha(theme.palette[color].main, 0.16),
|
||||
});
|
||||
|
||||
return {
|
||||
height: 22,
|
||||
minWidth: 22,
|
||||
lineHeight: 0,
|
||||
borderRadius: 6,
|
||||
// cursor: 'default',
|
||||
alignItems: 'center',
|
||||
whiteSpace: 'nowrap',
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
padding: theme.spacing(0, 1),
|
||||
color: theme.palette.grey[800],
|
||||
fontSize: theme.typography.pxToRem(12),
|
||||
fontFamily: theme.typography.fontFamily,
|
||||
backgroundColor: theme.palette.grey[300],
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
|
||||
...(color !== 'default'
|
||||
? {
|
||||
...(variant === 'filled' && { ...styleFilled(color) }),
|
||||
...(variant === 'outlined' && { ...styleOutlined(color) }),
|
||||
...(variant === 'ghost' && { ...styleGhost(color) }),
|
||||
}
|
||||
: {
|
||||
...(variant === 'outlined' && {
|
||||
backgroundColor: 'transparent',
|
||||
color: theme.palette.text.primary,
|
||||
border: `1px solid ${theme.palette.grey[500_32]}`,
|
||||
}),
|
||||
...(variant === 'ghost' && {
|
||||
color: isLight ? theme.palette.text.secondary : theme.palette.common.white,
|
||||
backgroundColor: theme.palette.grey[500_16],
|
||||
}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface Props extends BoxProps {
|
||||
color?: LabelColor;
|
||||
variant?: LabelVariant;
|
||||
}
|
||||
|
||||
export default function Label({ color = 'default', variant = 'ghost', children, sx }: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<RootStyle ownerState={{ color, variant }} sx={sx} theme={theme}>
|
||||
{children}
|
||||
</RootStyle>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
58
frontend/dashboard/src/components/MoreMenu.tsx
Normal file
58
frontend/dashboard/src/components/MoreMenu.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import Iconify from '@/components/Iconify';
|
||||
import MenuPopover from './MenuPopover';
|
||||
import { IconButton, MenuItem } from '@mui/material';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type Props = {
|
||||
actions: React.ReactNode;
|
||||
};
|
||||
|
||||
export default function MoreMenu({ actions }: Props) {
|
||||
const [open, setOpen] = useState<HTMLElement | null>(null);
|
||||
|
||||
// Close menu popover
|
||||
useEffect(() => {
|
||||
setOpen(null);
|
||||
}, [actions])
|
||||
|
||||
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setOpen(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton onClick={handleOpen}>
|
||||
<Iconify icon={'eva:more-vertical-fill'} width={20} height={20} />
|
||||
</IconButton>
|
||||
|
||||
<MenuPopover
|
||||
open={Boolean(open)}
|
||||
anchorEl={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'left' }}
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
arrow="right-top"
|
||||
sx={{
|
||||
mt: -1,
|
||||
width: 'auto',
|
||||
minWidth: 160,
|
||||
'& .MuiMenuItem-root': {
|
||||
px: 1,
|
||||
typography: 'body2',
|
||||
borderRadius: 0.75,
|
||||
'& svg': { mr: 2, width: 20, height: 20 },
|
||||
},
|
||||
}}
|
||||
>
|
||||
{actions}
|
||||
</MenuPopover>
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Dialog, DialogTitle, DialogContent, Stack, Typography, IconButton } from '@mui/material';
|
||||
import { Dialog, DialogTitle, DialogContent, Stack, Typography, IconButton, DialogActions } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { ReactElement } from 'react';
|
||||
import Iconify from './Iconify';
|
||||
@@ -13,12 +13,13 @@ type MuiDialogProps = {
|
||||
openDialog: boolean;
|
||||
setOpenDialog: Function;
|
||||
content?: ReactElement;
|
||||
action?: ReactElement|null;
|
||||
maxWidth?: string;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const MuiDialog = ({ title, openDialog, setOpenDialog, content, maxWidth }: MuiDialogProps) => {
|
||||
const MuiDialog = ({ title, openDialog, setOpenDialog, content, action, maxWidth }: MuiDialogProps) => {
|
||||
const handleClose = () => {
|
||||
setOpenDialog(false);
|
||||
};
|
||||
@@ -46,9 +47,15 @@ const MuiDialog = ({ title, openDialog, setOpenDialog, content, maxWidth }: MuiD
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent sx={{ backgroundColor: '#F9FAFB' }}>
|
||||
{content ? content : 'Testing Content Dialog'}
|
||||
</DialogContent>
|
||||
|
||||
{action ? (
|
||||
<DialogActions> {action} </DialogActions>
|
||||
) : ''}
|
||||
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function ThemeColorPresets({ children }: Props) {
|
||||
...defaultTheme,
|
||||
palette: {
|
||||
...defaultTheme.palette,
|
||||
primary: setColor,
|
||||
// primary: setColor,
|
||||
},
|
||||
customShadows: {
|
||||
...defaultTheme.customShadows,
|
||||
|
||||
@@ -291,8 +291,8 @@ const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialog
|
||||
spacing={1}
|
||||
sx={{ marginY: 2 }}
|
||||
>
|
||||
{claim.files_by_type?.result &&
|
||||
claim.files_by_type.result.map((file, index) => (
|
||||
{claim.files_by_type?.claim_result &&
|
||||
claim.files_by_type.claim_result.map((file, index) => (
|
||||
<Stack direction="row" justifyContent={'space-between'} key={index}>
|
||||
<a href={file.url} target="_blank" style={{ textDecoration: 'none' }}>
|
||||
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
|
||||
|
||||
209
frontend/dashboard/src/components/history/History.tsx
Normal file
209
frontend/dashboard/src/components/history/History.tsx
Normal file
@@ -0,0 +1,209 @@
|
||||
// @mui
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Collapse,
|
||||
Container,
|
||||
FormControl,
|
||||
Grid,
|
||||
IconButton,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
OutlinedInput,
|
||||
Paper,
|
||||
Select,
|
||||
SelectChangeEvent,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TextField,
|
||||
Typography,
|
||||
Badge,
|
||||
Stack,
|
||||
} from '@mui/material';
|
||||
import * as React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp';
|
||||
import MuiAccordion, { AccordionProps } from '@mui/material/Accordion';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import MuiAccordionSummary, {
|
||||
AccordionSummaryProps,
|
||||
} from '@mui/material/AccordionSummary';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import axios from '../../utils/axios';
|
||||
import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext';
|
||||
import MuiAccordionDetails from '@mui/material/AccordionDetails';
|
||||
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
|
||||
import { Corporate } from '@/@types/corporates';
|
||||
import { fDate, fDateTime } from '@/utils/formatTime';
|
||||
|
||||
const Accordion = styled((props: AccordionProps) => (
|
||||
<MuiAccordion disableGutters elevation={0} square {...props} />
|
||||
))(({ theme }) => ({
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
'&:not(:last-child)': {
|
||||
borderBottom: 0,
|
||||
},
|
||||
'&:before': {
|
||||
display: 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
const AccordionSummary = styled((props: AccordionSummaryProps) => (
|
||||
<MuiAccordionSummary
|
||||
expandIcon={<ArrowForwardIosSharpIcon sx={{ fontSize: '0.9rem' }} />}
|
||||
{...props}
|
||||
/>
|
||||
))(({ theme }) => ({
|
||||
backgroundColor:
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgba(255, 255, 255, .05)'
|
||||
: 'rgba(0, 0, 0, .03)',
|
||||
flexDirection: 'row-reverse',
|
||||
'& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': {
|
||||
transform: 'rotate(90deg)',
|
||||
},
|
||||
'& .MuiAccordionSummary-content': {
|
||||
marginLeft: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
|
||||
padding: theme.spacing(2),
|
||||
borderTop: '1px solid rgba(0, 0, 0, .125)',
|
||||
}));
|
||||
|
||||
export default function CustomizedAccordions() {
|
||||
const [expanded, setExpanded] = React.useState<string | false>('panel1');
|
||||
|
||||
const handleChange =
|
||||
(panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
|
||||
setExpanded(newExpanded ? panel : false);
|
||||
};
|
||||
const pageTitle = 'Audittrail Corporate';
|
||||
|
||||
const { themeStretch } = useSettings();
|
||||
|
||||
const { corporate_id } = useParams();
|
||||
|
||||
const [corporate, setCorporate] = useState<Corporate | null>();
|
||||
const [ currentCorporate, setCurrentCorporate ] = useState<Corporate>();
|
||||
|
||||
const configuredCorporateContext = useContext(ConfiguredCorporateContext);
|
||||
|
||||
useEffect(() => {
|
||||
setCorporate(configuredCorporateContext.currentCorporate);
|
||||
const model = 'App\\Models\\Corporate';
|
||||
const url = `/audittrail/${corporate_id}?model=${model}`;
|
||||
axios.get(url)
|
||||
.then((res) => {
|
||||
setCurrentCorporate(res.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Terjadi kesalahan:', error);
|
||||
});
|
||||
|
||||
}, [configuredCorporateContext]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<HeaderBreadcrumbs
|
||||
heading={pageTitle}
|
||||
links={[
|
||||
{
|
||||
name: 'Corporates',
|
||||
href: '/corporates',
|
||||
},
|
||||
{
|
||||
name: corporate?.name ?? '-',
|
||||
href: '/corporate/' + corporate_id,
|
||||
},
|
||||
{
|
||||
name: 'Audittrail Corporate',
|
||||
href: '/corporate/' + corporate_id + '/plans',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{currentCorporate?.data.map((item, index) => (
|
||||
<Accordion
|
||||
key={index}
|
||||
expanded={expanded === `panel${index}`}
|
||||
onChange={handleChange(`panel${index}`)}
|
||||
>
|
||||
<AccordionSummary
|
||||
aria-controls={`panel${index}d-content`}
|
||||
id={`panel${index}d-header`}
|
||||
>
|
||||
<Typography>{`Data has ${item.action} by ${item.user_id} on ${fDateTime(item.updated_at)}`}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="center">Field</TableCell>
|
||||
<TableCell align="center">Old Value</TableCell>
|
||||
<TableCell align="center">New Values</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{Object.entries(item.old_values).map(([key, value]) => {
|
||||
let renderedValue;
|
||||
if (key === 'deleted_by' || key === 'created_by' || key === 'updated_by') {
|
||||
return null; // Melewati iterasi saat key adalah 'deleted_by'
|
||||
}
|
||||
switch (key) {
|
||||
case 'welcome_message':
|
||||
renderedValue = item.new_values[key].replace(/<[^>]*>/g, '');
|
||||
value = value.replace(/<[^>]*>/g, '');
|
||||
break;
|
||||
case 'help_text':
|
||||
renderedValue = item.new_values[key].replace(/<[^>]*>/g, '');
|
||||
value = value.replace(/<[^>]*>/g, '');
|
||||
break;
|
||||
case 'active':
|
||||
renderedValue = item.new_values[key] == 1 ? 'Active' : 'Inactive';
|
||||
value = value == 1 ? 'Active' : 'Inactive';
|
||||
break;
|
||||
case 'created_at':
|
||||
renderedValue = fDateTime(item.new_values[key]);
|
||||
value = fDateTime(value);
|
||||
break;
|
||||
case 'updated_at':
|
||||
renderedValue = fDateTime(item.new_values[key]);
|
||||
value = fDateTime(value);
|
||||
break;
|
||||
case 'updated_at':
|
||||
renderedValue = fDateTime(item.new_values[key]);
|
||||
value = fDateTime(value);
|
||||
break;
|
||||
case 'delete_at':
|
||||
renderedValue = fDateTime(item.new_values[key]);
|
||||
value = fDateTime(value);
|
||||
break;
|
||||
default:
|
||||
renderedValue = item.new_values[key];
|
||||
break;
|
||||
}
|
||||
|
||||
const field = key.charAt(0).toUpperCase() + key.slice(1);
|
||||
|
||||
return (
|
||||
<TableRow key={key} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
|
||||
<TableCell>{`${field}`}</TableCell>
|
||||
<TableCell align="center">{`${value}`}</TableCell>
|
||||
<TableCell align="center">{renderedValue}</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
|
||||
</TableBody>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { FormHelperText } from "@mui/material";
|
||||
import { Autocomplete, TextField } from "@mui/material";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
|
||||
type Props = {
|
||||
name: string,
|
||||
label: string,
|
||||
options: string[];
|
||||
disabled?: boolean,
|
||||
}
|
||||
|
||||
const RHFAutocompleteNonTerminology = ({ ...props }: Props) => {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={props.name}
|
||||
control={control}
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
let xValue = (value && value.codingCode!=="" ? value : null );
|
||||
|
||||
return (
|
||||
<>
|
||||
<Autocomplete
|
||||
handleHomeEndKeys
|
||||
fullWidth
|
||||
options={props.options}
|
||||
value={xValue}
|
||||
disabled={props.disabled}
|
||||
freeSolo={false}
|
||||
onChange={(e, newValue) => {
|
||||
onChange(newValue);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label={props.label}
|
||||
name={props.name}
|
||||
error={!!error}
|
||||
variant="outlined"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}}
|
||||
|
||||
/>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default RHFAutocompleteNonTerminology
|
||||
@@ -0,0 +1,102 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { Autocomplete, AutocompleteProps, FormHelperText, TextField, UseAutocompleteProps } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import { FilterOptionsState } from '@mui/material/useAutocomplete';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string,
|
||||
label: string,
|
||||
options: any,
|
||||
getOptionLabel: (option: any) => string,
|
||||
isOptionEqualToValue: any,
|
||||
disableClearable: boolean,
|
||||
freeSolo: boolean,
|
||||
renderOption?: any,
|
||||
onInputChange?: any,
|
||||
onKeyDown?: any,
|
||||
onKeyPress?: any,
|
||||
noOptionsText?: string,
|
||||
popupIcon?: any,
|
||||
disabled?: boolean,
|
||||
sx?: any,
|
||||
sxTextField?: any,
|
||||
InputProps?: any,
|
||||
onSelect?: (option: any) => void,
|
||||
filterOptions?: (options: any[], state: FilterOptionsState<any>) => any[];
|
||||
}
|
||||
|
||||
const RHFAutocompleteV2 = ( {name, label, options, ...rest} : IProps ) => {
|
||||
const { control } = useFormContext();
|
||||
const {
|
||||
sx, sxTextField, InputProps,
|
||||
onSelect,
|
||||
getOptionLabel, filterOptions,
|
||||
popupIcon,
|
||||
isOptionEqualToValue, disableClearable,
|
||||
freeSolo, renderOption,
|
||||
onInputChange, onKeyDown, onKeyPress,
|
||||
noOptionsText, disabled } = rest;
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<>
|
||||
<Autocomplete
|
||||
disabled={disabled}
|
||||
handleHomeEndKeys
|
||||
getOptionLabel={getOptionLabel}
|
||||
options={options}
|
||||
freeSolo={freeSolo}
|
||||
disableClearable={disableClearable}
|
||||
isOptionEqualToValue={isOptionEqualToValue}
|
||||
value={value}
|
||||
filterOptions={filterOptions}
|
||||
renderOption={renderOption}
|
||||
onInputChange={onInputChange}
|
||||
onKeyDown={onKeyDown}
|
||||
onKeyPress={onKeyPress}
|
||||
sx={sx}
|
||||
popupIcon={popupIcon}
|
||||
noOptionsText={noOptionsText}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label={label}
|
||||
sx={sxTextField}
|
||||
InputProps={{
|
||||
...params.InputProps,
|
||||
...InputProps
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
onChange={(e, newValue) => {
|
||||
onChange(newValue);
|
||||
onSelect && onSelect(newValue);
|
||||
}}
|
||||
/>
|
||||
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
|
||||
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default RHFAutocompleteV2;
|
||||
@@ -1,7 +1,8 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { Checkbox, FormControlLabel, FormGroup, FormControlLabelProps } from '@mui/material';
|
||||
import { Checkbox, FormControlLabel, FormGroup, FormControlLabelProps, SxProps } from '@mui/material';
|
||||
import { Theme } from '@mui/system';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -77,9 +78,10 @@ interface optionsCustomInterface {
|
||||
interface RHFCustomMultiCheckboxProps {
|
||||
name: string;
|
||||
options: optionsCustomInterface[];
|
||||
sx?: SxProps<Theme>
|
||||
}
|
||||
|
||||
export function RHFCustomMultiCheckbox({ name, options, ...other }: RHFCustomMultiCheckboxProps) {
|
||||
export function RHFCustomMultiCheckbox({ name, options, sx, ...other }: RHFCustomMultiCheckboxProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
@@ -93,7 +95,7 @@ export function RHFCustomMultiCheckbox({ name, options, ...other }: RHFCustomMul
|
||||
: [...field.value, option.value];
|
||||
|
||||
return (
|
||||
<FormGroup>
|
||||
<FormGroup sx={{ ...sx }}>
|
||||
{options.map((option, index) => (
|
||||
<FormControlLabel
|
||||
key={index}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { FormHelperText, TextField } from '@mui/material';
|
||||
import { DesktopDatePicker, LocalizationProvider } from '@mui/lab';
|
||||
import AdapterDateFns from '@mui/lab/AdapterDateFns';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
label: string;
|
||||
dateFormat: string;
|
||||
fullWidth?: boolean;
|
||||
minDate?: any;
|
||||
maxDate?: any;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default function RHFDatePicker({ name, label, dateFormat, minDate, maxDate, disabled, ...other }: IProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
const { fullWidth } = other;
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<DesktopDatePicker
|
||||
disabled={disabled}
|
||||
label={label}
|
||||
onChange={(date)=> field.onChange(date)}
|
||||
inputFormat={dateFormat}
|
||||
value={field.value}
|
||||
mask={''}
|
||||
minDate={(minDate) ? new Date(minDate) : null}
|
||||
maxDate={(maxDate) ? new Date(maxDate) : null}
|
||||
renderInput={(params) => <TextField
|
||||
fullWidth={fullWidth}
|
||||
{...params}
|
||||
/>}
|
||||
/>
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
}
|
||||
@@ -62,6 +62,7 @@ export default function RHFDatepicker({ name, ...other }: IProps & TextFieldProp
|
||||
value={field.value}
|
||||
inputFormat="dd/MMM/yyyy"
|
||||
// inputFormat="dd - MMM - yyyy"
|
||||
mask=''
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ReactNode } from 'react';
|
||||
// form
|
||||
import { FormProvider as Form, UseFormReturn } from 'react-hook-form';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
methods: UseFormReturn<any>;
|
||||
onSubmit?: VoidFunction;
|
||||
preventEnterSubmit?: boolean;
|
||||
};
|
||||
|
||||
export default function FormProvider({ children, onSubmit, methods, preventEnterSubmit }: Props) {
|
||||
const checkKeyDown = (e: any) => {
|
||||
if (e.key === 'Enter' && preventEnterSubmit){
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...methods}>
|
||||
<form onSubmit={onSubmit} onKeyDown={(e) => checkKeyDown(e)}>{children}</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { Autocomplete, AutocompleteProps, FormHelperText, TextField, UseAutocompleteProps } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import { FilterOptionsState } from '@mui/material/useAutocomplete';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string,
|
||||
label: string,
|
||||
options: any,
|
||||
getOptionLabel: (option: any) => string,
|
||||
isOptionEqualToValue: any,
|
||||
disableClearable: boolean,
|
||||
freeSolo: boolean,
|
||||
renderOption?: any,
|
||||
onInputChange?: any,
|
||||
onKeyDown?: any,
|
||||
onKeyPress?: any,
|
||||
noOptionsText?: string,
|
||||
popupIcon?: any,
|
||||
disabled?: boolean,
|
||||
sx?: any,
|
||||
sxTextField?: any,
|
||||
InputProps?: any,
|
||||
onSelect?: (option: any) => void,
|
||||
filterOptions?: (options: any[], state: FilterOptionsState<any>) => any[];
|
||||
}
|
||||
|
||||
const RHFAutocomplete = ( {name, label, options, ...rest} : IProps ) => {
|
||||
const { control } = useFormContext();
|
||||
const {
|
||||
sx, sxTextField, InputProps,
|
||||
onSelect,
|
||||
getOptionLabel, filterOptions,
|
||||
popupIcon,
|
||||
isOptionEqualToValue, disableClearable,
|
||||
freeSolo, renderOption,
|
||||
onInputChange, onKeyDown, onKeyPress,
|
||||
noOptionsText, disabled } = rest;
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<>
|
||||
<Autocomplete
|
||||
disabled={disabled}
|
||||
handleHomeEndKeys
|
||||
getOptionLabel={getOptionLabel}
|
||||
options={options}
|
||||
freeSolo={freeSolo}
|
||||
disableClearable={disableClearable}
|
||||
isOptionEqualToValue={isOptionEqualToValue}
|
||||
value={value}
|
||||
filterOptions={filterOptions}
|
||||
renderOption={renderOption}
|
||||
onInputChange={onInputChange}
|
||||
onKeyDown={onKeyDown}
|
||||
onKeyPress={onKeyPress}
|
||||
sx={sx}
|
||||
popupIcon={popupIcon}
|
||||
noOptionsText={noOptionsText}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label={label}
|
||||
sx={sxTextField}
|
||||
InputProps={{
|
||||
...params.InputProps,
|
||||
...InputProps
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
onChange={(e, newValue) => {
|
||||
onChange(newValue);
|
||||
onSelect && onSelect(newValue);
|
||||
}}
|
||||
/>
|
||||
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
|
||||
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default RHFAutocomplete;
|
||||
@@ -0,0 +1,75 @@
|
||||
import * as React from 'react';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Autocomplete from '@mui/material/Autocomplete';
|
||||
import { FormHelperText } from '@mui/material';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface IProps {
|
||||
name: string,
|
||||
label: string,
|
||||
options: any,
|
||||
defaultValue: any
|
||||
}
|
||||
|
||||
const RHFAutocompleteTags = ({ name, label, options, defaultValue, ...rest }: IProps) => {
|
||||
const { control } = useFormContext();
|
||||
const fixedOptions: any = [];
|
||||
const [value, setValue] = React.useState([...fixedOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(defaultValue)
|
||||
}, [options, defaultValue])
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field: { onChange }, fieldState: { error } }) => {
|
||||
return (
|
||||
<>
|
||||
<Autocomplete
|
||||
multiple
|
||||
id="fixed-tags-demo"
|
||||
value={value}
|
||||
onChange={(event, newValue) => {
|
||||
setValue([
|
||||
...fixedOptions,
|
||||
...newValue.filter((option) => fixedOptions.indexOf(option) === -1),
|
||||
]);
|
||||
onChange(newValue);
|
||||
}}
|
||||
isOptionEqualToValue={(option, value)=>{
|
||||
return option.optionID === value.optionID
|
||||
}}
|
||||
options={options}
|
||||
getOptionLabel={(option: { optionID: string, optionLabel: string }) => `${option.optionLabel}` || ""}
|
||||
renderTags={(tagValue, getTagProps) =>
|
||||
tagValue.map((option, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<Chip
|
||||
label={option?.optionLabel}
|
||||
{...getTagProps({ index })}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))
|
||||
}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label={label} placeholder={label} />
|
||||
)}
|
||||
/>
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default RHFAutocompleteTags;
|
||||
111
frontend/dashboard/src/components/hook-form/v2/RHFCheckbox.tsx
Normal file
111
frontend/dashboard/src/components/hook-form/v2/RHFCheckbox.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { Checkbox, FormControlLabel, FormGroup, FormControlLabelProps, Grid, FormHelperText } from '@mui/material';
|
||||
import RHFDatePicker from './RHFDatePicker';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface RHFCheckboxProps extends Omit<FormControlLabelProps, 'control'> {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function RHFCheckbox({ name, ...other }: RHFCheckboxProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<Checkbox {...field} checked={field.value} />
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
|
||||
)}
|
||||
/>
|
||||
}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
interface RHFCheckboxEndDateProps extends Omit<FormControlLabelProps, 'control'> {
|
||||
name: string;
|
||||
endPeriodProp: any;
|
||||
idx?: any;
|
||||
}
|
||||
|
||||
export function RHFCheckboxEndDate({ name, endPeriodProp, idx, ...other }: RHFCheckboxEndDateProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field }) =>
|
||||
<Checkbox
|
||||
{...field} checked={field.value}
|
||||
onChange={e => {
|
||||
endPeriodProp(e.target.checked, idx)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface RHFMultiCheckboxProps extends Omit<FormControlLabelProps, 'control' | 'label'> {
|
||||
name: string;
|
||||
options: string[];
|
||||
}
|
||||
|
||||
export function RHFMultiCheckbox({ name, options, ...other }: RHFMultiCheckboxProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
const onSelected = (option: string) =>
|
||||
field.value.includes(option)
|
||||
? field.value.filter((value: string) => value !== option)
|
||||
: [...field.value, option];
|
||||
|
||||
return (
|
||||
<FormGroup>
|
||||
{options.map((option) => (
|
||||
<FormControlLabel
|
||||
key={option}
|
||||
control={
|
||||
<Checkbox
|
||||
checked={field.value.includes(option)}
|
||||
onChange={() => field.onChange(onSelected(option))}
|
||||
/>
|
||||
}
|
||||
label={option}
|
||||
{...other}
|
||||
/>
|
||||
))}
|
||||
</FormGroup>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { FormHelperText, TextField } from '@mui/material';
|
||||
import { DesktopDatePicker, LocalizationProvider } from '@mui/lab';
|
||||
import AdapterDateFns from '@mui/lab/AdapterDateFns';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
label: string;
|
||||
dateFormat: string;
|
||||
fullWidth?: boolean;
|
||||
minDate?: any;
|
||||
maxDate?: any;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default function RHFDatePicker({ name, label, dateFormat, minDate, maxDate, disabled, ...other }: IProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
const { fullWidth } = other;
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<DesktopDatePicker
|
||||
allowSameDateSelection={true}
|
||||
disabled={disabled}
|
||||
label={label}
|
||||
onChange={(date) => field.onChange(date)}
|
||||
inputFormat={dateFormat}
|
||||
value={field.value}
|
||||
mask={''}
|
||||
minDate={(minDate) ? new Date(minDate) : null}
|
||||
maxDate={(maxDate) ? new Date(maxDate) : null}
|
||||
renderInput={(params) => <TextField fullWidth={fullWidth} {...params} />}
|
||||
/>
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// form
|
||||
import { useFormContext, Controller, useForm } from 'react-hook-form';
|
||||
// @mui
|
||||
import { FormHelperText, TextField } from '@mui/material';
|
||||
import { DesktopDateTimePicker, LocalizationProvider } from '@mui/lab';
|
||||
import AdapterDateFns from '@mui/lab/AdapterDateFns';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
label: string;
|
||||
dateFormat: string;
|
||||
fullWidth?: boolean;
|
||||
onChange?: (date: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default function RHFDateTimePicker({ name, label, dateFormat, ...other }: IProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
const { fullWidth, onChange, disabled } = other;
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<DesktopDateTimePicker
|
||||
label={label}
|
||||
onChange={(date) => {
|
||||
field.onChange(date);
|
||||
onChange && onChange(date);
|
||||
}}
|
||||
inputFormat={dateFormat}
|
||||
value={field.value}
|
||||
mask={''}
|
||||
disabled={disabled}
|
||||
renderInput={(params) => <TextField fullWidth={fullWidth} {...params} />}
|
||||
/>
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
}
|
||||
37
frontend/dashboard/src/components/hook-form/v2/RHFEditor.tsx
Normal file
37
frontend/dashboard/src/components/hook-form/v2/RHFEditor.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { FormHelperText } from '@mui/material';
|
||||
//
|
||||
import Editor, { Props as EditorProps } from '../../editor';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface Props extends EditorProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default function RHFEditor({ name, ...other }: Props) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<Editor
|
||||
id={name}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!error}
|
||||
helperText={
|
||||
<FormHelperText error sx={{ px: 2, textTransform: 'capitalize' }}>
|
||||
{error?.message}
|
||||
</FormHelperText>
|
||||
}
|
||||
{...other}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import {
|
||||
Radio,
|
||||
RadioGroup,
|
||||
FormHelperText,
|
||||
RadioGroupProps,
|
||||
FormControlLabel,
|
||||
} from '@mui/material';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
options: string[];
|
||||
getOptionLabel?: string[];
|
||||
fullWidth?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default function RHFRadioGroup({
|
||||
name,
|
||||
options,
|
||||
getOptionLabel,
|
||||
fullWidth,
|
||||
disabled,
|
||||
...other
|
||||
}: IProps & RadioGroupProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<div>
|
||||
<RadioGroup {...field} row {...other}>
|
||||
{options.map((option, index) => (
|
||||
<FormControlLabel
|
||||
style={{width: (fullWidth)?'100%':''}}
|
||||
key={option}
|
||||
value={option}
|
||||
control={<Radio />}
|
||||
label={getOptionLabel?.length ? getOptionLabel[index] : option}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
))}
|
||||
</RadioGroup>
|
||||
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
35
frontend/dashboard/src/components/hook-form/v2/RHFSelect.tsx
Normal file
35
frontend/dashboard/src/components/hook-form/v2/RHFSelect.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { TextField, TextFieldProps } from '@mui/material';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
children: any;
|
||||
}
|
||||
|
||||
export default function RHFSelect({ name, children, ...other }: IProps & TextFieldProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
select
|
||||
fullWidth
|
||||
SelectProps={{ native: true }}
|
||||
error={!!error}
|
||||
helperText={error?.message}
|
||||
{...other}
|
||||
>
|
||||
{children}
|
||||
</TextField>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { FormControl, FormHelperText, InputLabel, Select, SelectProps } from '@mui/material';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
id: string;
|
||||
children: any;
|
||||
}
|
||||
|
||||
export default function RHFSelectV2({ name, id, children, ...other }: IProps & SelectProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl fullWidth error={error !== undefined ? true : false}>
|
||||
<InputLabel id={`${id}-label`}>{other.label}</InputLabel>
|
||||
<Select {...field} labelId={`${id}-label`} id={id} fullWidth {...other}>
|
||||
{children}
|
||||
</Select>
|
||||
{error && <FormHelperText>{error.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
29
frontend/dashboard/src/components/hook-form/v2/RHFSwitch.tsx
Normal file
29
frontend/dashboard/src/components/hook-form/v2/RHFSwitch.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { Switch, FormControlLabel, FormControlLabelProps } from '@mui/material';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type IProps = Omit<FormControlLabelProps, 'control'>;
|
||||
|
||||
interface Props extends IProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default function RHFSwitch({ name, ...other }: Props) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field }) => <Switch {...field} checked={field.value} />}
|
||||
/>
|
||||
}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { TextField, TextFieldProps, Typography } from '@mui/material';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default function RHFTextField({ name, ...other }: IProps & TextFieldProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
{/* <Typography>
|
||||
*
|
||||
</Typography> */}
|
||||
<TextField {...field} autoComplete="off" fullWidth error={!!error} helperText={error?.message} {...other} />
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { InputAdornment, TextField, TextFieldProps, Typography } from '@mui/material';
|
||||
import MoneyFormat from '../../numeric_format/MoneyFormat';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default function RHFTextFieldMoney({ name, ...other }: IProps & TextFieldProps) {
|
||||
const { control, watch, setValue } = useFormContext();
|
||||
const values = watch();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<TextField
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
fullWidth error={!!error}
|
||||
helperText={error?.message}
|
||||
{...other}
|
||||
inputProps={{ min: 0, max: 5, style: { textAlign: 'right' } }}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">Rp</InputAdornment>,
|
||||
inputComponent: MoneyFormat as any,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { InputAdornment, TextField, TextFieldProps, Typography } from '@mui/material';
|
||||
import MoneyFormat from '../../numeric_format/MoneyFormat';
|
||||
// import AutoNumeric from "autonumeric"
|
||||
// import { useEffect, useRef } from 'react';
|
||||
// import React from 'react';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
endAdornment?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function RHFTextFieldNumber({ name, endAdornment, ...other }: IProps & TextFieldProps) {
|
||||
const { control, watch, setValue } = useFormContext();
|
||||
const values = watch();
|
||||
|
||||
// const ref = React.createRef();
|
||||
// const mountedRef = useRef(false);
|
||||
// useEffect(() => {
|
||||
// mountedRef.current = true;
|
||||
// new AutoNumeric(ref.current as HTMLElement)
|
||||
|
||||
// return () => {
|
||||
// mountedRef.current = false;
|
||||
// }
|
||||
// }, [])
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<TextField
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
fullWidth error={!!error}
|
||||
helperText={error?.message}
|
||||
onFocus={() => { (watch(name) == '0') && setValue(name, '') }}
|
||||
onBlur={() => { (watch(name) == '') && setValue(name, '0') }}
|
||||
{...other}
|
||||
inputProps={{ min: 0, max: 5, style: { textAlign: 'right' } }}
|
||||
InputProps={{
|
||||
inputComponent: MoneyFormat as any,
|
||||
endAdornment: endAdornment,
|
||||
|
||||
}}
|
||||
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { InputAdornment, TextField, TextFieldProps, Typography } from '@mui/material';
|
||||
import MoneyFormat from '../../numeric_format/MoneyFormat';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default function RHFTextFieldPercentage({ name, ...other }: IProps & TextFieldProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<TextField
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
fullWidth error={!!error}
|
||||
helperText={error?.message}
|
||||
{...other}
|
||||
inputProps={{ min: 0, max: 5, style: { textAlign: 'right' } }}
|
||||
InputProps={{
|
||||
endAdornment: <InputAdornment position="end">%</InputAdornment>,
|
||||
inputComponent: MoneyFormat as any,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { FormHelperText, TextField } from '@mui/material';
|
||||
import { DesktopDatePicker, LocalizationProvider, TimePicker } from '@mui/lab';
|
||||
import AdapterDateFns from '@mui/lab/AdapterDateFns';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
label: string;
|
||||
fullWidth?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default function RHFTimePicker({ name, label, disabled, ...other }: IProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
const { fullWidth } = other;
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<TimePicker
|
||||
disabled={disabled}
|
||||
label={label}
|
||||
onChange={(date) => field.onChange(date)}
|
||||
value={field.value}
|
||||
renderInput={(params) => <TextField fullWidth={fullWidth} {...params} />}
|
||||
/>
|
||||
{!!error && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
}
|
||||
112
frontend/dashboard/src/components/hook-form/v2/RHFUpload.tsx
Normal file
112
frontend/dashboard/src/components/hook-form/v2/RHFUpload.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
// form
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
// @mui
|
||||
import { FormHelperText } from '@mui/material';
|
||||
// type
|
||||
import {
|
||||
UploadAvatar,
|
||||
UploadMultiFile,
|
||||
UploadSingleFile,
|
||||
UploadProps,
|
||||
UploadMultiFileProps,
|
||||
} from '../../upload';
|
||||
import { Accept } from 'react-dropzone';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface Props extends Omit<UploadProps, 'file'> {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function RHFUploadAvatar({ name, ...other }: Props) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
const checkError = !!error && !field.value;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<UploadAvatar error={checkError} {...other} file={field.value} />
|
||||
{checkError && (
|
||||
<FormHelperText error sx={{ px: 2, textAlign: 'center' }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function RHFUploadSingleFile({ name, ...other }: Props) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
const checkError = !!error && !field.value;
|
||||
|
||||
return (
|
||||
<UploadSingleFile
|
||||
accept={"image/*" as unknown as Accept}
|
||||
file={field.value}
|
||||
error={checkError}
|
||||
helperText={
|
||||
checkError && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error.message}
|
||||
</FormHelperText>
|
||||
)
|
||||
}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface RHFUploadMultiFileProps extends Omit<UploadMultiFileProps, 'files'> {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function RHFUploadMultiFile({ name, ...other }: RHFUploadMultiFileProps) {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
const checkError = !!error && field.value?.length === 0;
|
||||
|
||||
return (
|
||||
<UploadMultiFile
|
||||
accept={"image/*" as unknown as Accept}
|
||||
files={field.value}
|
||||
error={checkError}
|
||||
helperText={
|
||||
checkError && (
|
||||
<FormHelperText error sx={{ px: 2 }}>
|
||||
{error?.message}
|
||||
</FormHelperText>
|
||||
)
|
||||
}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
12
frontend/dashboard/src/components/hook-form/v2/index.ts
Normal file
12
frontend/dashboard/src/components/hook-form/v2/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export * from './RHFCheckbox';
|
||||
export * from './RHFUpload';
|
||||
|
||||
export { default as FormProvider } from './FormProvider';
|
||||
|
||||
export { default as RHFSwitch } from './RHFSwitch';
|
||||
export { default as RHFAutocomplete } from './RHFAutocomplete';
|
||||
export { default as RHFSelect } from './RHFSelect';
|
||||
export { default as RHFEditor } from './RHFEditor';
|
||||
export { default as RHFTextField } from './RHFTextField';
|
||||
export { default as RHFRadioGroup } from './RHFRadioGroup';
|
||||
export { default as RHFSelectV2 } from './RHFSelectV2';
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from "react";
|
||||
import { InputAttributes, NumericFormat, NumericFormatProps } from "react-number-format";
|
||||
|
||||
interface CustomProps {
|
||||
onChange: (event: { target: { name: string; value: string } }) => void;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const DiscountPctFormat = React.forwardRef<
|
||||
NumericFormatProps<InputAttributes>,
|
||||
CustomProps
|
||||
>(function DiscountPctFormat(props, ref) {
|
||||
const { onChange, ...other } = props;
|
||||
|
||||
return (
|
||||
<NumericFormat
|
||||
{...other}
|
||||
getInputRef={ref}
|
||||
onValueChange={(values) => {
|
||||
onChange({
|
||||
target: {
|
||||
name: props.name,
|
||||
value: values.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
thousandSeparator
|
||||
valueIsNumericString
|
||||
allowLeadingZeros={false}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default DiscountPctFormat;
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from "react";
|
||||
import { InputAttributes, NumericFormat, NumericFormatProps } from "react-number-format";
|
||||
|
||||
interface CustomProps {
|
||||
onChange: (event: { target: { name: string; value: string } }) => void;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const MoneyFormat = React.forwardRef<
|
||||
NumericFormatProps<InputAttributes>,
|
||||
CustomProps
|
||||
>(function MoneyFormat(props, ref) {
|
||||
const { onChange, ...other } = props;
|
||||
|
||||
return (
|
||||
<NumericFormat
|
||||
{...other}
|
||||
getInputRef={ref}
|
||||
onValueChange={(values) => {
|
||||
onChange({
|
||||
target: {
|
||||
name: props.name,
|
||||
value: values.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
thousandSeparator
|
||||
valueIsNumericString
|
||||
allowLeadingZeros={false}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default MoneyFormat;
|
||||
1
frontend/dashboard/src/components/table/Index.ts
Normal file
1
frontend/dashboard/src/components/table/Index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as TableMoreMenu } from './TableMoreMenu';
|
||||
60
frontend/dashboard/src/components/table/TableMoreMenu.tsx
Normal file
60
frontend/dashboard/src/components/table/TableMoreMenu.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
// @mui
|
||||
import { IconButton } from '@mui/material';
|
||||
//
|
||||
import Iconify from '../Iconify';
|
||||
import MenuPopover from '../MenuPopover';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type Props = {
|
||||
actions: React.ReactNode;
|
||||
disableRipple?: boolean;
|
||||
};
|
||||
|
||||
export default function TableMoreMenu({ actions, disableRipple }: Props) {
|
||||
const [open, setOpen] = useState<HTMLElement | null>(null);
|
||||
|
||||
// Close menu popover
|
||||
useEffect(() => {
|
||||
setOpen(null);
|
||||
}, [actions])
|
||||
|
||||
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setOpen(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton onClick={handleOpen} disableRipple={disableRipple}>
|
||||
<Iconify icon={'eva:more-vertical-fill'} width={20} height={20} />
|
||||
</IconButton>
|
||||
|
||||
<MenuPopover
|
||||
open={Boolean(open)}
|
||||
anchorEl={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'left' }}
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
arrow="right-top"
|
||||
sx={{
|
||||
mt: -1,
|
||||
width: 'auto',
|
||||
minWidth: 160,
|
||||
'& .MuiMenuItem-root': {
|
||||
px: 1,
|
||||
typography: 'body2',
|
||||
borderRadius: 0.75,
|
||||
'& svg': { mr: 2, width: 20, height: 20 },
|
||||
},
|
||||
}}
|
||||
>
|
||||
{actions}
|
||||
</MenuPopover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -26,9 +26,6 @@ function ConfiguredCorporateProvider({ children }: ConfiguredCorporateProviderPr
|
||||
const [corporate, setCorporate] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Load Corporate
|
||||
console.log('calling corporate' + corporate_id);
|
||||
|
||||
axios.get(`corporates/${corporate_id}`)
|
||||
.then((res) => {
|
||||
setCorporate(res.data)
|
||||
|
||||
98
frontend/dashboard/src/hooks/useLoadOnScroll.ts
Normal file
98
frontend/dashboard/src/hooks/useLoadOnScroll.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { RefObject, useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface FetchFunction<T> {
|
||||
(page: number): Promise<T[]>;
|
||||
}
|
||||
|
||||
const useLoadOnScroll = <T>(executeFetch: FetchFunction<T>) => {
|
||||
const [data, setData] = useState<T[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [page, setPage] = useState<number>(1);
|
||||
const [listener, setListener] = useState<number>(1);
|
||||
const [lastPage, setLastPage] = useState<boolean>(false);
|
||||
|
||||
const fetchData = async (isSearch?: boolean) => {
|
||||
if (!lastPage || isSearch === true)
|
||||
if (isLoading === false) {
|
||||
setIsLoading(true);
|
||||
if (isSearch === true) {
|
||||
const newData = await executeFetch(1);
|
||||
if (newData.length > 0) {
|
||||
setData(newData);
|
||||
setPage((prevPage) => 2);
|
||||
} else {
|
||||
setLastPage(true);
|
||||
setData([]);
|
||||
}
|
||||
} else {
|
||||
const newData = await executeFetch(page);
|
||||
if (newData.length > 0) {
|
||||
setData((prevData) => [...prevData, ...newData]);
|
||||
setPage((prevPage) => prevPage + 1);
|
||||
} else {
|
||||
setLastPage(true);
|
||||
}
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const refetchData = () => {
|
||||
setPage(1);
|
||||
setListener((prev) => prev + 1);
|
||||
};
|
||||
|
||||
const resetLastPage = () => {
|
||||
setLastPage(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [listener]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (data.length === 0) {
|
||||
// fetchData();
|
||||
// }
|
||||
// }, [data]);
|
||||
|
||||
const handleScroll = () => {
|
||||
const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
|
||||
|
||||
if (scrollTop + clientHeight >= scrollHeight - 1) {
|
||||
setListener((prevListener) => prevListener + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const resetSearch = () => {
|
||||
console.log('reset search');
|
||||
fetchData(true);
|
||||
resetLastPage();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onClose = () => {
|
||||
setData([]);
|
||||
setPage(1);
|
||||
setLastPage(false);
|
||||
};
|
||||
|
||||
const onOpen = () => {
|
||||
setData([]);
|
||||
setLastPage(false);
|
||||
fetchData(true);
|
||||
};
|
||||
|
||||
// setData and setPage to reset when the dialog closed
|
||||
return { data, isLoading, setPage, setData, resetSearch, resetLastPage, onClose, onOpen, refetchData };
|
||||
};
|
||||
|
||||
export default useLoadOnScroll;
|
||||
@@ -42,6 +42,7 @@ const navConfig = [
|
||||
{
|
||||
title: 'PHARMACY & DELIVERY MANAGEMENT',
|
||||
children: [
|
||||
{ title: 'Drug', path: '/master/drugs'},
|
||||
{ title: 'Inventory', path: '/inventory' },
|
||||
{ title: 'Delivery Services', path: '/delivery' },
|
||||
],
|
||||
@@ -52,9 +53,8 @@ const navConfig = [
|
||||
children: [
|
||||
{ title: 'Corporate', path: '/corporates' },
|
||||
// { title: 'Corporate Create', path: '/corporates/create' },
|
||||
{ title: 'Formularium', path: '/master/formularium' },
|
||||
{ title: 'Obat', path: '/master/drugs' },
|
||||
{ title: 'Diagnosis Library (ICD-X)', path: '/master/diagnosis' },
|
||||
{ title: 'Formularium', path: '/master/formularium-template-v2' },
|
||||
{ title: 'Master ICD-10 Diagnosis', path: '/master/diagnosis' },
|
||||
{ title: 'Hospitals', path: '/hospitals' },
|
||||
],
|
||||
},
|
||||
@@ -72,11 +72,20 @@ const navConfig = [
|
||||
// { title: 'Report', path: '/case-report' },
|
||||
// ],
|
||||
},
|
||||
{
|
||||
title: 'CASE MANAGEMENT',
|
||||
children: [
|
||||
{ title: 'Daily Monitoring', path: '/case_management/daily_monitoring' },
|
||||
{ title: 'Laboratorium Result', path: '/case_management/laboratorium_result' },
|
||||
{ title: 'Inpatient Monitoring', path: '/case_management/inpatient_monitoring' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'CUSTOMER SERVICES',
|
||||
children: [
|
||||
{ title: 'Request', path: '/cs-request' },
|
||||
{ title: 'Membership', path: '/cs-membership' },
|
||||
{ title: 'Request', path: '/custormer-service/request' },
|
||||
// { title: 'Membership', path: '/cs-membership' },
|
||||
{ title: 'Final LOG', path: '/custormer-service/final-log' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -90,6 +99,13 @@ const navConfig = [
|
||||
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'MASTER',
|
||||
children: [
|
||||
{ title: 'Diagnosis', path: '/master/diagnosis' },
|
||||
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'USER MANAGEMENT',
|
||||
path: '/users',
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Box, Grid, IconButton, Typography } from '@mui/material';
|
||||
import { ArrowBackIosNew } from '@mui/icons-material';
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Page from '../../../components/Page';
|
||||
// - Local -
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { getClaimList } from './Model/Functions';
|
||||
import { ClaimListType, MemberDetailType } from './Model/Types';
|
||||
import ClaimList from './Components/ClaimList';
|
||||
|
||||
export default function Claim() {
|
||||
const navigate = useNavigate()
|
||||
const { member_id } = useParams();
|
||||
|
||||
// State
|
||||
// --------------------
|
||||
const [memberDetail, setMemberDetail] = useState<MemberDetailType>();
|
||||
const [claimList, setClaimList] = useState<ClaimListType[]>();
|
||||
|
||||
// Load Data
|
||||
// -------------------
|
||||
const loadDataTableData = async () => {
|
||||
const response = await getClaimList(member_id??'');
|
||||
|
||||
setMemberDetail(response.member_detail);
|
||||
setClaimList(response.claim_list);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Page title={ `claims | ${memberDetail?.name??'_ _ _'}` } sx={{ px: 2 }}>
|
||||
<Grid container gap={6}>
|
||||
{/* back button */}
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center'}}>
|
||||
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/daily_monitoring`)} >
|
||||
<ArrowBackIosNew/>
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
|
||||
{memberDetail?.name??'_ _ _'}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* tabel claims */}
|
||||
<Grid item xs={12}>
|
||||
<ClaimList claim_list={claimList??null} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { Box, Paper, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from "@mui/material";
|
||||
|
||||
/**
|
||||
* Component
|
||||
* ============================================
|
||||
*/
|
||||
import ClaimListRow from "./ClaimListRow";
|
||||
|
||||
/**
|
||||
* Types & Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { ClaimListType } from "../Model/Types";
|
||||
|
||||
type Props = {
|
||||
claim_list: ClaimListType[] | null,
|
||||
}
|
||||
|
||||
export default function ClaimList({ ...props }: Props) {
|
||||
// Tabel Style
|
||||
// --------------------
|
||||
const TableHeadStyle = {
|
||||
fontWeight: 'bold',
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 250 }} size='medium' aria-label="collapsible table">
|
||||
{/* Head Table */}
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={TableHeadStyle} align="left" width={50} />
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>Admission Date</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>Discharge Date</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={200}>Code</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={'*'}>Service Type</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={200}>Status</TableCell>
|
||||
<TableCell align="left" width={"10"} />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
{/* Body Table */}
|
||||
{props.claim_list == null ?
|
||||
(
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} align="center">Loading</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
)
|
||||
:
|
||||
(
|
||||
props.claim_list.length == 0 ?
|
||||
(
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} align="center">No Data</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
)
|
||||
:
|
||||
(
|
||||
<TableBody>
|
||||
{props.claim_list.map((row: ClaimListType, index) => (
|
||||
<ClaimListRow key={index} number={index+1} row={row} />
|
||||
))}
|
||||
</TableBody>
|
||||
)
|
||||
)}
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { Box, Collapse, MenuItem, TableCell, TableRow, Stack } from "@mui/material";
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
|
||||
/**
|
||||
* Component
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Label from "@/components/Label";
|
||||
import TableMoreMenu from '@/components/table/TableMoreMenu';
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { fDate } from "@/utils/formatTime";
|
||||
import { ClaimListType } from "../Model/Types";
|
||||
|
||||
type Props = {
|
||||
row: ClaimListType,
|
||||
number: number
|
||||
}
|
||||
|
||||
export default function ClaimListRow ({ ...props }: Props) {
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
||||
<TableRow hover sx={{ '& > td': { borderBottom: '1' } }}>
|
||||
<TableCell align="left" />
|
||||
<TableCell align="left">
|
||||
{props.row.admission_date == null?
|
||||
('-')
|
||||
:
|
||||
(
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{fDate(props.row.admission_date)}
|
||||
</Label>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{props.row.discharge_date == null ?
|
||||
('-')
|
||||
:
|
||||
(
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{fDate(props.row.discharge_date)}
|
||||
</Label>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="left">{props.row.code}</TableCell>
|
||||
<TableCell align="left">{props.row.service_name}</TableCell>
|
||||
<TableCell align="left">
|
||||
{props.row.discharge_date == null ?
|
||||
(
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="warning"
|
||||
>
|
||||
On Monitor
|
||||
</Label>
|
||||
) :
|
||||
(
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="success"
|
||||
>
|
||||
Close Monitor
|
||||
</Label>
|
||||
)
|
||||
}
|
||||
|
||||
</TableCell>
|
||||
<TableCell align="right" onClick={(e) => e.stopPropagation()}>
|
||||
<Stack direction="row" justifyContent="flex-end" spacing={1}>
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => navigate(`/case_management/daily_monitoring/${props.row.member_id}/claims/${props.row.claim_code}/list_monitoring`)}>
|
||||
<Visibility />
|
||||
View
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => navigate(`/case_management/daily_monitoring/${props.row.member_id}/claims/${props.row.claim_code}/add_monitoring`)}>
|
||||
<AddIcon />
|
||||
Daily Monitoring
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect, useState } from "react";
|
||||
import { Box, Paper, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from "@mui/material";
|
||||
|
||||
/**
|
||||
* Types & Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { getDailyMonitoringList } from "../Model/Functions";
|
||||
import { DailyMonitoringListType } from "../Model/Types";
|
||||
import DailyMonitoringListRow from "./DailyMonitoringListRow";
|
||||
|
||||
export default function DailyMonitoringList() {
|
||||
// State
|
||||
// --------------------
|
||||
const [dataTableIsLoading, setDataTableLoading] = useState<boolean>(true);
|
||||
const [dataTableData, setDataTableData] = useState<DailyMonitoringListType[]>([]);
|
||||
|
||||
// Tabel Style
|
||||
// --------------------
|
||||
const TableHeadStyle = {
|
||||
fontWeight: 'bold',
|
||||
};
|
||||
|
||||
// Load Data
|
||||
// -------------------
|
||||
const loadDataTableData = async () => {
|
||||
setDataTableLoading(true);
|
||||
|
||||
const response = await getDailyMonitoringList();
|
||||
|
||||
setDataTableLoading(false);
|
||||
setDataTableData(response);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 250 }} size='medium' aria-label="collapsible table">
|
||||
{/* Head Table */}
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={TableHeadStyle} align="left" width={50} />
|
||||
<TableCell style={TableHeadStyle} align="left" width={150}>Member ID</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={'*'}>Name</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>Start Date</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>End Date</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>Admission Date</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>Provider</TableCell>
|
||||
<TableCell align="left" width={"10"} />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
{/* Body Table */}
|
||||
{dataTableIsLoading ?
|
||||
(
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} align="center">Loading</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
)
|
||||
:
|
||||
(
|
||||
dataTableData.length == 0 ?
|
||||
(
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} align="center">No Data</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
)
|
||||
:
|
||||
(
|
||||
<TableBody>
|
||||
{dataTableData.map((row: DailyMonitoringListType, index) => (
|
||||
<DailyMonitoringListRow key={index} number={index+1} row={row} />
|
||||
))}
|
||||
</TableBody>
|
||||
)
|
||||
)}
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { Box, Collapse, MenuItem, TableCell, TableRow, Stack } from "@mui/material";
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
|
||||
/**
|
||||
* Component
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Label from "@/components/Label";
|
||||
import TableMoreMenu from '@/components/table/TableMoreMenu';
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { fDate } from "@/utils/formatTime";
|
||||
import { DailyMonitoringListType } from "../Model/Types";
|
||||
|
||||
type Props = {
|
||||
row: DailyMonitoringListType,
|
||||
number: number
|
||||
}
|
||||
|
||||
export default function DailyMonitoringListRow ({ ...props }: Props) {
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
||||
<TableRow hover sx={{ '& > td': { borderBottom: '1' } }}>
|
||||
<TableCell align="left" />
|
||||
<TableCell align="left">{props.row.member_id}</TableCell>
|
||||
<TableCell align="left">{props.row.name}</TableCell>
|
||||
<TableCell align="left">
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{fDate(props.row.startdate)}
|
||||
</Label>
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{fDate(props.row.enddate)}
|
||||
</Label>
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{fDate(props.row.addmision_date)}
|
||||
</Label>
|
||||
</TableCell>
|
||||
<TableCell align="left">{props.row.provider}</TableCell>
|
||||
<TableCell align="right" onClick={(e) => e.stopPropagation()}>
|
||||
<Stack direction="row" justifyContent="flex-end" spacing={1}>
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => navigate(`/case_management/daily_monitoring/${props.row.member_id}/claims`)}>
|
||||
<Visibility />
|
||||
View
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Box, IconButton, Typography, Grid, Card, Button } from '@mui/material';
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
import Page from '@/components/Page';
|
||||
import { FormProvider, RHFTextField } from '@/components/hook-form';
|
||||
|
||||
/**
|
||||
* Icon
|
||||
* ============================================
|
||||
*/
|
||||
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import RemoveIcon from '@mui/icons-material/Remove';
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { AddMonitoringDetail } from '../Model/Functions';
|
||||
import { DetailMonitoringListType} from '../Model/Types';
|
||||
|
||||
export default function DetailMonitoringList() {
|
||||
const { member_id, claim_code } = useParams();
|
||||
const navigate = useNavigate()
|
||||
const pageTitle = claim_code??'_ _ _ _';
|
||||
|
||||
// setup form
|
||||
// ====================================
|
||||
const defaultValues: DetailMonitoringListType = {
|
||||
id : '',
|
||||
claim_code : '',
|
||||
claim_id : '',
|
||||
subject : '',
|
||||
body_temperature: '',
|
||||
sistole : '',
|
||||
diastole : '',
|
||||
respiration_rate: '',
|
||||
complaints : '',
|
||||
analysis : '',
|
||||
medical_plan : [{
|
||||
medical_plan_str: ''
|
||||
}],
|
||||
created_at : ''
|
||||
};
|
||||
|
||||
const methods = useForm<any>({
|
||||
defaultValues
|
||||
});
|
||||
|
||||
const {fields, append, remove} = useFieldArray({name: 'medical_plan',control: methods.control})
|
||||
|
||||
const { handleSubmit, reset, formState: { isDirty, isSubmitting } } = methods;
|
||||
|
||||
// Submit Form
|
||||
// =====================================
|
||||
const submitHandler = async (data: DetailMonitoringListType) => {
|
||||
console.log(claim_code);
|
||||
|
||||
const response = await AddMonitoringDetail(claim_code??'', data);
|
||||
|
||||
if (response == true) {
|
||||
reset();
|
||||
navigate('case_management/daily_monitoring/'+claim_code+'claims');
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Page title={pageTitle}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', pl: '22px', mb: '40px' }}>
|
||||
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/daily_monitoring/${member_id}/claims`)} >
|
||||
<ArrowBackIosNew/>
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
|
||||
{pageTitle}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ px: '28px' }}>
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(submitHandler)}>
|
||||
<Card sx={{ padding: '24px' }}>
|
||||
<Grid container spacing={6}>
|
||||
{/* Subject */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" component="div">
|
||||
Subject* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFTextField
|
||||
id="subject"
|
||||
name='subject'
|
||||
placeholder='Subjective'
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Objectif */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" component="div">
|
||||
Objectif
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="body1" component="div" color={'GrayText'}>
|
||||
Body Temperature* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="body1" component="div" color={'GrayText'}>
|
||||
Sistole* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="body1" component="div" color={'GrayText'}>
|
||||
Diastole* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="body1" component="div" color={'GrayText'}>
|
||||
Respiration Rate* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={2}>
|
||||
<RHFTextField
|
||||
id="body_temperature"
|
||||
name='body_temperature'
|
||||
placeholder='Body Temperature'
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={1} sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Typography variant="body1" component="div" color={'GrayText'}>
|
||||
Cel
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<RHFTextField
|
||||
id="sistole"
|
||||
name='sistole'
|
||||
placeholder='Sistole'
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={1} sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Typography variant="body1" component="div" color={'GrayText'}>
|
||||
mm[Hg]
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<RHFTextField
|
||||
id="diastole"
|
||||
name='diastole'
|
||||
placeholder='Diastole'
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={1} sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Typography variant="body1" component="div" color={'GrayText'}>
|
||||
mm[Hg]
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<RHFTextField
|
||||
id="respiration_rate"
|
||||
name='respiration_rate'
|
||||
placeholder='Respiration Rate'
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={1} sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Typography variant="body1" component="div" color={'GrayText'}>
|
||||
/min
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Complaints */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" component="div">
|
||||
Complaints* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFTextField
|
||||
id="complaints"
|
||||
name='complaints'
|
||||
placeholder='Complaints'
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Analysis */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" component="div">
|
||||
Analysis* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFTextField
|
||||
id="analysis"
|
||||
name='analysis'
|
||||
placeholder='Analysis'
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Medical Plan */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" component="div">
|
||||
Medical Plan* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{
|
||||
fields.map((field,index) => {
|
||||
return (
|
||||
<Grid key={field.id} container sx={{ mb: 3 }}>
|
||||
<Grid item xs={11}>
|
||||
<RHFTextField
|
||||
id="analysis"
|
||||
name={`medical_plan.${index}.medical_plan_str`}
|
||||
placeholder='Medical Plan'
|
||||
/>
|
||||
</Grid>
|
||||
{
|
||||
index == (fields.length-1) ?
|
||||
(
|
||||
<Grid item xs={1} sx={{ textAlign: 'center' }}>
|
||||
<IconButton size='large' color='primary' onClick={() => append({medical_plan_str: ''})}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
)
|
||||
:
|
||||
(
|
||||
<Grid item xs={1} sx={{ textAlign: 'center' }}>
|
||||
<IconButton size='large' color='error' onClick={() => remove(index)}>
|
||||
<RemoveIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
</Grid>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Button Cancle & Save */}
|
||||
<Grid item xs={12} md={12}>
|
||||
<Box display="flex" justifyContent={'flex-end'}>
|
||||
<Box display="flex" gap={1}>
|
||||
<Button variant="outlined" color="inherit" onClick={() => navigate(`/case_management/daily_monitoring/${member_id}/claims`)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<LoadingButton disabled={!isDirty} type="submit" variant="contained" loading={isSubmitting}>
|
||||
Save Changes
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
</FormProvider>
|
||||
</Box>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Box, IconButton, Typography, Grid, Card, List, ListItem } from '@mui/material';
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Page from '@/components/Page';
|
||||
import Label from "@/components/Label";
|
||||
|
||||
/**
|
||||
* Icon
|
||||
* ============================================
|
||||
*/
|
||||
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
|
||||
import FiberManualRecord from '@mui/icons-material/FiberManualRecord';
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { fDate } from "@/utils/formatTime";
|
||||
import { getMonitoringDetailList } from '../Model/Functions';
|
||||
import { DetailMonitoringListType } from '../Model/Types';
|
||||
|
||||
|
||||
export default function DetailMonitoringList() {
|
||||
const { member_id, claim_code } = useParams();
|
||||
const navigate = useNavigate()
|
||||
const pageTitle = claim_code??'_ _ _ _';
|
||||
|
||||
// State
|
||||
// --------------------
|
||||
const [detailMonitoringList, setDetailMonitoringList] = useState<DetailMonitoringListType[]>();
|
||||
|
||||
// Use Effect
|
||||
// --------------------
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, [])
|
||||
|
||||
// Load Data
|
||||
// -------------------
|
||||
const loadDataTableData = async () => {
|
||||
const response = await getMonitoringDetailList(claim_code??'');
|
||||
|
||||
setDetailMonitoringList(response);
|
||||
}
|
||||
|
||||
return (
|
||||
<Page title={pageTitle} sx={{ px: 2 }}>
|
||||
<Grid container gap={6}>
|
||||
{/* back button */}
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/daily_monitoring/${member_id}/claims`)} >
|
||||
<ArrowBackIosNew/>
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
|
||||
{pageTitle}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* tabel claims */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container gap={4} sx={{ px: 2 }}>
|
||||
{
|
||||
detailMonitoringList?.map((row, index) => {
|
||||
return (
|
||||
<Grid key={index} item xs={12}>
|
||||
<Card sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '32px', py: '24px'}}>
|
||||
{/* card header */}
|
||||
<Box sx={{ pb: '20px', mb: '20px', borderBottom: '1px solid rgba(0,0,0,0.125)' }}>
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{row.created_at ? fDate(row.created_at) : '-'}
|
||||
</Label>
|
||||
</Box>
|
||||
|
||||
{/* card body */}
|
||||
<Grid container gap={4}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container gap={1}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
||||
Subject :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
{row.subject}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid container gap={1}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
||||
Object :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
Body Temperature
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2">
|
||||
{row.body_temperature}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
Sistole
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2">
|
||||
{row.sistole} mm[Hg]
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
Diastole
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2">
|
||||
{row.diastole} mm[Hg]
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
Respiration Rate
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2">
|
||||
{row.respiration_rate} / min
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid container gap={1}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
||||
Subject :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
{row.subject}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid container gap={1}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
||||
Analysis :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
{row.analysis}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid container gap={1}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
||||
Complaints :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
{row.complaints}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid container gap={1}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
||||
Medical Plan :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<List sx={{ color: 'GrayText' }}>
|
||||
{
|
||||
row.medical_plan.map((data, index) => {
|
||||
return (
|
||||
<ListItem key={index}>
|
||||
<FiberManualRecord sx={{ fontSize: '8px', mr: '10px' }} /> {data.medical_plan_str}
|
||||
</ListItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Grid>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import axios from '@/utils/axios';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { DailyMonitoringListType, DetailMonitoringListType, ResponseListingClaimType } from "./Types";
|
||||
|
||||
/**
|
||||
* Listing Daily Monitoring
|
||||
*/
|
||||
export const getDailyMonitoringList = async ( ): Promise<DailyMonitoringListType[]> => {
|
||||
const response = await axios.get('/case_management/memberlist')
|
||||
.then((res) =>{
|
||||
return res.data.data.member_list;
|
||||
})
|
||||
.catch((res) => {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* Listing Claim
|
||||
*/
|
||||
export const getClaimList = async ( member_id: string ): Promise<ResponseListingClaimType> => {
|
||||
const response = await axios.get(`/case_management/claimlist/${member_id}`)
|
||||
.then((res) =>{
|
||||
return res.data.data;
|
||||
})
|
||||
.catch((res) => {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add Monitoring Detail
|
||||
*/
|
||||
export const AddMonitoringDetail = async ( claim_code: string,data: DetailMonitoringListType ): Promise<boolean> => {
|
||||
const response = await axios.post(`/case_management/daily_monitoring/detail/${claim_code}/add`, {
|
||||
...data
|
||||
})
|
||||
.then((res) =>{
|
||||
enqueueSnackbar(res.data.message, {
|
||||
variant: 'success',
|
||||
});
|
||||
|
||||
return true;
|
||||
})
|
||||
.catch((res) => {
|
||||
if (res.response.status == 400) {
|
||||
let arr_message = res.response.data.message;
|
||||
|
||||
for (const key in arr_message) {
|
||||
enqueueSnackbar(arr_message[key][0], {
|
||||
variant: 'warning',
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Monitoring Detail List
|
||||
*/
|
||||
export const getMonitoringDetailList = async ( claim_code: string ): Promise<DetailMonitoringListType[]> => {
|
||||
const response = await axios.get(`/case_management/daily_monitoring/detail/${claim_code}/list`)
|
||||
.then((res) =>{
|
||||
return res.data.data.detail_list;
|
||||
})
|
||||
.catch((res) => {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* List Daily Monitoring
|
||||
*/
|
||||
export type DailyMonitoringListType = {
|
||||
member_id : string,
|
||||
name : string,
|
||||
startdate : string,
|
||||
enddate : string,
|
||||
addmision_date : string,
|
||||
provider : string
|
||||
}
|
||||
|
||||
/**
|
||||
* Response Listing Claim
|
||||
*/
|
||||
export type ResponseListingClaimType = {
|
||||
member_detail : MemberDetailType,
|
||||
claim_list : ClaimListType[],
|
||||
}
|
||||
|
||||
/**
|
||||
* Member Detail
|
||||
*/
|
||||
export type MemberDetailType = {
|
||||
id : string,
|
||||
member_id : string,
|
||||
name : string,
|
||||
}
|
||||
|
||||
/**
|
||||
* List Claim
|
||||
*/
|
||||
export type ClaimListType = {
|
||||
claim_id : number,
|
||||
admission_date : string,
|
||||
discharge_date : string,
|
||||
claim_code : string,
|
||||
claim_status : string,
|
||||
service_type : string,
|
||||
member_id : string
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail Monitoring List
|
||||
*/
|
||||
export type DetailMonitoringListType = {
|
||||
id : string|null,
|
||||
claim_id : string|null,
|
||||
claim_code : string,
|
||||
subject : string,
|
||||
body_temperature: string,
|
||||
respiration_rate: string,
|
||||
sistole : string,
|
||||
diastole : string
|
||||
analysis : string,
|
||||
complaints : string,
|
||||
medical_plan : MedicalPlanStrType[],
|
||||
created_at : string|null
|
||||
}
|
||||
|
||||
export type MedicalPlanStrType = {
|
||||
medical_plan_str: string
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { Box, Grid } from '@mui/material';
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Page from '../../../components/Page';
|
||||
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
|
||||
// - Local -
|
||||
import DailyMonitoringList from './Components/DailyMonitoringList';
|
||||
|
||||
export default function DailyMonitoring() {
|
||||
const pageTitle = "Daily Monitoring";
|
||||
|
||||
return (
|
||||
<Page title={ pageTitle } sx={{ px: 2 }}>
|
||||
<Grid container>
|
||||
{/* page header */}
|
||||
<Grid item xs={12}>
|
||||
<HeaderBreadcrumbs
|
||||
heading={ pageTitle }
|
||||
sx={{ px: 1 }}
|
||||
links={[
|
||||
{
|
||||
name: 'Dashboard',
|
||||
href: '/dashboard',
|
||||
},
|
||||
{
|
||||
name: pageTitle,
|
||||
href: '/case_management/daily_monitoring',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* tabel daily monitoring */}
|
||||
<Grid item xs={12}>
|
||||
<DailyMonitoringList />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Card, Stack } from "@mui/material";
|
||||
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
|
||||
import Page from "../../../components/Page";
|
||||
import List from "./List";
|
||||
|
||||
|
||||
|
||||
export default function Claims() {
|
||||
|
||||
const pageTitle = 'Inpatient Monitoring';
|
||||
return (
|
||||
<Page title={ pageTitle } sx={{ mx: 2}}>
|
||||
|
||||
<HeaderBreadcrumbs
|
||||
heading={ pageTitle }
|
||||
links={[
|
||||
{ name: 'Dashboard', href: '/dashboard' },
|
||||
{
|
||||
name: 'Inpatient Monitoring',
|
||||
href: '/inpatient_monitoring',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* <Stack> */}
|
||||
<List />
|
||||
{/* </Stack> */}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,582 @@
|
||||
// @mui
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Collapse,
|
||||
IconButton,
|
||||
MenuItem,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
TextField,
|
||||
Typography,
|
||||
Stack,
|
||||
Menu,
|
||||
ButtonGroup,
|
||||
Link,
|
||||
Chip,
|
||||
TableHead,
|
||||
Grid,
|
||||
} from '@mui/material';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import UploadIcon from '@mui/icons-material/Upload';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
|
||||
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
|
||||
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
||||
// hooks
|
||||
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import useSettings from '@/hooks/useSettings';
|
||||
// components
|
||||
import axios from '../../../utils/axios';
|
||||
import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../../@types/paginated-data';
|
||||
import DataTable from '../../../components/LaravelTable';
|
||||
import { fCurrency } from '../../../utils/formatNumber';
|
||||
import EditRoundedIcon from '@mui/icons-material/EditRounded';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { Divider } from '@mui/material';
|
||||
import Iconify from '@/components/Iconify';
|
||||
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
|
||||
import { fDateTimesecond } from '@/utils/formatTime';
|
||||
import { capitalizeFirstLetter } from '@/utils/formatString';
|
||||
import Label from '@/components/Label';
|
||||
import TableMoreMenu from '@/components/table/TableMoreMenu';
|
||||
import { Import } from '@/@types/claims';
|
||||
|
||||
import { FinalLogType } from '../../CustomerService/FinalLog/Model/Types';
|
||||
// import LoadingButton from '@/theme/overrides/LoadingButton';
|
||||
|
||||
export default function List() {
|
||||
const { themeColorPresets } = useSettings();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [importResult, setImportResult] = useState<Import>(null);
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
function SearchInput(props: any) {
|
||||
// SEARCH
|
||||
const searchInput = useRef<HTMLInputElement>(null);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
const handleSearchChange = (event: any) => {
|
||||
const newSearchText = event.target.value ?? '';
|
||||
setSearchText(newSearchText);
|
||||
};
|
||||
|
||||
const handleSearchSubmit = (event: any) => {
|
||||
event.preventDefault();
|
||||
props.onSearch({ search: searchText }); // Trigger to Parent
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Trigger First Search
|
||||
setSearchText(searchParams.get('search') ?? '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSearchSubmit} style={{ width: '100%' }}>
|
||||
<TextField
|
||||
id="search-input"
|
||||
ref={searchInput}
|
||||
label="Search"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
onChange={handleSearchChange}
|
||||
value={searchText}
|
||||
placeholder='Search Code or Name...'
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function ImportForm(props: any) {
|
||||
// IMPORT
|
||||
// Create Button Menu
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const createMenu = Boolean(anchorEl);
|
||||
const importForm = useRef<HTMLInputElement>(null);
|
||||
const [currentImportFileName, setCurrentImportFileName] = useState(null);
|
||||
const [importLoading, setImportLoading] = useState(false);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleImportButton = () => {
|
||||
if (importForm?.current) {
|
||||
handleClose();
|
||||
importForm.current ? importForm.current.click() : console.log('No File selected');
|
||||
} else {
|
||||
alert('No file selected');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelImportButton = () => {
|
||||
importForm.current.value = '';
|
||||
importForm.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 (importForm.current?.files.length) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', importForm.current?.files[0]);
|
||||
|
||||
setImportLoading(true);
|
||||
axios
|
||||
.post(`claim-requests/import`, formData)
|
||||
.then((response) => {
|
||||
handleCancelImportButton();
|
||||
loadDataTableData();
|
||||
setImportResult(response.data);
|
||||
// alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows');
|
||||
setImportLoading(false);
|
||||
})
|
||||
.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 = (type :string) => {
|
||||
axios.get('corporates/import-document-example/' + type)
|
||||
.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 handleGetData = (type :string) => {
|
||||
axios.get(`corporates/${corporate_id}/data-plan-benefit`)
|
||||
.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();
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={importForm}
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleImportChange}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
|
||||
/>
|
||||
{!currentImportFileName && (
|
||||
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
|
||||
<SearchInput onSearch={applyFilter} />
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<UploadIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={handleClick}
|
||||
>
|
||||
Import
|
||||
</Button>
|
||||
<Menu
|
||||
id="import-button"
|
||||
anchorEl={anchorEl}
|
||||
open={createMenu}
|
||||
onClose={handleClose}
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'basic-button',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleImportButton}>Import</MenuItem>
|
||||
<MenuItem onClick={() => {handleGetTemplate('claim-request')}}>Download Template</MenuItem>
|
||||
<MenuItem onClick={() => {handleGetData('data-plan-benefit')}}>Download Claim Request</MenuItem>
|
||||
</Menu>
|
||||
{/* <Button
|
||||
variant="contained"
|
||||
startIcon={<AddIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={() => {
|
||||
navigate('/claim-requests/create');
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button> */}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{currentImportFileName && (
|
||||
<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>
|
||||
)}
|
||||
{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>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Dummy Default Data
|
||||
const [dataTableIsLoading, setDataTableLoading] = useState(true);
|
||||
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>(
|
||||
LaravelPaginatedDataDefault
|
||||
);
|
||||
|
||||
const loadDataTableData = async (appliedFilter: any | null = null) => {
|
||||
setDataTableLoading(true);
|
||||
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
|
||||
const response = await axios.get('/customer-service/request?final_log=1&service_code=IP', { 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);
|
||||
};
|
||||
|
||||
const handleApprove = (claimRequest) => {
|
||||
axios
|
||||
.post(`claim-requests/${claimRequest.id}/approve`)
|
||||
.then((response) => {
|
||||
enqueueSnackbar('Success Approve', { variant: 'success' });
|
||||
loadDataTableData();
|
||||
})
|
||||
.catch(({ response }) => {
|
||||
enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' });
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, []);
|
||||
|
||||
const headStyle = {
|
||||
fontWeight: 'bold',
|
||||
};
|
||||
|
||||
// Called on every row to map the data to the columns
|
||||
function createData(data: FinalLogType) {
|
||||
return {
|
||||
...data,
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
/* ------------------ TABLE ROW ------------------ */
|
||||
}
|
||||
function Row(props: { row: ReturnType<typeof createData> }) {
|
||||
const { row } = props;
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [loadingApprove, setLoadingApprove] = React.useState(false);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
|
||||
{/* <TableCell>
|
||||
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
|
||||
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
|
||||
</IconButton>
|
||||
</TableCell> */ }
|
||||
{/* <TableCell align="left">
|
||||
<Typography
|
||||
// onClick={() => {
|
||||
// handleShowClaim(row);
|
||||
// }}
|
||||
>
|
||||
{row.id}
|
||||
</Typography>
|
||||
</TableCell> */}
|
||||
<TableCell align="left">{row.code}</TableCell>
|
||||
<TableCell align="left">{row.member_name}</TableCell>
|
||||
<TableCell align="left"><Label>{fDateTimesecond(row.submission_date)}</Label></TableCell>
|
||||
<TableCell align="left">{row.service_name}</TableCell>
|
||||
<TableCell align="left">{row.payment_type_name}</TableCell>
|
||||
<TableCell align="left">
|
||||
{ row.status_final_log == "requested" ?
|
||||
(<Label variant='ghost' color='primary'>{capitalizeFirstLetter(row.status_final_log)}</Label>) :
|
||||
row.status_final_log == "declined" ?
|
||||
(<Label color='error'> {capitalizeFirstLetter(row.status_final_log)}</Label>)
|
||||
:
|
||||
(<Label color='success'> {capitalizeFirstLetter(row.status_final_log)}</Label>)
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
{/* <MenuItem onClick={() => navigate(`/claim-requests/edit/${row.id}`)}>
|
||||
<EditOutlinedIcon />
|
||||
Edit
|
||||
</MenuItem> */}
|
||||
<MenuItem onClick={() => navigate ('/custormer-service/final-log/detail/'+row.id+'')}>
|
||||
<FindInPageOutlinedIcon />
|
||||
Detail
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</TableCell>
|
||||
{/* <TableCell>
|
||||
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
handleShowClaim(row);
|
||||
}}
|
||||
>
|
||||
<Iconify icon="eva:eye-fill" />
|
||||
</IconButton>
|
||||
</TableCell> */}
|
||||
</TableRow>
|
||||
{/* COLLAPSIBLE ROW */}
|
||||
<TableRow>
|
||||
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={99}>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<Box sx={{ borderBottom: 1 }}>
|
||||
<Stack
|
||||
divider={<Divider orientation="horizontal" flexItem />}
|
||||
spacing={1}
|
||||
sx={{ marginY: 2 }}
|
||||
>
|
||||
<Box>
|
||||
<Typography fontWeight={600}>Berkas Hasil Penunjang</Typography>
|
||||
{/* {row.files_by_type?.claim_kondisi &&
|
||||
row.files_by_type?.claim_kondisi.map((file, index) => (
|
||||
<Stack direction="row" key={index}>
|
||||
<Typography sx={{ marginRight: 2 }}>-</Typography>{' '}
|
||||
<a href={file.url} target="_blank">
|
||||
{file.name}
|
||||
</a>
|
||||
</Stack>
|
||||
))} */}
|
||||
|
||||
{row.files_by_type?.claim_kondisi && (
|
||||
<>
|
||||
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Kondisi</Typography>
|
||||
{row.files_by_type?.claim_kondisi.map((file, index) => (
|
||||
|
||||
<Stack direction="row" key={index}>
|
||||
<a href={file.url} target="_blank">
|
||||
{file.name}
|
||||
</a>
|
||||
</Stack>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{row.files_by_type?.claim_diagnosis && (
|
||||
<>
|
||||
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Diagnosa</Typography>
|
||||
{row.files_by_type?.claim_diagnosis.map((file, index) => (
|
||||
|
||||
<Stack direction="row" key={index}>
|
||||
<a href={file.url} target="_blank">
|
||||
{file.name}
|
||||
</a>
|
||||
</Stack>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{row.files_by_type?.claim_result && (
|
||||
<>
|
||||
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Hasil</Typography>
|
||||
{row.files_by_type?.claim_result.map((file, index) => (
|
||||
|
||||
<Stack direction="row" key={index}>
|
||||
<a href={file.url} target="_blank">
|
||||
{file.name}
|
||||
</a>
|
||||
</Stack>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{(!row.files_by_type?.claim_result && !row.files_by_type?.claim_diagnosis && !row.files_by_type?.claim_kondisi)&& <Typography>Tidak ada berkas</Typography>}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
{
|
||||
/* ------------------ 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">
|
||||
ID Request LOG
|
||||
</TableCell> */}
|
||||
<TableCell style={headStyle} align="left">
|
||||
Code
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Name
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Date of Submission
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Service Type
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Claim Method
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Status
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="right"></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
{/* ------------------ END TABLE HEADER ------------------ */}
|
||||
|
||||
{/* ------------------ TABLE ROW ------------------ */}
|
||||
{dataTableIsLoading ? (
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} align="center">
|
||||
Loading
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
) : dataTableData.data.length === 0 ? (
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} align="center">
|
||||
No Data
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
) : (
|
||||
<TableBody>
|
||||
{dataTableData.data.map((row) => (
|
||||
<Row key={row.id} row={row} />
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
{/* ------------------ END TABLE ROW ------------------ */}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Dialog Detail Claim Request
|
||||
const [openDialogDetailClaim, setOpenDialogDetailClaim] = useState(false);
|
||||
const [loadingClaimDetail, setLoadingClaimDetail] = useState(true);
|
||||
const [currentClaim, setCurrentClaim] = useState(null);
|
||||
|
||||
function handleShowClaim(claimRequest) {
|
||||
setLoadingClaimDetail(true);
|
||||
setOpenDialogDetailClaim(true);
|
||||
|
||||
axios
|
||||
.get(`/claim-requests/${claimRequest.id}`)
|
||||
.then(({ data }) => {
|
||||
setCurrentClaim(data.data);
|
||||
setLoadingClaimDetail(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
enqueueSnackbar(err.message, { variant: 'error' });
|
||||
});
|
||||
}
|
||||
|
||||
function handleDownloadLog() {}
|
||||
|
||||
return (
|
||||
<Grid container>
|
||||
<Grid item sm={12}>
|
||||
<ImportForm />
|
||||
</Grid>
|
||||
|
||||
<Grid item sm={12}>
|
||||
<DataTable
|
||||
isLoading={dataTableIsLoading}
|
||||
lastRequest={0}
|
||||
data={dataTableData}
|
||||
handlePageChange={handlePageChange}
|
||||
TableContent={<TableContent />}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item sm={12}>
|
||||
<DialogDetailClaim
|
||||
openDialog={openDialogDetailClaim}
|
||||
setOpenDialog={setOpenDialogDetailClaim}
|
||||
title={{ name: 'Claim Request Detail' }}
|
||||
data={{ claim: currentClaim, isLoading: loadingClaimDetail, handleDownloadLog }}
|
||||
></DialogDetailClaim>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Box, Grid, IconButton, Typography } from '@mui/material';
|
||||
import { ArrowBackIosNew } from '@mui/icons-material';
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Page from '../../../components/Page';
|
||||
// - Local -
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { getClaimList } from './Model/Functions';
|
||||
import { ClaimListType, MemberDetailType } from './Model/Types';
|
||||
import ClaimList from './Components/ClaimList';
|
||||
|
||||
export default function Claim() {
|
||||
const navigate = useNavigate()
|
||||
const { member_id } = useParams();
|
||||
|
||||
// State
|
||||
// --------------------
|
||||
const [memberDetail, setMemberDetail] = useState<MemberDetailType>();
|
||||
const [claimList, setClaimList] = useState<ClaimListType[]>();
|
||||
|
||||
// Use Effect
|
||||
// --------------------
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, [])
|
||||
|
||||
// Load Data
|
||||
// -------------------
|
||||
const loadDataTableData = async () => {
|
||||
const response = await getClaimList(member_id??'');
|
||||
|
||||
setMemberDetail(response.member_detail);
|
||||
setClaimList(response.claim_list);
|
||||
}
|
||||
|
||||
return (
|
||||
<Page title={ `claims | ${memberDetail?.name??'_ _ _'}` } sx={{ px: 2 }}>
|
||||
<Grid container gap={6}>
|
||||
{/* back button */}
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center'}}>
|
||||
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/laboratorium_result`)} >
|
||||
<ArrowBackIosNew/>
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
|
||||
{memberDetail?.name??'_ _ _'}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* tabel claims */}
|
||||
<Grid item xs={12}>
|
||||
<ClaimList claim_list={claimList??null} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { Box, Paper, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from "@mui/material";
|
||||
|
||||
/**
|
||||
* Component
|
||||
* ============================================
|
||||
*/
|
||||
import ClaimListRow from "./ClaimListRow";
|
||||
|
||||
/**
|
||||
* Types & Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { ClaimListType } from "../Model/Types";
|
||||
|
||||
type Props = {
|
||||
claim_list: ClaimListType[] | null,
|
||||
}
|
||||
|
||||
export default function ClaimList({ ...props }: Props) {
|
||||
// Tabel Style
|
||||
// --------------------
|
||||
const TableHeadStyle = {
|
||||
fontWeight: 'bold',
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 250 }} size='medium' aria-label="collapsible table">
|
||||
{/* Head Table */}
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={TableHeadStyle} align="left" width={50} />
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>Admission Date</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>Discharge Date</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={200}>Code</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={'*'}>Service Type</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={200}>Status</TableCell>
|
||||
<TableCell align="left" width={"10"} />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
{/* Body Table */}
|
||||
{props.claim_list == null ?
|
||||
(
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} align="center">Loading</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
)
|
||||
:
|
||||
(
|
||||
props.claim_list.length == 0 ?
|
||||
(
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} align="center">No Data</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
)
|
||||
:
|
||||
(
|
||||
<TableBody>
|
||||
{props.claim_list.map((row: ClaimListType, index) => (
|
||||
<ClaimListRow key={index} number={index+1} row={row} />
|
||||
))}
|
||||
</TableBody>
|
||||
)
|
||||
)}
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { Box, Collapse, MenuItem, TableCell, TableRow, Stack } from "@mui/material";
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
|
||||
/**
|
||||
* Component
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Label from "@/components/Label";
|
||||
import TableMoreMenu from '@/components/table/TableMoreMenu';
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { fDate } from "@/utils/formatTime";
|
||||
import { ClaimListType } from "../Model/Types";
|
||||
|
||||
type Props = {
|
||||
row: ClaimListType,
|
||||
number: number
|
||||
}
|
||||
|
||||
export default function ClaimListRow ({ ...props }: Props) {
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
||||
<TableRow hover sx={{ '& > td': { borderBottom: '1' } }}>
|
||||
<TableCell align="left" />
|
||||
<TableCell align="left">
|
||||
{props.row.admission_date == "0000-00-00 00:00:00" ?
|
||||
('-')
|
||||
:
|
||||
(
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{fDate(props.row.admission_date)}
|
||||
</Label>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{props.row.discharge_date == "0000-00-00 00:00:00" ?
|
||||
('-')
|
||||
:
|
||||
(
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{fDate(props.row.discharge_date)}
|
||||
</Label>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="left">{props.row.claim_code}</TableCell>
|
||||
<TableCell align="left">{props.row.service_type}</TableCell>
|
||||
<TableCell align="left">{props.row.claim_status}</TableCell>
|
||||
<TableCell align="right" onClick={(e) => e.stopPropagation()}>
|
||||
<Stack direction="row" justifyContent="flex-end" spacing={1}>
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => navigate(`/case_management/laboratorium_result/${props.row.member_id}/claims/${props.row.claim_code}/list_lab_result`)}>
|
||||
<Visibility />
|
||||
View
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => navigate(`/case_management/laboratorium_result/${props.row.member_id}/claims/${props.row.claim_code}/add_lab_result`)}>
|
||||
<AddIcon />
|
||||
Laboratorium Result
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useRef } from 'react';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Box, IconButton, Typography, Grid, Card, Button, ButtonBase, Stack } from '@mui/material';
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
import Page from '@/components/Page';
|
||||
import { FormProvider, RHFDatepicker, RHFTextField } from '@/components/hook-form';
|
||||
import RHFDatePickerV2 from '@/components/hook-form/RHFDatePickerV2';
|
||||
|
||||
/**
|
||||
* Icon
|
||||
* ============================================
|
||||
*/
|
||||
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
|
||||
import Iconify from '@/components/Iconify';
|
||||
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { AddLabResultDetail } from '../Model/Functions';
|
||||
import { DetailLabResultListType} from '../Model/Types';
|
||||
|
||||
export default function DetailMonitoringList() {
|
||||
const { member_id, claim_code } = useParams();
|
||||
const navigate = useNavigate()
|
||||
const pageTitle = claim_code??'_ _ _ _';
|
||||
const fileInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
// setup form
|
||||
// ====================================
|
||||
const defaultValues: DetailLabResultListType = {
|
||||
id : '',
|
||||
claim_id : '',
|
||||
claim_code : '',
|
||||
date : null,
|
||||
location : '',
|
||||
examination : '',
|
||||
lab_result_file : [],
|
||||
created_at : ''
|
||||
};
|
||||
|
||||
const methods = useForm<any>({
|
||||
defaultValues
|
||||
});
|
||||
|
||||
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting } } = methods;
|
||||
const formValues = watch();
|
||||
|
||||
// Handle File Input
|
||||
// =====================================
|
||||
const handleInputChange = (event: any) => {
|
||||
if (event.target.files[0]) {
|
||||
|
||||
let arr_lab_result_file = formValues.lab_result_file;
|
||||
arr_lab_result_file.push(event.target.files[0]);
|
||||
|
||||
setValue('lab_result_file', arr_lab_result_file)
|
||||
}
|
||||
else {
|
||||
console.log('NO FILE');
|
||||
}
|
||||
};
|
||||
|
||||
// Handle Remove File
|
||||
// =====================================
|
||||
const handleRemoveFile = (target_index: number) => {
|
||||
let arr_lab_result_file = formValues.lab_result_file.filter((file: any, index: number) =>{
|
||||
if (target_index !== index) {
|
||||
return file;
|
||||
}
|
||||
});
|
||||
|
||||
setValue('lab_result_file', arr_lab_result_file)
|
||||
};
|
||||
|
||||
// Submit Form
|
||||
// =====================================
|
||||
const submitHandler = async (data: DetailLabResultListType) => {
|
||||
const response = await AddLabResultDetail(claim_code??'', data);
|
||||
|
||||
if (response == true) {
|
||||
reset(defaultValues);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Page title={pageTitle}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', pl: '22px', mb: '40px' }}>
|
||||
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/laboratorium_result/${member_id}/claims`)} >
|
||||
<ArrowBackIosNew/>
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
|
||||
{pageTitle}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ px: '28px' }}>
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(submitHandler)}>
|
||||
<Card sx={{ padding: '24px' }}>
|
||||
<Grid container spacing={6}>
|
||||
{/* Date */}
|
||||
<Grid item xs={6}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" component="div">
|
||||
Date* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFDatePickerV2
|
||||
label=''
|
||||
name="date"
|
||||
dateFormat='dd-MMMM-yyyy'
|
||||
fullWidth
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Location */}
|
||||
<Grid item xs={6}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" component="div">
|
||||
Location* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFTextField
|
||||
id="location"
|
||||
name='location'
|
||||
placeholder='Location'
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Examination */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" component="div">
|
||||
Examination* :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFTextField
|
||||
id="examination"
|
||||
name='examination'
|
||||
placeholder='Examination'
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* list file & button upload */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
{
|
||||
formValues.lab_result_file.map((file: any, index: number) => (
|
||||
<Stack direction="row" justifyContent={'space-between'} key={index} sx={{ mb: '16px' }}>
|
||||
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
|
||||
<InsertDriveFileIcon />
|
||||
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
|
||||
</Stack>
|
||||
<Iconify
|
||||
icon="eva:trash-2-outline"
|
||||
color={'darkred'}
|
||||
onClick={() => handleRemoveFile(index)}
|
||||
sx={{cursor: 'pointer'}}
|
||||
></Iconify>
|
||||
</Stack>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',bgcolor: '#919EAB52',borderRadius: '8px',width: '100%', height: '60px'}} onClick={() => fileInput.current?.click()}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
placeItems: 'center',
|
||||
gap: 1,
|
||||
placeContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
|
||||
<Typography variant="body1" fontWeight="bold">
|
||||
Upload Result
|
||||
</Typography>
|
||||
</Box>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={fileInput}
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={handleInputChange}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Button Cancle & Save */}
|
||||
<Grid item xs={12} md={12}>
|
||||
<Box display="flex" justifyContent={'flex-end'}>
|
||||
<Box display="flex" gap={1}>
|
||||
<Button variant="outlined" color="inherit" onClick={() => navigate(`/case_management/laboratorium_result/${member_id}/claims`)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<LoadingButton disabled={!isDirty} type="submit" variant="contained" loading={isSubmitting}>
|
||||
Save Changes
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
</FormProvider>
|
||||
</Box>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Box, IconButton, Typography, Grid, Card, List, ListItem, Stack, Link } from '@mui/material';
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Page from '@/components/Page';
|
||||
import Label from "@/components/Label";
|
||||
|
||||
/**
|
||||
* Icon
|
||||
* ============================================
|
||||
*/
|
||||
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
|
||||
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { fDate } from "@/utils/formatTime";
|
||||
import { getLabResultDetailList } from '../Model/Functions';
|
||||
import { DetailLabResultListType } from '../Model/Types';
|
||||
|
||||
|
||||
export default function DetailLabResultList() {
|
||||
const { member_id, claim_code } = useParams();
|
||||
const navigate = useNavigate()
|
||||
const pageTitle = claim_code??'_ _ _ _';
|
||||
|
||||
// State
|
||||
// --------------------
|
||||
const [LabResultList, setLabResultList] = useState<DetailLabResultListType[]>();
|
||||
|
||||
// Use Effect
|
||||
// --------------------
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, [])
|
||||
|
||||
// Load Data
|
||||
// -------------------
|
||||
const loadDataTableData = async () => {
|
||||
const response = await getLabResultDetailList(claim_code??'');
|
||||
|
||||
setLabResultList(response);
|
||||
}
|
||||
|
||||
return (
|
||||
<Page title={pageTitle} sx={{ px: 2 }}>
|
||||
<Grid container gap={6}>
|
||||
{/* back button */}
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/laboratorium_result/${member_id}/claims`)} >
|
||||
<ArrowBackIosNew/>
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
|
||||
{pageTitle}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* tabel claims */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container gap={4} sx={{ px: 2 }}>
|
||||
{
|
||||
LabResultList?.map((row, index) => {
|
||||
return (
|
||||
<Grid key={index} item xs={12}>
|
||||
<Card sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '32px', py: '24px'}}>
|
||||
{/* card header */}
|
||||
<Box sx={{ pb: '20px', mb: '20px', borderBottom: '1px solid rgba(0,0,0,0.125)' }}>
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{row.created_at ? fDate(row.created_at) : '-'}
|
||||
</Label>
|
||||
</Box>
|
||||
|
||||
{/* card body */}
|
||||
<Grid container gap={3}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
Date
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2">
|
||||
{row.date}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
Location
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2">
|
||||
{row.location}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" color={"GrayText"}>
|
||||
Examination
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2">
|
||||
{row.examination}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid container gap={1}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
||||
Document :
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{
|
||||
row.lab_result_file.map((data, index) => {
|
||||
return (
|
||||
<Stack direction="row" justifyContent={'space-between'} key={index} sx={{ mt: '16px' }}>
|
||||
<Link href={data.lab_result_file_obj.path} underline='hover' target="_blank">
|
||||
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
|
||||
<InsertDriveFileIcon />
|
||||
<Typography variant="body2" gutterBottom>{data.lab_result_file_obj.original_name??'-'}</Typography>
|
||||
</Stack>
|
||||
</Link>
|
||||
</Stack>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Grid>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect, useState } from "react";
|
||||
import { Box, Paper, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from "@mui/material";
|
||||
|
||||
/**
|
||||
* Types & Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { getDailyMonitoringList } from "../Model/Functions";
|
||||
import { LaboratoriumResultListType } from "../Model/Types";
|
||||
import LaboratoriumListRow from "./LaboratoriumResultListRow";
|
||||
|
||||
export default function LaboratoriumResultList() {
|
||||
// State
|
||||
// --------------------
|
||||
const [dataTableIsLoading, setDataTableLoading] = useState<boolean>(true);
|
||||
const [dataTableData, setDataTableData] = useState<LaboratoriumResultListType[]>([]);
|
||||
|
||||
// Tabel Style
|
||||
// --------------------
|
||||
const TableHeadStyle = {
|
||||
fontWeight: 'bold',
|
||||
};
|
||||
|
||||
// Use Effect
|
||||
// --------------------
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, [])
|
||||
|
||||
// Load Data
|
||||
// -------------------
|
||||
const loadDataTableData = async () => {
|
||||
setDataTableLoading(true);
|
||||
|
||||
const response = await getDailyMonitoringList();
|
||||
|
||||
setDataTableLoading(false);
|
||||
setDataTableData(response);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 250 }} size='medium' aria-label="collapsible table">
|
||||
{/* Head Table */}
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={TableHeadStyle} align="left" width={50} />
|
||||
<TableCell style={TableHeadStyle} align="left" width={150}>Member ID</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={'*'}>Name</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>Start Date</TableCell>
|
||||
<TableCell style={TableHeadStyle} align="left" width={160}>End Date</TableCell>
|
||||
<TableCell align="left" width={"10"} />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
{/* Body Table */}
|
||||
{dataTableIsLoading ?
|
||||
(
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} align="center">Loading</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
)
|
||||
:
|
||||
(
|
||||
dataTableData.length == 0 ?
|
||||
(
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} align="center">No Data</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
)
|
||||
:
|
||||
(
|
||||
<TableBody>
|
||||
{dataTableData.map((row: LaboratoriumResultListType, index) => (
|
||||
<LaboratoriumListRow key={index} number={index+1} row={row} />
|
||||
))}
|
||||
</TableBody>
|
||||
)
|
||||
)}
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { Box, Collapse, MenuItem, TableCell, TableRow, Stack } from "@mui/material";
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
|
||||
/**
|
||||
* Component
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Label from "@/components/Label";
|
||||
import TableMoreMenu from '@/components/table/TableMoreMenu';
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { fDate } from "@/utils/formatTime";
|
||||
import { LaboratoriumResultListType } from "../Model/Types";
|
||||
|
||||
type Props = {
|
||||
row: LaboratoriumResultListType,
|
||||
number: number
|
||||
}
|
||||
|
||||
export default function LaboratoriumResultListRow ({ ...props }: Props) {
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
||||
<TableRow hover sx={{ '& > td': { borderBottom: '1' } }}>
|
||||
<TableCell align="left" />
|
||||
<TableCell align="left">{props.row.member_id}</TableCell>
|
||||
<TableCell align="left">{props.row.name}</TableCell>
|
||||
<TableCell align="left">
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{fDate(props.row.startdate)}
|
||||
</Label>
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<Label
|
||||
variant="ghost"
|
||||
color="default"
|
||||
>
|
||||
{fDate(props.row.enddate)}
|
||||
</Label>
|
||||
</TableCell>
|
||||
<TableCell align="right" onClick={(e) => e.stopPropagation()}>
|
||||
<Stack direction="row" justifyContent="flex-end" spacing={1}>
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => navigate(`/case_management/laboratorium_result/${props.row.member_id}/claims`)}>
|
||||
<Visibility />
|
||||
View
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import axios from '@/utils/axios';
|
||||
import { makeFormData } from '@/utils/jsonToFormData';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { DetailLabResultListType, LaboratoriumResultListType, ResponseListingClaimType } from "./Types";
|
||||
import { fDate } from '@/utils/formatTime';
|
||||
|
||||
/**
|
||||
* Listing Daily Monitoring
|
||||
*/
|
||||
export const getDailyMonitoringList = async ( ): Promise<LaboratoriumResultListType[]> => {
|
||||
const response = await axios.get('/case_management/memberlist')
|
||||
.then((res) =>{
|
||||
return res.data.data.member_list;
|
||||
})
|
||||
.catch((res) => {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* Listing Claim
|
||||
*/
|
||||
export const getClaimList = async ( member_id: string ): Promise<ResponseListingClaimType> => {
|
||||
const response = await axios.get(`/case_management/claimlist/${member_id}`)
|
||||
.then((res) =>{
|
||||
return res.data.data;
|
||||
})
|
||||
.catch((res) => {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add Lab Result Detail
|
||||
*/
|
||||
export const AddLabResultDetail = async ( claim_code: string,data: DetailLabResultListType ): Promise<boolean> => {
|
||||
data.date = data.date != '' && data.date != null ? fDate(data.date) : '';
|
||||
|
||||
const formData = makeFormData({...data});
|
||||
|
||||
const response = await axios.post(`/case_management/laboratorium_result/detail/${claim_code}/add`, formData)
|
||||
.then((res) =>{
|
||||
enqueueSnackbar(res.data.message, {
|
||||
variant: 'success',
|
||||
});
|
||||
|
||||
return true;
|
||||
})
|
||||
.catch((res) => {
|
||||
if (res.response.status == 400) {
|
||||
let arr_message = res.response.data.message;
|
||||
|
||||
for (const key in arr_message) {
|
||||
enqueueSnackbar(arr_message[key][0], {
|
||||
variant: 'warning',
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Lab Result Detail List
|
||||
*/
|
||||
export const getLabResultDetailList = async ( claim_code: string ): Promise<DetailLabResultListType[]> => {
|
||||
const response = await axios.get(`/case_management/laboratorium_result/detail/${claim_code}/list`)
|
||||
.then((res) =>{
|
||||
return res.data.data.lab_result_list;
|
||||
})
|
||||
.catch((res) => {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* List Laboratorium
|
||||
*/
|
||||
export type LaboratoriumResultListType = {
|
||||
member_id : string,
|
||||
name : string,
|
||||
startdate : string,
|
||||
enddate : string,
|
||||
}
|
||||
|
||||
/**
|
||||
* Response Listing Claim
|
||||
*/
|
||||
export type ResponseListingClaimType = {
|
||||
member_detail : MemberDetailType,
|
||||
claim_list : ClaimListType[],
|
||||
}
|
||||
|
||||
/**
|
||||
* Member Detail
|
||||
*/
|
||||
export type MemberDetailType = {
|
||||
id : string,
|
||||
member_id : string,
|
||||
name : string,
|
||||
}
|
||||
|
||||
/**
|
||||
* List Claim
|
||||
*/
|
||||
export type ClaimListType = {
|
||||
claim_id : number,
|
||||
admission_date : string,
|
||||
discharge_date : string,
|
||||
claim_code : string,
|
||||
claim_status : string,
|
||||
service_type : string,
|
||||
member_id : string
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail Lab Result List
|
||||
*/
|
||||
export type DetailLabResultListType = {
|
||||
id : string|null,
|
||||
claim_id : string|null,
|
||||
claim_code : string,
|
||||
date : string|null,
|
||||
location : string,
|
||||
examination : string,
|
||||
lab_result_file : LabResultFileStrType[],
|
||||
created_at : string|null
|
||||
}
|
||||
|
||||
export type LabResultFileStrType = {
|
||||
lab_result_file_obj: any
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { Box, Grid } from '@mui/material';
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Page from '../../../components/Page';
|
||||
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
|
||||
// - Local -
|
||||
import LaboratoriumResultList from './Components/LaboratoriumResultList';
|
||||
|
||||
export default function LaboratoriumResult() {
|
||||
const pageTitle = "Laboratorium Result";
|
||||
|
||||
return (
|
||||
<Page title={ pageTitle } sx={{ px: 2 }}>
|
||||
<Grid container>
|
||||
{/* page header */}
|
||||
<Grid item xs={12}>
|
||||
<HeaderBreadcrumbs
|
||||
heading={ pageTitle }
|
||||
sx={{ px: 1 }}
|
||||
links={[
|
||||
{
|
||||
name: 'Dashboard',
|
||||
href: '/dashboard',
|
||||
},
|
||||
{
|
||||
name: pageTitle,
|
||||
href: '/case_management/laboratorium_result',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* tabel daily monitoring */}
|
||||
<Grid item xs={12}>
|
||||
<LaboratoriumResultList />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { Box, FormControlLabel, Grid, Checkbox, Typography, CircularProgress , Button, styled, Stack, IconButton, Card} from '@mui/material';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Label from '@/components/Label';
|
||||
// - Local -
|
||||
import FormCreateSearch from './FormCreateSearch';
|
||||
import FormCreateListChoose from './FormCreateListChoose';
|
||||
import FormCreateBtnUpload from './FormCreateBtnUpload';
|
||||
|
||||
/**
|
||||
* Icon, Utils, Types, Functions, theme, hook
|
||||
* ============================================
|
||||
*/
|
||||
import { ArrowBackIosNew } from '@mui/icons-material';
|
||||
import { fDateTimesecond } from '@/utils/formatTime';
|
||||
import { MemberListType } from '../Model/Types';
|
||||
import { addClaimRequest, getMemberList } from '../Model/Functions';
|
||||
import palette from '@/theme/palette';
|
||||
import FormCreateFilesUpload from './FormCreateFilesUpload';
|
||||
import useLoadOnScroll from '@/hooks/useLoadOnScroll';
|
||||
import useCollapseDrawer from '@/hooks/useCollapseDrawer';
|
||||
import FormCreateBtnChoose from './FormCreateBtnChoose';
|
||||
import axios from '../../../utils/axios';
|
||||
|
||||
export default function FormCreate() {
|
||||
const navigate = useNavigate()
|
||||
const defaultListChoosed:MemberListType[] = [];
|
||||
|
||||
// State
|
||||
// -------------------------
|
||||
const [keyword, setKeyword] = useState<string>('');
|
||||
const [listChoosed, setListChoosed] = useState<MemberListType[]>([]);
|
||||
const [isChoosed, setIsChoosed] = useState<boolean>(false);
|
||||
const [formIsLoading, setFormIsLoading] = useState<boolean>(false);
|
||||
|
||||
// List Choose - auto Scroll
|
||||
// -------------------------
|
||||
const fetchFunction = async (page: number): Promise<MemberListType[]> => getMemberList(page, keyword)
|
||||
|
||||
const {data: MemberList, isLoading: scrollIsLoading, setData, resetLastPage, refetchData} = useLoadOnScroll<MemberListType>(fetchFunction);
|
||||
|
||||
// List Choose - Search
|
||||
// -------------------------
|
||||
const handleSearch = (keyword: string) => {
|
||||
setData([])
|
||||
resetLastPage()
|
||||
setKeyword(keyword)
|
||||
refetchData()
|
||||
}
|
||||
|
||||
// Function - Clear Form
|
||||
// -----------------------------
|
||||
const clearForm = () => {
|
||||
setListChoosed(defaultListChoosed);
|
||||
setIsChoosed(false);
|
||||
}
|
||||
|
||||
// Function - Choose Patien Type
|
||||
// -----------------------------
|
||||
const handleChoosePatienType = (data: MemberListType, type: string) => {
|
||||
let newListChoosed = listChoosed.map((list) => {
|
||||
if (data.id == list.id) {
|
||||
list.patien_type = type
|
||||
}
|
||||
|
||||
return list;
|
||||
})
|
||||
setListChoosed(newListChoosed)
|
||||
}
|
||||
|
||||
// Function - Handle Btn Upload
|
||||
// -----------------------------
|
||||
const handleChangeInput = (data: MemberListType, type_file: 'kondisi'|'diagnosa'|'penunjang', file: any) => {
|
||||
let newListChoosed = listChoosed.map((list) => {
|
||||
if (data.id == list.id) {
|
||||
if (type_file == 'kondisi') {
|
||||
if (list.file_kondisi == undefined) {
|
||||
list.file_kondisi = [file];
|
||||
}
|
||||
else {
|
||||
list.file_kondisi.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (type_file == 'diagnosa') {
|
||||
if (list.file_diagnosa == undefined) {
|
||||
list.file_diagnosa = [file];
|
||||
}
|
||||
else {
|
||||
list.file_diagnosa.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (type_file == 'penunjang') {
|
||||
if (list.file_penunjang == undefined) {
|
||||
list.file_penunjang = [file];
|
||||
}
|
||||
else {
|
||||
list.file_penunjang.push(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
})
|
||||
|
||||
setListChoosed(newListChoosed)
|
||||
}
|
||||
|
||||
// Function - Handle Remove Fle
|
||||
// -----------------------------
|
||||
const handleRemoveFile = (data: MemberListType, type_file: 'kondisi'|'diagnosa'|'penunjang', target_index: number) => {
|
||||
let newListChoosed = listChoosed.map((list) => {
|
||||
if (data.id == list.id) {
|
||||
if (type_file == 'kondisi') {
|
||||
list.file_kondisi = list.file_kondisi?.filter((file: any, index: number) =>{
|
||||
if (target_index !== index) {
|
||||
return file;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (type_file == 'diagnosa') {
|
||||
list.file_diagnosa = list.file_diagnosa?.filter((file: any, index: number) =>{
|
||||
if (target_index !== index) {
|
||||
return file;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (type_file == 'penunjang') {
|
||||
list.file_penunjang = list.file_penunjang?.filter((file: any, index: number) =>{
|
||||
if (target_index !== index) {
|
||||
return file;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
})
|
||||
|
||||
setListChoosed(newListChoosed)
|
||||
}
|
||||
|
||||
// Function - Handle Submit Form
|
||||
// -----------------------------
|
||||
const handleSubmit = async () => {
|
||||
setFormIsLoading(true)
|
||||
let response = await addClaimRequest(listChoosed)
|
||||
setFormIsLoading(false)
|
||||
|
||||
if (response == true) {
|
||||
clearForm()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
let isDirty = listChoosed.some((row) => {
|
||||
if (row.patien_type == undefined) {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* Back Button */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 5}}>
|
||||
<IconButton size='large' color='inherit' onClick={() => isChoosed==false ? navigate(`/claim-requests`) : setIsChoosed(false)} >
|
||||
<ArrowBackIosNew/>
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
|
||||
{'Create Claim Requests'}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Choose Section */}
|
||||
<Grid container spacing={4} sx={{ px: 2, position: 'relative', display: isChoosed==false ? 'inherit' : 'none' }}>
|
||||
{/* Search */}
|
||||
<Grid item xs={12}>
|
||||
<FormCreateSearch onEmpty={() => handleSearch('')} onSubmit={(keyword) => handleSearch(keyword)} />
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
{/* List */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
{
|
||||
MemberList.map((row, index) => {
|
||||
return (
|
||||
<FormCreateListChoose
|
||||
key={index}
|
||||
data={row}
|
||||
ListChoosed={listChoosed}
|
||||
handleCheckedProp={(checked, data) => {
|
||||
checked ? setListChoosed((prevData) => [...prevData, data]) : setListChoosed((items) => items.filter(item => item.id != data.id))
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Loading */}
|
||||
<Grid item xs={12} sx={{ display: scrollIsLoading === false ? 'none' : 'flex', justifyContent: 'center', marginTop: '40px' }}>
|
||||
<CircularProgress />
|
||||
</Grid>
|
||||
|
||||
{/* Submit List */}
|
||||
<Grid item xs={12}>
|
||||
<FormCreateBtnChoose disabled={listChoosed.length==0} title={`Create Number Batch (${listChoosed.length})`} handleClickProp={() => setIsChoosed(true)} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Input Section */}
|
||||
<Grid container spacing={10} sx={{ px: 2, display: isChoosed==true ? 'inherit' : 'none' }}>
|
||||
{
|
||||
listChoosed.map((row, index) => {
|
||||
return (
|
||||
<Grid key={index} item xs={12}>
|
||||
<Grid container spacing={6}>
|
||||
{/* Patien Name */}
|
||||
<Grid item xs={12}>
|
||||
<Card sx={{ border: '1px solid rgba(0,0,0,0.05)', display: 'flex', justifyContent: 'space-between', borderRadius: '12px', px: '24px', py: '16px' }}>
|
||||
<Box>
|
||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||
{row.name}
|
||||
</Typography>
|
||||
<Typography variant="caption" color={palette.light.grey[500]} sx={{ fontWeight: 600 }}>
|
||||
{row.member_id}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Label variant="ghost" color="default">
|
||||
{fDateTimesecond(new Date())}
|
||||
</Label>
|
||||
</Card>
|
||||
</Grid>
|
||||
{/* Patien Type */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
{row.service_type.map((r,i) => {
|
||||
const code = r.code
|
||||
return (
|
||||
<Grid item xs={6}>
|
||||
<Button
|
||||
sx={{ padding: 2, width: '100%',border: row.patien_type === code ? '1px solid #19BBBB' : '1px solid #919EAB52' }}
|
||||
variant="outlined"
|
||||
color={row.patien_type === code ? 'primary' : 'inherit'}
|
||||
onClick={() => {
|
||||
handleChoosePatienType(row, code)
|
||||
}}
|
||||
>
|
||||
{r.name}
|
||||
</Button>
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* File Kondisi */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Condition Document</Typography>
|
||||
</Grid>
|
||||
|
||||
{row.file_kondisi && row.file_kondisi.map((file, index) => (
|
||||
<Grid item xs={12} key={index}>
|
||||
<FormCreateFilesUpload file={file} handleRemoveFileProp={() => handleRemoveFile(row, 'kondisi', index)} />
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<FormCreateBtnUpload handleChangeInputProp={(file) => handleChangeInput(row, 'kondisi', file)} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* File Diagnosa */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Diagnosis Document</Typography>
|
||||
</Grid>
|
||||
|
||||
{row.file_diagnosa && row.file_diagnosa.map((file, index) => (
|
||||
<Grid item xs={12} key={index}>
|
||||
<FormCreateFilesUpload file={file} handleRemoveFileProp={() => handleRemoveFile(row, 'diagnosa', index)} />
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<FormCreateBtnUpload handleChangeInputProp={(file) => handleChangeInput(row, 'diagnosa', file)} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* File Penunjang */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Supporting Result Document</Typography>
|
||||
</Grid>
|
||||
|
||||
{row.file_penunjang && row.file_penunjang.map((file, index) => (
|
||||
<Grid item xs={12} key={index}>
|
||||
<FormCreateFilesUpload file={file} handleRemoveFileProp={() => handleRemoveFile(row, 'penunjang', index)} />
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<FormCreateBtnUpload handleChangeInputProp={(file) => handleChangeInput(row, 'penunjang', file)} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Box display="flex" gap={1}>
|
||||
<Button variant="outlined" color="inherit" onClick={() => clearForm()}>
|
||||
Cancel
|
||||
</Button>
|
||||
<LoadingButton disabled={isDirty} type="submit" variant="contained" loading={formIsLoading} onClick={() => handleSubmit()}>
|
||||
Save Changes
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { styled, Button } from "@mui/material";
|
||||
import useCollapseDrawer from "@/hooks/useCollapseDrawer";
|
||||
|
||||
/**
|
||||
* Custom Style
|
||||
* ============================================
|
||||
*/
|
||||
const DivCustom1 = styled('div')(({ theme }) => ({
|
||||
background: 'white',
|
||||
position: 'fixed',
|
||||
left: '350px',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
paddingLeft: '32px',
|
||||
paddingRight: '32px',
|
||||
paddingTop: '32px',
|
||||
paddingBottom: '48px',
|
||||
[theme.breakpoints.between('sm', 'lg')]: {
|
||||
left: '0px',
|
||||
},
|
||||
}));
|
||||
|
||||
type Props = {
|
||||
disabled: boolean,
|
||||
title : string,
|
||||
handleClickProp: () => void
|
||||
}
|
||||
|
||||
export default function FormCreateBtnChoose ({disabled, title, handleClickProp}: Props) {
|
||||
const { collapseClick } = useCollapseDrawer();
|
||||
|
||||
return (
|
||||
<DivCustom1 sx={{ left: collapseClick ? '80px' : '350px' }}>
|
||||
<Button variant="contained" color="primary" disabled={disabled} sx={{ width: '100%', p: '11px' }} onClick={handleClickProp}>
|
||||
{title}
|
||||
</Button>
|
||||
</DivCustom1>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useRef } from "react";
|
||||
import { Box, ButtonBase, Typography } from "@mui/material";
|
||||
import Iconify from "@/components/Iconify";
|
||||
|
||||
type Props = {
|
||||
handleChangeInputProp: (event: any) => void
|
||||
}
|
||||
|
||||
export default function FormCreateBtnUpload ({handleChangeInputProp}: Props) {
|
||||
const fileInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
return (
|
||||
<ButtonBase sx={{ py: 5, border: '2px dashed #F9FAFB',bgcolor: '#919EAB52',borderRadius: '8px',width: '100%', height: '60px'}} onClick={() => fileInput.current?.click()}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
placeItems: 'center',
|
||||
gap: 1,
|
||||
placeContent: 'center',
|
||||
py:'11px'
|
||||
}}
|
||||
>
|
||||
<Iconify icon="icon-park-outline:upload-one" fontSize="1.5em" />
|
||||
<Typography variant="body1" fontWeight="bold" fontSize={'15px'}>
|
||||
Upload Result
|
||||
</Typography>
|
||||
</Box>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={fileInput}
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={(event) => handleChangeInputProp(event.target.files ? event.target.files[0] : {})}
|
||||
accept="application/pdf"
|
||||
/>
|
||||
</ButtonBase>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import Iconify from "@/components/Iconify";
|
||||
import { ArrowBackIosNew, InsertDriveFile } from '@mui/icons-material';
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
|
||||
type Props = {
|
||||
file: any,
|
||||
handleRemoveFileProp: () => void,
|
||||
}
|
||||
|
||||
export default function FormCreateFilesUpload({ file, handleRemoveFileProp }: Props) {
|
||||
return (
|
||||
<Stack direction="row" justifyContent={'space-between'} sx={{ mb: '16px' }}>
|
||||
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
|
||||
<InsertDriveFile />
|
||||
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
|
||||
</Stack>
|
||||
<Iconify
|
||||
icon="eva:trash-2-outline"
|
||||
color={'darkred'}
|
||||
onClick={() => {handleRemoveFileProp()}}
|
||||
sx={{cursor: 'pointer'}}
|
||||
></Iconify>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Box, FormControlLabel, Grid, Checkbox, Typography, Card} from '@mui/material';
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import Label from '@/components/Label';
|
||||
// - Local -
|
||||
|
||||
/**
|
||||
* Icon, Utils, Types, Functions, theme, hook
|
||||
* ============================================
|
||||
*/
|
||||
import { fDateTimesecond } from '@/utils/formatTime';
|
||||
import { MemberListType } from '../Model/Types';
|
||||
import palette from '@/theme/palette';
|
||||
|
||||
/**
|
||||
* Props
|
||||
* =====================================================
|
||||
*/
|
||||
type Props = {
|
||||
data: MemberListType,
|
||||
ListChoosed: MemberListType[],
|
||||
handleCheckedProp: (checked: boolean, data: MemberListType) => void,
|
||||
};
|
||||
|
||||
export default function FormCreateListChoose({data, ListChoosed, handleCheckedProp}: Props) {
|
||||
const [isChoosed, setIsChoosed] = useState<boolean>(false)
|
||||
|
||||
useEffect(() => {
|
||||
setIsChoosed(false);
|
||||
|
||||
ListChoosed.forEach(list => {
|
||||
if (list.id == data.id) {
|
||||
setIsChoosed(true);
|
||||
}
|
||||
})
|
||||
}, [ListChoosed])
|
||||
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<Card sx={{
|
||||
border: '0px solid rgba(0,0,0,0.125)', px: '24px', py: '16px', borderRadius: '12px', display: 'flex', justifyContent: 'space-between',
|
||||
bgcolor: (theme) => {
|
||||
return isChoosed ? palette.light.primary.lighter : palette.light.background.default
|
||||
}
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', px: '8px'}}>
|
||||
<FormControlLabel
|
||||
label=""
|
||||
control={<Checkbox onChange={(event, checked) => handleCheckedProp(checked, data)} />}
|
||||
checked={isChoosed}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||
{data.name}
|
||||
</Typography>
|
||||
<Typography variant="caption" color={palette.light.grey[500]} sx={{ fontWeight: 600 }}>
|
||||
{data.member_id}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Label variant="ghost" color="default">
|
||||
{fDateTimesecond(new Date())}
|
||||
</Label>
|
||||
</Card>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { Grid } from '@mui/material';
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import { FormProvider, RHFTextField } from '@/components/hook-form';
|
||||
// - Local -
|
||||
|
||||
/**
|
||||
* Icon, Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { Search } from '@mui/icons-material';
|
||||
import { SearchType } from '../Model/Types';
|
||||
|
||||
type Props = {
|
||||
onSubmit: (keyword: string) => void,
|
||||
onEmpty: () => void,
|
||||
};
|
||||
|
||||
const FormCreateSearch = ({ onSubmit, onEmpty }: Props) => {
|
||||
const defaultValuesSearchForm = {
|
||||
keyword: ''
|
||||
};
|
||||
|
||||
const methodsSearchForm = useForm<SearchType>({
|
||||
defaultValues: defaultValuesSearchForm
|
||||
});
|
||||
|
||||
const { handleSubmit, formState: { isDirty } } = methodsSearchForm;
|
||||
|
||||
// search on submit
|
||||
const onSubmitSearch = (data: SearchType ) => {
|
||||
onSubmit(data.keyword);
|
||||
}
|
||||
|
||||
// search on empty
|
||||
useEffect(() => {
|
||||
if (isDirty === false) {
|
||||
onEmpty()
|
||||
}
|
||||
},[isDirty])
|
||||
|
||||
return (
|
||||
<FormProvider methods={methodsSearchForm} onSubmit={handleSubmit(onSubmitSearch)}>
|
||||
<Grid container direction={"row"}>
|
||||
<Grid item xs={12}>
|
||||
<RHFTextField
|
||||
name="keyword"
|
||||
placeholder="Search..."
|
||||
autoComplete='off'
|
||||
fullWidth
|
||||
InputProps={{ startAdornment: <Search /> }}
|
||||
sx={{ input: { paddingLeft: '14px' } }}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</FormProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormCreateSearch
|
||||
@@ -0,0 +1,456 @@
|
||||
import * as Yup from 'yup';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import React, { useRef, useEffect, useMemo, useState } from 'react';
|
||||
import axios from '../../../utils/axios';
|
||||
import { FormProvider, RHFTextField } from '../../../components/hook-form';
|
||||
|
||||
import { makeFormData } from '@/utils/jsonToFormData';
|
||||
import {
|
||||
Autocomplete,
|
||||
Button,
|
||||
Grid,
|
||||
Stack,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
TextField,
|
||||
Typography,
|
||||
useTheme,
|
||||
List,
|
||||
ListItem,
|
||||
IconButton,
|
||||
ListItemAvatar,
|
||||
Avatar,
|
||||
ListItemText,
|
||||
Card,
|
||||
InputAdornment,
|
||||
Divider,
|
||||
ButtonBase,
|
||||
Box,
|
||||
} from '@mui/material';
|
||||
import Iconify from '../../../components/Iconify';
|
||||
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { fCurrency } from '../../../utils/formatNumber';
|
||||
import MemberSelectDialog from '../../../components/dialogs/MemberSelectDialog';
|
||||
import { Add, ArrowBackIosNew, DeleteOutline } from '@mui/icons-material';
|
||||
import { ClaimRequest, Files } from '@/@types/claims';
|
||||
import { fDateTimesecond } from '@/utils/formatTime';
|
||||
|
||||
interface FormValuesProps extends Partial<ClaimRequest> {
|
||||
taxes: boolean;
|
||||
inStock: boolean;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
isEdit: boolean;
|
||||
currentClaim?: ClaimRequest;
|
||||
};
|
||||
|
||||
export default function FormEdit({ isEdit, currentClaim }: Props) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const EditClaimSchema = Yup.object().shape({
|
||||
organization_id: Yup.string().required('Code Provider is required'),
|
||||
});
|
||||
|
||||
const defaultValues = useMemo(
|
||||
() => ({
|
||||
id: currentClaim?.id || '-',
|
||||
code: currentClaim?.code || '-',
|
||||
member_name: currentClaim?.member?.name || '-',
|
||||
date: currentClaim?.submission_date ? fDateTimesecond(currentClaim?.submission_date) : '-',
|
||||
claim_method: currentClaim?.payment_type || '-',
|
||||
service_type: currentClaim?.service_code || '-',
|
||||
organization_id: currentClaim?.organization?.code || '-',
|
||||
}),
|
||||
[currentClaim]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit && currentClaim) {
|
||||
reset(defaultValues);
|
||||
}
|
||||
if (!isEdit) {
|
||||
reset(defaultValues);
|
||||
}
|
||||
// setFileKondisis(currentClaim?.files_by_type?.claim_diagnosis);
|
||||
// setFileDiagnosas(currentClaim?.files_by_type?.claim_diagnosis);
|
||||
setFileHasilPenunjangCurrent(currentClaim?.files_by_type?.claim_result);
|
||||
}, [isEdit, currentClaim]);
|
||||
|
||||
|
||||
const methods = useForm<FormValuesProps>({
|
||||
resolver: yupResolver(EditClaimSchema),
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const {
|
||||
reset,
|
||||
watch,
|
||||
control,
|
||||
setValue,
|
||||
getValues,
|
||||
setError,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting },
|
||||
} = methods;
|
||||
|
||||
const values = watch();
|
||||
|
||||
const [isCheckingLimit, setIsCheckingLimit] = useState(false);
|
||||
const [isEligible, setIsEligible] = useState(false);
|
||||
const [memberBenefits, setMemberBenefits] = useState([]);
|
||||
const [diagnosisOption, setDiagnosisOption] = useState([]);
|
||||
const [isMemberDialogOpen, setIsMemberDialogOpen] = useState(false);
|
||||
const [member, setMember] = useState({})
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// Files Result Kondisi
|
||||
const fileKondisiInput = useRef<HTMLInputElement>(null);
|
||||
const [fileKondisis, setFileKondisis] = useState<Files>([]);
|
||||
|
||||
const handleKondisiInputChange = (event) => {
|
||||
if (event.target.files[0]) {
|
||||
setFileKondisis([...fileKondisis, ...event.target.files]);
|
||||
} else {
|
||||
console.log('NO FILE');
|
||||
}
|
||||
};
|
||||
const removeKondisiFiles = (filesState, index) => {
|
||||
setFileKondisis(
|
||||
filesState.filter((file, fileIndex) => {
|
||||
return fileIndex != index;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// Files Result Diagnosa
|
||||
const fileDiagnosaInput = useRef<HTMLInputElement>(null);
|
||||
const [fileDiagnosas, setFileDiagnosas] = useState([]);
|
||||
|
||||
const handleDiagnosaInputChange = (event) => {
|
||||
if (event.target.files[0]) {
|
||||
setFileDiagnosas([...fileDiagnosas, ...event.target.files]);
|
||||
} else {
|
||||
console.log('NO FILE');
|
||||
}
|
||||
};
|
||||
const removeDiagnosaFiles = (filesState, index) => {
|
||||
setFileDiagnosas(
|
||||
filesState.filter((file, fileIndex) => {
|
||||
return fileIndex != index;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// Files Result Hasil Penunjang
|
||||
const fileHasilPenunjangInput = useRef<HTMLInputElement>(null);
|
||||
const [fileHasilPenunjangs, setFileHasilPenunjangs] = useState([]);
|
||||
const [fileHasilPenunjangsCurrent, setFileHasilPenunjangCurrent] = useState([]);
|
||||
|
||||
const handleResultInputChange = (event) => {
|
||||
if (event.target.files[0]) {
|
||||
setFileHasilPenunjangs([...fileHasilPenunjangs, ...event.target.files]);
|
||||
} else {
|
||||
console.log('NO FILE');
|
||||
}
|
||||
};
|
||||
const removeFiles = (filesState, index) => {
|
||||
setFileHasilPenunjangs(
|
||||
filesState.filter((file, fileIndex) => {
|
||||
return fileIndex != index;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const onSubmit = async (data: FormValuesProps) => {
|
||||
try {
|
||||
// const formData = new FormData();
|
||||
// formData.append('result_files', fileHasilPenunjangs);
|
||||
// formData.append('diagnosa_files', fileDiagnosaInput);
|
||||
// formData.append('kondisi_files', fileKondisiInput);
|
||||
// formData.append('provider_code', data.organization_id);
|
||||
// formData.append('_method', 'PUT');
|
||||
const formData = makeFormData({
|
||||
result_files: fileHasilPenunjangs,
|
||||
diagnosa_files: fileDiagnosas,
|
||||
kondisi_files: fileKondisis,
|
||||
provider_code: data.organization_id,
|
||||
_method: 'PUT'
|
||||
});
|
||||
|
||||
const response = await axios.put(`/claim-requests/${data.id}`, formData);
|
||||
|
||||
reset();
|
||||
enqueueSnackbar('Claim Request Updated Successfully!', { variant: 'success' });
|
||||
navigate('/claim-requests');
|
||||
} catch (error: any) {
|
||||
if (error && error.response.status === 422) {
|
||||
for (const [key, value] of Object.entries(error.response.data.errors)) {
|
||||
// setError(key, { message: value[0] });
|
||||
enqueueSnackbar('Failed Processing Request', { variant: 'error' });
|
||||
}
|
||||
} else {
|
||||
enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center'}}>
|
||||
<IconButton size='large' color='inherit' onClick={() => navigate(`/claim-requests`)} >
|
||||
<ArrowBackIosNew/>
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
|
||||
{'Edit Claim Requests'}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Card sx={{paddingX:2, paddingY:2}}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={5}>
|
||||
<Typography variant="subtitle1">Code*</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={7}>
|
||||
<Typography variant="subtitle1">Name*</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={5}>
|
||||
<RHFTextField name="code" label="Code" disabled/>
|
||||
</Grid>
|
||||
<Grid item xs={7}>
|
||||
<RHFTextField name="member_name" label="Name" disabled/>
|
||||
</Grid>
|
||||
{/* <input type="hidden" name="id"/> */}
|
||||
|
||||
|
||||
<Grid item xs={12}></Grid>
|
||||
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="subtitle1">Date of Submission*</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="subtitle1">Claim Method*</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="subtitle1">Service Type*</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="subtitle1">Code Provider*</Typography>
|
||||
</Grid>
|
||||
|
||||
|
||||
<Grid item xs={3}>
|
||||
<RHFTextField InputProps={{endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<CalendarTodayIcon />
|
||||
</InputAdornment>
|
||||
), }}
|
||||
name="date" label="Date of Submission" disabled/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<RHFTextField name="claim_method" label="Claim Method" disabled/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<RHFTextField name="service_type" label="Service Type*" disabled/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<RHFTextField name="organization_id" label="Code Provider*"/>
|
||||
</Grid>
|
||||
|
||||
|
||||
{/* -------------------------------Upload Dokumen Kondisi------------------------------- */}
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant='h6'> Condition Document</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{fileKondisis &&
|
||||
fileKondisis.map((file, index) => (
|
||||
<Stack sx={{marginTop: 2}} direction="row" justifyContent={'space-between'} key={index}>
|
||||
<Typography sx={{ color: "text.secondary" }}>{file.name}</Typography>
|
||||
<Iconify
|
||||
icon="eva:trash-2-outline"
|
||||
color={'darkred'}
|
||||
onClick={() => {
|
||||
removeKondisiFiles(fileKondisis, index);
|
||||
}}
|
||||
></Iconify>
|
||||
</Stack>
|
||||
))}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
|
||||
bgcolor: '#919EAB52',
|
||||
borderRadius: '8px',
|
||||
width: '100%', height: '60px'}} onClick={() => fileKondisiInput.current?.click()}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
placeItems: 'center',
|
||||
gap: 1,
|
||||
placeContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
|
||||
<Typography variant="body1" fontWeight="bold">
|
||||
Add File
|
||||
</Typography>
|
||||
</Box>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={fileKondisiInput}
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={handleKondisiInputChange}
|
||||
accept="application/pdf"
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
{/* -------------------------------Upload Dokumen Diagnosa------------------------------- */}
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant='h6'> Diagnosis Document</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{fileDiagnosas &&
|
||||
fileDiagnosas.map((file, index) => (
|
||||
<Stack sx={{marginTop: 2}} direction="row" justifyContent={'space-between'} key={index}>
|
||||
<Typography sx={{ color: "text.secondary" }}>{file.name}</Typography>
|
||||
<Iconify
|
||||
icon="eva:trash-2-outline"
|
||||
color={'darkred'}
|
||||
onClick={() => {
|
||||
removeDiagnosaFiles(fileDiagnosas, index);
|
||||
}}
|
||||
></Iconify>
|
||||
</Stack>
|
||||
))}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
|
||||
bgcolor: '#919EAB52',
|
||||
borderRadius: '8px',
|
||||
width: '100%', height: '60px'}} onClick={() => fileDiagnosaInput.current?.click()}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
placeItems: 'center',
|
||||
gap: 1,
|
||||
placeContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
|
||||
<Typography variant="body1" fontWeight="bold">
|
||||
Add Result
|
||||
</Typography>
|
||||
</Box>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={fileDiagnosaInput}
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={handleDiagnosaInputChange}
|
||||
accept="application/pdf"
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
{/* -------------------------------Upload Result Hasil Penunjang------------------------------- */}
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant='h6'> Supporting Result Document</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{fileHasilPenunjangs &&
|
||||
fileHasilPenunjangs.map((file, index) => (
|
||||
<Stack sx={{marginTop: 2}} direction="row" justifyContent={'space-between'} key={index}>
|
||||
<Typography sx={{ color: "text.secondary" }}>{file.name}</Typography>
|
||||
<Iconify
|
||||
icon="eva:trash-2-outline"
|
||||
color={'darkred'}
|
||||
onClick={() => {
|
||||
removeFiles(fileHasilPenunjangs, index);
|
||||
}}
|
||||
></Iconify>
|
||||
</Stack>
|
||||
))}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
|
||||
bgcolor: '#919EAB52',
|
||||
borderRadius: '8px',
|
||||
width: '100%', height: '60px'}} onClick={() => fileHasilPenunjangInput.current?.click()}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
placeItems: 'center',
|
||||
gap: 1,
|
||||
placeContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
|
||||
<Typography variant="body1" fontWeight="bold">
|
||||
Add Result
|
||||
</Typography>
|
||||
</Box>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={fileHasilPenunjangInput}
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={handleResultInputChange}
|
||||
accept="application/pdf"
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
</Grid>
|
||||
</Card>
|
||||
<Grid container marginTop={3}>
|
||||
<Grid item xs={12} md={12} >
|
||||
<Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button
|
||||
sx={{
|
||||
margin: 1
|
||||
}}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
color='inherit'
|
||||
onClick={() => navigate(`/claim-requests`)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<LoadingButton
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
loading={isSubmitting}
|
||||
>
|
||||
Update
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import * as Yup from 'yup';
|
||||
import { Box, IconButton } from '@mui/material';
|
||||
import { ArrowBackIosNew } from '@mui/icons-material';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { Autocomplete, Button, Card, Collapse, Container, Divider, Grid, 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 { useParams } from 'react-router-dom';
|
||||
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
|
||||
import { FormProvider, RHFCheckbox, RHFSelect, RHFTextField } from '../../components/hook-form';
|
||||
import Page from '../../components/Page';
|
||||
@@ -15,49 +17,46 @@ import { enqueueSnackbar } from 'notistack';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { fCurrency } from '../../utils/formatNumber';
|
||||
import Iconify from '../../components/Iconify';
|
||||
import Form from './Form';
|
||||
import { ClaimRequest } from '@/@types/claims';
|
||||
import FormEdit from './Components/FormEdit';
|
||||
import FormCreate from './Components/FormCreate';
|
||||
|
||||
export default function ClaimsCreateUpdate() {
|
||||
|
||||
const { themeStretch } = useSettings();
|
||||
const { id } = useParams();
|
||||
|
||||
const isEdit = id ? true : false;
|
||||
const { themeStretch } = useSettings();
|
||||
const { id } = useParams();
|
||||
|
||||
const [currentClaim, setCurrentClaim] = useState();
|
||||
const isEdit = id ? true : false;
|
||||
|
||||
const [currentClaim, setCurrentClaim] = useState<ClaimRequest>();
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit) {
|
||||
axios.get('/claim-requests/' + id).then((res) => {
|
||||
console.log('Yeet', res.data);
|
||||
setCurrentClaim(res.data.data);
|
||||
});
|
||||
|
||||
console.log(currentClaim)
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit) {
|
||||
axios.get('/claims/' + id).then((res) => {
|
||||
// console.log('Yeet', res.data);
|
||||
setCurrentClaim(res.data);
|
||||
});
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
|
||||
return (
|
||||
<Page title={isEdit ? `Edit Claim : ${currentClaim?.id}` : "Create New Claim"}>
|
||||
<Page title={isEdit ? `Edit Claim Request` : "Create New Claim"}>
|
||||
<Container maxWidth={themeStretch ? false : 'xl'}>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<HeaderBreadcrumbs
|
||||
heading={
|
||||
!isEdit
|
||||
? 'Create New Claim'
|
||||
: `Edit Claim : ${currentClaim?.code}`
|
||||
}
|
||||
links={[
|
||||
{ name: 'Dashboard', href: '/dashboard' },
|
||||
{
|
||||
name: 'Claim',
|
||||
href: '/claims',
|
||||
},
|
||||
{ name: !isEdit ? 'Create' : currentClaim?.id ?? '' },
|
||||
]}
|
||||
/>
|
||||
</Stack>
|
||||
{
|
||||
id == undefined
|
||||
?
|
||||
(
|
||||
<FormCreate />
|
||||
)
|
||||
:
|
||||
(
|
||||
<FormEdit isEdit={isEdit} currentClaim={currentClaim} />
|
||||
)
|
||||
}
|
||||
|
||||
<Form isEdit={isEdit} currentClaim={currentClaim} />
|
||||
</Container>
|
||||
</Page>
|
||||
);
|
||||
|
||||
306
frontend/dashboard/src/pages/ClaimRequests/Detail.tsx
Normal file
306
frontend/dashboard/src/pages/ClaimRequests/Detail.tsx
Normal file
@@ -0,0 +1,306 @@
|
||||
// mui
|
||||
import { Container, Grid, Stack, Typography, Card, TextField, Divider, ButtonBase, Box, IconButton } from '@mui/material';
|
||||
// components
|
||||
import Page from '../../components/Page';
|
||||
// utils
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
// react
|
||||
import { useNavigate, useParams, useLocation } from 'react-router-dom';
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import axios from '../../utils/axios';
|
||||
// pages
|
||||
import DetailTimeline from '../../pages/ClaimRequests/DetailTimeline';
|
||||
import DetailStepper from '../../pages/ClaimRequests/DetailStepper';
|
||||
import { format } from 'date-fns';
|
||||
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
|
||||
import Button from '@mui/material/Button';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import RemoveIcon from '@mui/icons-material/Remove';
|
||||
import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers';
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import Iconify from '@/components/Iconify';
|
||||
import { fPostFormat } from '@/utils/formatTime';
|
||||
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { fDateTimesecond } from '@/utils/formatTime';
|
||||
import { makeFormData } from '@/utils/jsonToFormData';
|
||||
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function Detail() {
|
||||
const location = useLocation();
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
const code = queryParams.get('code');
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { themeStretch } = useSettings();
|
||||
const [data, setData] = useState();
|
||||
const [dataDialog, setDataDialog] = useState();
|
||||
const [document, setDocument] = useState(null);
|
||||
|
||||
const { id } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get('/claim-requests/detail/'+id)
|
||||
.then((response) => {
|
||||
setData(response.data);
|
||||
setDataDialog(response.data.data.dialog_submits);
|
||||
setDocument(response.data.data.documents);
|
||||
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
}, []);
|
||||
|
||||
const [isInvoiceVisible, setInvoiceVisibility] = useState(false);
|
||||
|
||||
const handleInvoice = () => {
|
||||
setInvoiceVisibility(!isInvoiceVisible);
|
||||
}
|
||||
const currentDate = new Date();
|
||||
const formattedCurrentDate = format(currentDate, 'dd MMM yyyy');
|
||||
const [dateInvoice, setDateInvoice] = useState(currentDate);
|
||||
|
||||
const fileInvoiceInput = useRef<HTMLInputElement>(null);
|
||||
const [fileInvoices, setFileInvoices] = useState([]);
|
||||
|
||||
const handleInvoiceInputChange = (event) => {
|
||||
if (event.target.files[0]) {
|
||||
setFileInvoices([...fileInvoices, ...event.target.files]);
|
||||
} else {
|
||||
console.log('NO FILE');
|
||||
}
|
||||
};
|
||||
const removeInvoiceFiles = (filesState, index) => {
|
||||
setFileInvoices(
|
||||
filesState.filter((file, fileIndex) => {
|
||||
return fileIndex != index;
|
||||
})
|
||||
);
|
||||
};
|
||||
const date = dateInvoice ? fPostFormat(dateInvoice, 'yyyy-MM-dd') : null;
|
||||
|
||||
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
|
||||
const handleCloseDialogSubmit = () => {
|
||||
setOpenDialogSubmit(false);
|
||||
}
|
||||
const handleSubmitData = () => {
|
||||
// if(fileInvoices.length > 0)
|
||||
// {
|
||||
//submit data
|
||||
axios
|
||||
.post('claim-requests/'+id+'/approve')
|
||||
.then((response) => {
|
||||
enqueueSnackbar('Success Submit Claim Request', { variant: 'success' });
|
||||
setOpenDialogSubmit(false);
|
||||
})
|
||||
.catch(({ response }) => {
|
||||
enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' });
|
||||
});
|
||||
//Upload file invoices
|
||||
const formData = makeFormData({
|
||||
date:date,
|
||||
invoice_files: fileInvoices,
|
||||
});
|
||||
axios
|
||||
.post('claim-requests/'+id+'/invoice-files', formData)
|
||||
.then((response) => {
|
||||
enqueueSnackbar(response.data.message ?? 'Success upload invoice', { variant: 'success' });
|
||||
})
|
||||
.catch(({ response }) => {
|
||||
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
|
||||
});
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// enqueueSnackbar('Please upload file invoice, before submit', { variant: 'warning' });
|
||||
// }
|
||||
|
||||
setTimeout(() =>
|
||||
{
|
||||
window.location.reload();
|
||||
}, 5000);
|
||||
|
||||
};
|
||||
|
||||
const check_invoice = document?.find((dataInvoice) => dataInvoice.type === 'claim-invoice');
|
||||
|
||||
return (
|
||||
<Page title='Detail'>
|
||||
<Container maxWidth={themeStretch ? false : 'xl'}>
|
||||
<Stack direction="row" alignItems="center" sx={{ marginBottom: 3 }}>
|
||||
<ArrowBackIosIcon onClick={() => navigate(-1)} sx={{cursor:'pointer'}}/>
|
||||
<Typography variant="h5" sx={{marginLeft:2}}>{(data && data.data) ? data.data.status.code : ''}</Typography>
|
||||
{data ? (
|
||||
<Stack direction="row" spacing={2} ml="auto">
|
||||
<Typography variant="body2" sx={{color: '#757575'}}>Submission Date</Typography>
|
||||
<Typography variant="body2" fontWeight="bold">{(data && data.data) ? format(new Date(data.data.status.submission_date), "d MMM yyyy") : ''}</Typography>
|
||||
</Stack>
|
||||
) : ''}
|
||||
</Stack>
|
||||
{data ? (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={12}>
|
||||
<DetailStepper data={data}/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={12}>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Typography variant="subtitle1">Format Claim</Typography>
|
||||
<Button variant="outlined" color="primary" startIcon={< DownloadIcon/>} sx={{marginLeft: 'auto'}}>
|
||||
<Typography variant="button" display="block">Import</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
{check_invoice ? (
|
||||
<Grid item xs={12} md={12}>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Typography variant="subtitle1">Request Claim</Typography>
|
||||
<Button variant="outlined" color="primary" startIcon={ isInvoiceVisible ? < RemoveIcon/> : < AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => handleInvoice()}>
|
||||
<Typography variant="button" display="block">Invoice</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
) : ''}
|
||||
<Grid item xs={12} md={12} sx={{display : isInvoiceVisible ? '' : 'none',}}>
|
||||
<Card sx={{padding: 2}}>
|
||||
<Stack direction="column" spacing={2}>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DatePicker
|
||||
label="Invoice Date"
|
||||
value={dateInvoice}
|
||||
onChange={(newValue) => {
|
||||
setDateInvoice(newValue);
|
||||
}}
|
||||
inputFormat="dd MMM yyyy"
|
||||
renderInput={(params) => <TextField sx={{width:'40%'}} {...params} defaultValue={formattedCurrentDate} required/>}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
<Stack
|
||||
divider={<Divider orientation="horizontal" flexItem />}
|
||||
spacing={1}
|
||||
sx={{ marginY: 2 }}
|
||||
>
|
||||
{fileInvoices &&
|
||||
fileInvoices.map((file, index) => (
|
||||
<Stack direction="row" justifyContent={'space-between'} key={index}>
|
||||
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
|
||||
<InsertDriveFileIcon />
|
||||
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
|
||||
</Stack>
|
||||
<Iconify
|
||||
icon="eva:trash-2-outline"
|
||||
color={'darkred'}
|
||||
onClick={() => {
|
||||
removeInvoiceFiles(fileInvoices, index);
|
||||
}}
|
||||
sx={{cursor: 'pointer'}}
|
||||
></Iconify>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
|
||||
bgcolor: '#919EAB52',
|
||||
borderRadius: '8px',
|
||||
width: '100%', height: '60px'}} onClick={() => fileInvoiceInput.current?.click()}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
placeItems: 'center',
|
||||
gap: 1,
|
||||
placeContent: 'center',
|
||||
|
||||
|
||||
}}
|
||||
>
|
||||
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
|
||||
<Typography variant="body1" fontWeight="bold">
|
||||
Upload Invoice
|
||||
</Typography>
|
||||
</Box>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={fileInvoiceInput}
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={handleInvoiceInputChange}
|
||||
accept="application/pdf"
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={12}>
|
||||
<DetailTimeline data={data}/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={12}>
|
||||
<Stack direction="row" padding={4}>
|
||||
{dataDialog && dataDialog.status === 'requested' ? (
|
||||
<>
|
||||
<Button variant="outlined" sx={{color: '#212B36', marginLeft: 'auto', borderColor: '#919EAB52'}} >Cancel</Button>
|
||||
<Button sx={{backgroundColor: '#19BBBB', marginLeft: 1}} variant="contained" onClick={()=> setOpenDialogSubmit(true)}>Submit</Button>
|
||||
</>
|
||||
) : ''}
|
||||
{/* Dialog Submits */}
|
||||
<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>
|
||||
{dataDialog ? (
|
||||
<Stack spacing={2} padding={2}>
|
||||
<Typography variant='body1'>Are you sure to submit this claim ?</Typography>
|
||||
<Card sx={{padding:2}} >
|
||||
<Stack direction='row' spacing={2}>
|
||||
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Code</Typography>
|
||||
<Typography variant='subtitle2' sx={{width: '70%'}}>{dataDialog.code}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2}>
|
||||
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Name</Typography>
|
||||
<Typography variant='subtitle2' sx={{width: '70%'}}>{dataDialog.name}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2}>
|
||||
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Date Submission</Typography>
|
||||
<Typography variant='subtitle2' sx={{width: '70%'}}>{fDateTimesecond(dataDialog.submission_date)}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2}>
|
||||
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Claim Method</Typography>
|
||||
<Typography variant='subtitle2' sx={{width: '70%'}}>Service Type</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2}>
|
||||
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Service Type</Typography>
|
||||
<Typography variant='subtitle2' sx={{width: '70%'}}>
|
||||
{dataDialog.service_code === 'IP' ? 'Inpatient' : 'Outpatient'}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Stack>
|
||||
) : ''}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogSubmit}>Cancel</Button>
|
||||
<Button sx={{backgroundColor: '#19BBBB'}} onClick={handleSubmitData} variant="contained">Submit</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : ''}
|
||||
</Container>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
58
frontend/dashboard/src/pages/ClaimRequests/DetailStepper.tsx
Normal file
58
frontend/dashboard/src/pages/ClaimRequests/DetailStepper.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import Stepper from '@mui/material/Stepper';
|
||||
import Step from '@mui/material/Step';
|
||||
import StepLabel from '@mui/material/StepLabel';
|
||||
import { useEffect, useState } from 'react';
|
||||
import ClearIcon from '@mui/icons-material/Clear';
|
||||
|
||||
const steps = [
|
||||
'Request',
|
||||
'Review',
|
||||
'Approval',
|
||||
'Decline',
|
||||
];
|
||||
|
||||
export default function HorizontalLinearAlternativeLabelStepper({data}) {
|
||||
const [active, setActive] = useState(0);
|
||||
const [status, SetStatus] = useState(null);
|
||||
let updatedSteps = [...steps];
|
||||
useEffect(() => {
|
||||
if (data && data.data) {
|
||||
if (data.data.status.status === 'requested') {
|
||||
setActive(1);
|
||||
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
|
||||
}
|
||||
else if (data.data.status.status === 'reviewed') {
|
||||
setActive(2);
|
||||
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
|
||||
}
|
||||
else if (data.data.status.status === 'approved')
|
||||
{
|
||||
setActive(3);
|
||||
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
|
||||
}
|
||||
else if(data.data.status.status === 'declined')
|
||||
{
|
||||
setActive(4)
|
||||
updatedSteps = updatedSteps.filter(step => step !== 'Approval');
|
||||
}
|
||||
}
|
||||
SetStatus(updatedSteps);
|
||||
}, [data]);
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%', marginBottom: 2 }}>
|
||||
<Stepper activeStep={active} alternativeLabel>
|
||||
{status?.map((label) => (
|
||||
<Step key={label}>
|
||||
<StepLabel icon={label==='Decline' ? <ClearIcon sx={{ color: 'white', backgroundColor: 'red', borderRadius: '50%' }} /> : ''}>{label}</StepLabel>
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
426
frontend/dashboard/src/pages/ClaimRequests/DetailTimeline.tsx
Normal file
426
frontend/dashboard/src/pages/ClaimRequests/DetailTimeline.tsx
Normal file
@@ -0,0 +1,426 @@
|
||||
import * as React from 'react';
|
||||
import Timeline from '@mui/lab/Timeline';
|
||||
import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
|
||||
import TimelineSeparator from '@mui/lab/TimelineSeparator';
|
||||
import TimelineConnector from '@mui/lab/TimelineConnector';
|
||||
import TimelineContent from '@mui/lab/TimelineContent';
|
||||
import TimelineDot from '@mui/lab/TimelineDot';
|
||||
import {Typography, Card, Stack, ButtonBase, Box, Divider} from '@mui/material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Button from '@mui/material/Button';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import Iconify from '../../components/Iconify';
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { format } from 'date-fns';
|
||||
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||
import DescriptionIcon from '@mui/icons-material/Description';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import axios from '../../utils/axios';
|
||||
import { makeFormData } from '@/utils/jsonToFormData';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { useParams} from 'react-router-dom';
|
||||
|
||||
const Item1 = styled(Paper)(({ theme }) => ({
|
||||
...theme.typography.body2,
|
||||
padding: theme.spacing(1),
|
||||
textAlign: 'center',
|
||||
backgroundColor: '#919EAB29',
|
||||
color: '#637381',
|
||||
width: 'fit-content',
|
||||
marginRight: 'auto',
|
||||
}));
|
||||
|
||||
const Item2 = styled(Paper)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
|
||||
...theme.typography.body2,
|
||||
padding: theme.spacing(1),
|
||||
textAlign: 'center',
|
||||
color: theme.palette.text.secondary,
|
||||
width: 'fit-content',
|
||||
marginLeft: 'auto',
|
||||
}));
|
||||
|
||||
export default function NoOppositeContent({data}) {
|
||||
const [timeline, setTimeline] = useState(null);
|
||||
const [requestFile, setRequestFile] = useState(null);
|
||||
const [document, setDocument] = useState(null);
|
||||
useEffect(() => {
|
||||
if (data && data.data) {
|
||||
setTimeline(data.data.timeline);
|
||||
setRequestFile(data.data.request_files);
|
||||
setDocument(data.data.documents);
|
||||
}
|
||||
|
||||
}, [data]);
|
||||
|
||||
// Diagnosis
|
||||
const fileRequestDocumentInputDiagnosis = useRef<HTMLInputElement>(null);
|
||||
const [fileDiagnosis, setFileDiagnosis] = useState([]);
|
||||
const handleRequestDocumentInputChangeDiagnosis = (event) => {
|
||||
if (event.target.files[0]) {
|
||||
setFileDiagnosis([...fileDiagnosis, ...event.target.files]);
|
||||
}
|
||||
};
|
||||
const removeFileDiagnois = (filesState, index) => {
|
||||
setFileDiagnosis(
|
||||
filesState.filter((file, fileIndex) => {
|
||||
return fileIndex != index;
|
||||
})
|
||||
);
|
||||
};
|
||||
// Kondisi
|
||||
const fileRequestDocumentInputKondisi = useRef<HTMLInputElement>(null);
|
||||
const [fileKondisi, setFileKondisi] = useState([]);
|
||||
const handleRequestDocumentInputChangeKondisi = (event) => {
|
||||
if (event.target.files[0]) {
|
||||
setFileKondisi([...fileKondisi, ...event.target.files]);
|
||||
}
|
||||
};
|
||||
const removeFileKondisi = (filesState, index) => {
|
||||
setFileKondisi(
|
||||
filesState.filter((file, fileIndex) => {
|
||||
return fileIndex != index;
|
||||
})
|
||||
);
|
||||
};
|
||||
// Result
|
||||
const fileRequestDocumentInputResult = useRef<HTMLInputElement>(null);
|
||||
const [fileResult, setFileResult] = useState([]);
|
||||
const handleRequestDocumentInputChangeResult = (event) => {
|
||||
if (event.target.files[0]) {
|
||||
setFileResult([...fileResult, ...event.target.files]);
|
||||
}
|
||||
};
|
||||
const removeFileResult = (filesState, index) => {
|
||||
setFileResult(
|
||||
filesState.filter((file, fileIndex) => {
|
||||
return fileIndex != index;
|
||||
})
|
||||
);
|
||||
};
|
||||
const { id } = useParams();
|
||||
const [submitLoading, setSubmitLoading] = useState(false);
|
||||
const submitRequestFiles = () => {
|
||||
setSubmitLoading(true);
|
||||
const formData = makeFormData({
|
||||
fileDiagnosis: fileDiagnosis,
|
||||
fileKondisis: fileKondisi,
|
||||
fileResults: fileResult
|
||||
});
|
||||
axios
|
||||
.post('claim-requests/'+id+'/request-files', formData)
|
||||
.then((response) => {
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(({ response }) => {
|
||||
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
|
||||
});
|
||||
}
|
||||
const submitButton = requestFile?.find((dataRequestFile) => dataRequestFile.check_files === null);
|
||||
return (
|
||||
<>
|
||||
{timeline?.map((dataTimeline, index) => (
|
||||
<Timeline
|
||||
sx={{
|
||||
[`& .${timelineItemClasses.root}:before`]: {
|
||||
flex: 0,
|
||||
padding: 0,
|
||||
},
|
||||
}}
|
||||
key={index}
|
||||
>
|
||||
<Typography variant="body2" gutterBottom fontWeight="bold">{dataTimeline.date ? format(new Date(dataTimeline.date), "d MMM yyyy") : ''}</Typography>
|
||||
<TimelineItem>
|
||||
<TimelineSeparator>
|
||||
<TimelineDot />
|
||||
<TimelineConnector />
|
||||
</TimelineSeparator>
|
||||
<TimelineContent spacing={3}>
|
||||
<Card sx={{ borderRadius: '6px', paddingY: 2 }}>
|
||||
<Stack sx={{marginLeft: 2, marginRight: 2, marginTop: 2 }}>
|
||||
<Stack direction="row" sx={{marginBottom: 2, paddingBottom: 2, borderBottom: '1px solid #919EAB52' }}>
|
||||
<Item1>{dataTimeline.date ? format(new Date(dataTimeline.date), "HH : mm") : ''}</Item1>
|
||||
<Item2 sx={{backgroundColor: dataTimeline.txt_status_backgroundColor, color: dataTimeline.txt_status_color}}>{dataTimeline.txt_status}</Item2>
|
||||
</Stack>
|
||||
<Stack direction="row" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Typography variant="body2" gutterBottom>Detail:</Typography>
|
||||
<Typography variant="body2" gutterBottom>{dataTimeline.description}</Typography>
|
||||
</Stack>
|
||||
{dataTimeline.status === 'reviewed' && requestFile ? (
|
||||
<>
|
||||
{submitButton ? (
|
||||
<Typography variant="body2" gutterBottom>Request Document</Typography>
|
||||
) : (
|
||||
<Typography sx={{color: '#19BBBB'}} variant="body2" gutterBottom>Request Document Success Uploaded</Typography>
|
||||
)}
|
||||
{/* Diagnosis */}
|
||||
{requestFile?.map((dataRequestFile, index) => {
|
||||
if(dataRequestFile.type !== 'claim-diagnosis' || dataRequestFile.check_files !== null){
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
|
||||
<Typography variant="body2" gutterBottom fontWeight="bold">
|
||||
Diagnosis
|
||||
</Typography>
|
||||
<Stack
|
||||
divider={<Divider orientation="horizontal" flexItem />}
|
||||
spacing={1}
|
||||
sx={{ marginY: 2 }}
|
||||
>
|
||||
{fileDiagnosis &&
|
||||
fileDiagnosis.map((file, index) => (
|
||||
<Stack direction="row" justifyContent={'space-between'} key={index}>
|
||||
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
|
||||
<InsertDriveFileIcon />
|
||||
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
|
||||
</Stack>
|
||||
<Iconify
|
||||
icon="eva:trash-2-outline"
|
||||
color={'darkred'}
|
||||
onClick={() => {
|
||||
removeFileDiagnois(fileDiagnosis, index);
|
||||
}}
|
||||
sx={{cursor: 'pointer'}}
|
||||
></Iconify>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
p: 4,
|
||||
border: '2px dashed #F9FAFB',
|
||||
bgcolor: '#919EAB52',
|
||||
borderRadius: '8px',
|
||||
width: '100%',
|
||||
height: '60px',
|
||||
}}
|
||||
onClick={() => fileRequestDocumentInputDiagnosis.current?.click()}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
placeItems: 'center',
|
||||
gap: 1,
|
||||
placeContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
|
||||
<Typography variant="body1" fontWeight="bold">
|
||||
Add Result
|
||||
</Typography>
|
||||
</Box>
|
||||
<input
|
||||
type="file"
|
||||
id={`file-${index}`}
|
||||
ref={fileRequestDocumentInputDiagnosis}
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={(event) => handleRequestDocumentInputChangeDiagnosis(event)}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
{/* Kondisi */}
|
||||
{requestFile?.map((dataRequestFile, index) => {
|
||||
if(dataRequestFile.type !== 'claim-kondisi' || dataRequestFile.check_files !== null){
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
|
||||
<Typography variant="body2" gutterBottom fontWeight="bold">
|
||||
Condition
|
||||
</Typography>
|
||||
<Stack
|
||||
divider={<Divider orientation="horizontal" flexItem />}
|
||||
spacing={1}
|
||||
sx={{ marginY: 2 }}
|
||||
>
|
||||
{fileKondisi &&
|
||||
fileKondisi.map((file, index) => (
|
||||
<Stack direction="row" justifyContent={'space-between'} key={index}>
|
||||
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
|
||||
<InsertDriveFileIcon />
|
||||
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
|
||||
</Stack>
|
||||
<Iconify
|
||||
icon="eva:trash-2-outline"
|
||||
color={'darkred'}
|
||||
onClick={() => {
|
||||
removeFileKondisi(fileKondisi, index);
|
||||
}}
|
||||
sx={{cursor: 'pointer'}}
|
||||
></Iconify>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
p: 4,
|
||||
border: '2px dashed #F9FAFB',
|
||||
bgcolor: '#919EAB52',
|
||||
borderRadius: '8px',
|
||||
width: '100%',
|
||||
height: '60px',
|
||||
}}
|
||||
onClick={() => fileRequestDocumentInputKondisi.current?.click()}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
placeItems: 'center',
|
||||
gap: 1,
|
||||
placeContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
|
||||
<Typography variant="body1" fontWeight="bold">
|
||||
Add Result
|
||||
</Typography>
|
||||
</Box>
|
||||
<input
|
||||
type="file"
|
||||
id={`file-${index}`}
|
||||
ref={fileRequestDocumentInputKondisi}
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={(event) => handleRequestDocumentInputChangeKondisi(event)}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
{/* Supporting Result */}
|
||||
{requestFile?.map((dataRequestFile, index) => {
|
||||
if(dataRequestFile.type !== 'claim-result' || dataRequestFile.check_files !== null){
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
|
||||
<Typography variant="body2" gutterBottom fontWeight="bold">
|
||||
Supporting Result
|
||||
</Typography>
|
||||
<Stack
|
||||
divider={<Divider orientation="horizontal" flexItem />}
|
||||
spacing={1}
|
||||
sx={{ marginY: 2 }}
|
||||
>
|
||||
{fileResult &&
|
||||
fileResult.map((file, index) => (
|
||||
<Stack direction="row" justifyContent={'space-between'} key={index}>
|
||||
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
|
||||
<InsertDriveFileIcon />
|
||||
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
|
||||
</Stack>
|
||||
<Iconify
|
||||
icon="eva:trash-2-outline"
|
||||
color={'darkred'}
|
||||
onClick={() => {
|
||||
removeFileResult(fileResult, index);
|
||||
}}
|
||||
sx={{cursor: 'pointer'}}
|
||||
></Iconify>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
p: 4,
|
||||
border: '2px dashed #F9FAFB',
|
||||
bgcolor: '#919EAB52',
|
||||
borderRadius: '8px',
|
||||
width: '100%',
|
||||
height: '60px',
|
||||
}}
|
||||
onClick={() => fileRequestDocumentInputResult.current?.click()}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
placeItems: 'center',
|
||||
gap: 1,
|
||||
placeContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
|
||||
<Typography variant="body1" fontWeight="bold">
|
||||
Add Result
|
||||
</Typography>
|
||||
</Box>
|
||||
<input
|
||||
type="file"
|
||||
id={`file-${index}`}
|
||||
ref={fileRequestDocumentInputResult}
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={(event) => handleRequestDocumentInputChangeResult(event)}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
{submitButton ? (
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
sx={{ marginTop: 2, p: 2, backgroundColor: '#19BBBB' }}
|
||||
onClick={() => {
|
||||
submitRequestFiles();
|
||||
}}
|
||||
loading={submitLoading}
|
||||
>
|
||||
Submit
|
||||
</LoadingButton>
|
||||
) : ''}
|
||||
</>
|
||||
) : ''}
|
||||
</Stack>
|
||||
</Card>
|
||||
{dataTimeline.status === 'requested' ? (
|
||||
<Card sx={{marginTop: 2 }}>
|
||||
<Stack sx={{marginLeft: 2, marginRight: 2, marginTop: 2 }}>
|
||||
<Stack direction="row" spacing={2} sx={{marginBottom: 2, paddingBottom: 2, borderBottom: '1px solid #919EAB52' }} alignItems="center">
|
||||
<DescriptionIcon />
|
||||
<Typography variant="Subtitle2" sx={{fontWeight: 'bold'}}>Documents</Typography>
|
||||
</Stack>
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
{document?.map((dataDocument, index) => (
|
||||
<Stack direction="column" spacing={2} key={index}>
|
||||
<Typography variant="Subtitle2" gutterBottom>
|
||||
{dataDocument.type === 'claim-diagnosis' ?
|
||||
'Diagnosis'
|
||||
: dataDocument.type === 'claim-kondisi' ?
|
||||
'Condition'
|
||||
: dataDocument.type === 'claim-result' ?
|
||||
'Supporting Result'
|
||||
: dataDocument.type === 'claim-invoice' ?
|
||||
'Invoice'
|
||||
: ''}
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
|
||||
<InsertDriveFileIcon />
|
||||
<a
|
||||
href={dataDocument.path}
|
||||
style={{ cursor: 'pointer', textDecoration: 'underline', color: '#19BBBB' }}
|
||||
target="_blank"
|
||||
>
|
||||
<Typography variant="body2" gutterBottom>{dataDocument.original_name ? dataDocument.original_name : '-'}</Typography>
|
||||
</a>
|
||||
</Stack>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
) : ''}
|
||||
</TimelineContent>
|
||||
</TimelineItem>
|
||||
</Timeline>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,596 +0,0 @@
|
||||
import * as Yup from 'yup';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import axios from '../../utils/axios';
|
||||
import { FormProvider, RHFTextField } from '../../components/hook-form';
|
||||
import {
|
||||
Autocomplete,
|
||||
Button,
|
||||
Grid,
|
||||
Stack,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
TextField,
|
||||
Typography,
|
||||
useTheme,
|
||||
List,
|
||||
ListItem,
|
||||
IconButton,
|
||||
ListItemAvatar,
|
||||
Avatar,
|
||||
ListItemText,
|
||||
} from '@mui/material';
|
||||
import Iconify from '../../components/Iconify';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { fCurrency } from '../../utils/formatNumber';
|
||||
import MemberSelectDialog from '../../components/dialogs/MemberSelectDialog';
|
||||
import { Add, DeleteOutline } from '@mui/icons-material';
|
||||
|
||||
type Props = {
|
||||
isEdit: boolean;
|
||||
currentClaim?: any;
|
||||
};
|
||||
|
||||
export default function ClaimForm({ isEdit, currentClaim }: Props) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const NewCorporateSchema = Yup.object().shape({
|
||||
name: Yup.string().required('Name is required'),
|
||||
code: Yup.string().required('Corporate Code is required'),
|
||||
active: Yup.boolean().required('Corporate Status is required'),
|
||||
// file: Yup.boolean().required('Corporate Status is required'),
|
||||
});
|
||||
|
||||
const defaultValues = useMemo(
|
||||
() => ({
|
||||
member: currentClaim?.member || {},
|
||||
member_id: currentClaim?.member_id || null,
|
||||
diagnosis_id: currentClaim?.diagnosis_id || null,
|
||||
total_claim: currentClaim?.total_claim || 0,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[currentClaim]
|
||||
);
|
||||
|
||||
const methods = useForm<any>({
|
||||
resolver: yupResolver(NewCorporateSchema),
|
||||
defaultValues,
|
||||
});
|
||||
const {
|
||||
reset,
|
||||
watch,
|
||||
control,
|
||||
setValue,
|
||||
getValues,
|
||||
setError,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting },
|
||||
} = methods;
|
||||
|
||||
const values = watch();
|
||||
|
||||
const [isCheckingLimit, setIsCheckingLimit] = useState(false);
|
||||
const [isEligible, setIsEligible] = useState(false);
|
||||
const [memberBenefits, setMemberBenefits] = useState([]);
|
||||
const [diagnosisOption, setDiagnosisOption] = useState([]);
|
||||
const [isMemberDialogOpen, setIsMemberDialogOpen] = useState(false);
|
||||
const [member, setMember] = useState({})
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
console.log('defaultValues', defaultValues);
|
||||
if (isEdit && currentClaim) {
|
||||
reset(defaultValues);
|
||||
setMember(defaultValues.member)
|
||||
}
|
||||
if (!isEdit) {
|
||||
reset(defaultValues);
|
||||
setMember(defaultValues.member)
|
||||
}
|
||||
}, [isEdit, currentClaim]);
|
||||
|
||||
const fileSelected = (event, type) => {
|
||||
const files = event.target.files;
|
||||
const currentFiles = getValues(`uploaded_files.${type}`) ?? [];
|
||||
|
||||
setValue(`uploaded_files.${type}`, [...currentFiles, ...files]);
|
||||
|
||||
console.log('currentFiles', getValues('uploaded_files'));
|
||||
};
|
||||
|
||||
const memberSelected = (member) => {
|
||||
setMember(member)
|
||||
};
|
||||
|
||||
const checkLimit = async () => {
|
||||
console.log('CHECKING LIMIT');
|
||||
};
|
||||
|
||||
const onSubmit = async (data: any) => {
|
||||
try {
|
||||
if (!isEdit) {
|
||||
const response = await axios.post('/claims', data);
|
||||
} else {
|
||||
const response = await axios.put('/claims/' + currentClaim?.id ?? '', data);
|
||||
}
|
||||
reset();
|
||||
enqueueSnackbar(
|
||||
!isEdit ? 'Organizations Created Successfully!' : 'Organizations Udpated Successfully!',
|
||||
{ variant: 'success' }
|
||||
);
|
||||
navigate('/claims');
|
||||
} catch (error: any) {
|
||||
if (error && error.response.status === 422) {
|
||||
for (const [key, value] of Object.entries(error.response.data.errors)) {
|
||||
setError(key, { message: value[0] });
|
||||
enqueueSnackbar(value[0] ?? 'Failed Processing Request', { variant: 'error' });
|
||||
}
|
||||
} else {
|
||||
enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' });
|
||||
}
|
||||
}
|
||||
|
||||
const ascent = document?.querySelector('ascent');
|
||||
if (ascent != null) {
|
||||
ascent.innerHTML = '';
|
||||
}
|
||||
};
|
||||
|
||||
function generate(files, element: React.ReactElement) {
|
||||
return files.map((value) =>
|
||||
React.cloneElement(element, {
|
||||
key: value,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const headStyle = {};
|
||||
return (
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<Stack spacing={3}>
|
||||
<Typography variant="h6">Member</Typography>
|
||||
|
||||
<Stack spacing={2} direction="row">
|
||||
<Grid item xs={12}>
|
||||
<RHFTextField
|
||||
name="member_id"
|
||||
label="Member"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={member?.name || ''}
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!isEdit) setIsMemberDialogOpen(true);
|
||||
if (isEdit) enqueueSnackbar('Cannot Change Member', { variant: 'error' });
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
{/* <Grid item xs={2}>
|
||||
<Button variant="outlined" fullWidth sx={{ p: 1.8 }} onClick={() => {
|
||||
setIsMemberDialogOpen(true)
|
||||
}}>
|
||||
{member ? 'Change' : 'Search'}
|
||||
</Button>
|
||||
</Grid> */}
|
||||
</Stack>
|
||||
|
||||
{member?.id && (
|
||||
<Stack>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Table border="light-700">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Name
|
||||
</TableCell>
|
||||
<TableCell align="left">{member?.full_name}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
DOB
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{member?.birth_date} ({member?.age + ' years'})
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Marital Status
|
||||
</TableCell>
|
||||
<TableCell align="left">{member?.marital_status}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Record Type
|
||||
</TableCell>
|
||||
<TableCell align="left">{member?.record_type}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Principal ID
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{member?.principal_id} (
|
||||
{member?.relation_with_principal})
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Table border="light-700">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Plan
|
||||
</TableCell>
|
||||
<TableCell align="left">{member?.current_plan?.code}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Active
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{member?.current_plan?.start} -{' '}
|
||||
{member?.current_plan?.end} (Active)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Corporate Limit
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{fCurrency(0)} / {fCurrency(member?.current_plan?.limit_rules)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Plan Usage
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{fCurrency(0)} / {fCurrency(member?.current_plan?.limit_rules)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<Controller
|
||||
name="benefit"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Autocomplete
|
||||
options={memberBenefits}
|
||||
getOptionLabel={(option) =>
|
||||
option ? `#${option.id} (${option.code}) ${option.description}` : ''
|
||||
}
|
||||
value={value || ''}
|
||||
onChange={(event: any, newValue: any) => {
|
||||
setValue('benefit_id', newValue?.id);
|
||||
onChange(newValue);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
name="benefit"
|
||||
{...params}
|
||||
label="Benefit"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
// onKeyPress={(event) => {
|
||||
// if (event.key === 'Enter')
|
||||
// searchDiagnosis(event.target.value)
|
||||
// }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="diagnosis"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Autocomplete
|
||||
options={diagnosisOption}
|
||||
getOptionLabel={(option) => (option ? `(${option.code}) ${option.name}` : '')}
|
||||
value={value || ''}
|
||||
onChange={(event: any, newValue: any) => {
|
||||
setValue('diagnosis_id', newValue?.id);
|
||||
// setValue('diagnosis', newValue)
|
||||
onChange(newValue);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
name="diagnosis"
|
||||
{...params}
|
||||
label="Diagnosis"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
onKeyPress={(event) => {
|
||||
if (event.key === 'Enter') searchDiagnosis(event.target.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{isCheckingLimit && (
|
||||
<Stack
|
||||
sx={{
|
||||
backgroundColor: 'gray',
|
||||
paddingY: 1,
|
||||
paddingX: 1.5,
|
||||
mb: 2,
|
||||
borderRadius: '3-xl',
|
||||
}}
|
||||
>
|
||||
{/* Checking */}
|
||||
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
|
||||
<Iconify
|
||||
icon="bxs:info-circle"
|
||||
width={12}
|
||||
height={13}
|
||||
sx={{ color: '#424242', marginRight: '6px' }}
|
||||
/>
|
||||
<Typography variant="caption" component="span">
|
||||
Please Wait, Checking Eligibilty
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
{false && isCheckingLimit == false && isEligible == null && (
|
||||
<Stack
|
||||
sx={{
|
||||
backgroundColor: 'gray',
|
||||
paddingY: 1,
|
||||
paddingX: 1.5,
|
||||
mb: 2,
|
||||
borderRadius: '3-xl',
|
||||
}}
|
||||
>
|
||||
{/* No Data Selected */}
|
||||
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
|
||||
<Iconify
|
||||
icon="bxs:info-circle"
|
||||
width={12}
|
||||
height={13}
|
||||
sx={{ color: '#424242', marginRight: '6px' }}
|
||||
/>
|
||||
<Typography variant="caption" component="span">
|
||||
Please Select Diagnosis !
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
{!isCheckingLimit && isEligible !== null && isEligible && (
|
||||
<Stack
|
||||
sx={{
|
||||
backgroundColor: '#B2E8E8',
|
||||
paddingY: 1,
|
||||
paddingX: 1.5,
|
||||
mb: 2,
|
||||
borderRadius: '3-xl',
|
||||
}}
|
||||
>
|
||||
{/* Eligible */}
|
||||
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
|
||||
<Iconify
|
||||
icon="bxs:lock-alt"
|
||||
width={12}
|
||||
height={13}
|
||||
sx={{ color: '#424242', marginRight: '6px' }}
|
||||
/>
|
||||
<Typography variant="caption" component="span">
|
||||
Diagnosis is Eligible
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Typography sx={{ typography: 'caption', color: '#637381' }}>
|
||||
125.000.000 / 125.000.000
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
{!isCheckingLimit && isEligible !== null && !isEligible && (
|
||||
<Stack
|
||||
sx={{
|
||||
backgroundColor: '#B2E8E8',
|
||||
paddingY: 1,
|
||||
paddingX: 1.5,
|
||||
mb: 2,
|
||||
borderRadius: '3-xl',
|
||||
}}
|
||||
>
|
||||
{/* Not Eligible */}
|
||||
{/* <Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
|
||||
<Iconify
|
||||
icon="bxs:lock-alt"
|
||||
width={12}
|
||||
height={13}
|
||||
sx={{ color: '#424242', marginRight: '6px' }}
|
||||
/>
|
||||
<Typography variant="caption" component="span">
|
||||
Not Eligible
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Typography sx={{ typography: 'caption', color: '#637381' }}>
|
||||
125.000.000 / 125.000.000
|
||||
</Typography> */}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<RHFTextField type="number" name="total_claim" label="Total Claim" />
|
||||
|
||||
{isEdit && (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6">Documents</Typography>
|
||||
|
||||
<List>
|
||||
{(getValues('uploaded_files.invoice') && getValues('uploaded_files.invoice').length
|
||||
? getValues('uploaded_files.invoice')
|
||||
: []
|
||||
).map((file, index) => (
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteOutline />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
{/* <FileIcon /> */}
|
||||
I
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={file.name} secondary={file.type} />
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
component="label"
|
||||
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
|
||||
>
|
||||
Invoice
|
||||
<input
|
||||
name="invoice"
|
||||
hidden
|
||||
accept="image/*,application/pdf"
|
||||
multiple
|
||||
type="file"
|
||||
onChange={(event) => {
|
||||
fileSelected(event, 'invoice');
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
<List>
|
||||
{(getValues('uploaded_files.prescription') && getValues('uploaded_files.prescription').length
|
||||
? getValues('uploaded_files.prescription')
|
||||
: []
|
||||
).map((file, index) => (
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteOutline />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
{/* <FileIcon /> */}
|
||||
P
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={file.name} secondary={file.type} />
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
component="label"
|
||||
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
|
||||
>
|
||||
Prescription
|
||||
<input
|
||||
name="prescription"
|
||||
hidden
|
||||
accept="image/*,application/pdf"
|
||||
multiple
|
||||
type="file"
|
||||
onChange={(event) => {
|
||||
fileSelected(event, 'prescription');
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<List>
|
||||
{(getValues('uploaded_files.diagnosis') && getValues('uploaded_files.diagnosis').length
|
||||
? getValues('uploaded_files.diagnosis')
|
||||
: []
|
||||
).map((file, index) => (
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteOutline />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
{/* <FileIcon /> */}
|
||||
DR
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={file.name} secondary={file.type} />
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
component="label"
|
||||
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
|
||||
>
|
||||
Doctor Result
|
||||
<input
|
||||
name="invoice"
|
||||
hidden
|
||||
accept="image/*,application/pdf"
|
||||
multiple
|
||||
type="file"
|
||||
onChange={(event) => {
|
||||
fileSelected(event, 'diagnosis');
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
{isEligible === true ? (
|
||||
<LoadingButton
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="contained"
|
||||
color="success"
|
||||
style={{ color: '#ffffff' }}
|
||||
size="large"
|
||||
fullWidth={true}
|
||||
loading={isCheckingLimit}
|
||||
>
|
||||
Create Claim
|
||||
</LoadingButton>
|
||||
) : (
|
||||
<LoadingButton
|
||||
onClick={checkLimit}
|
||||
variant="outlined"
|
||||
size="large"
|
||||
fullWidth={true}
|
||||
loading={isCheckingLimit}
|
||||
>
|
||||
Check Limit
|
||||
</LoadingButton>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
<MemberSelectDialog
|
||||
openDialog={isMemberDialogOpen}
|
||||
setOpenDialog={setIsMemberDialogOpen}
|
||||
onSelect={memberSelected}
|
||||
></MemberSelectDialog>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
@@ -17,12 +17,17 @@ import {
|
||||
ButtonGroup,
|
||||
Link,
|
||||
Chip,
|
||||
TableHead,
|
||||
Grid,
|
||||
} from '@mui/material';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import UploadIcon from '@mui/icons-material/Upload';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
|
||||
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
|
||||
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
||||
// hooks
|
||||
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
@@ -38,12 +43,19 @@ import { enqueueSnackbar } from 'notistack';
|
||||
import { Divider } from '@mui/material';
|
||||
import Iconify from '@/components/Iconify';
|
||||
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
|
||||
import { fDateTimesecond } from '@/utils/formatTime';
|
||||
import { capitalizeFirstLetter } from '@/utils/formatString';
|
||||
import Label from '@/components/Label';
|
||||
import TableMoreMenu from '@/components/table/TableMoreMenu';
|
||||
import { Import } from '@/@types/claims';
|
||||
// import LoadingButton from '@/theme/overrides/LoadingButton';
|
||||
|
||||
export default function List() {
|
||||
const { themeColorPresets } = useSettings();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [importResult, setImportResult] = useState(null);
|
||||
const [importResult, setImportResult] = useState<Import>(null);
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
function SearchInput(props: any) {
|
||||
// SEARCH
|
||||
@@ -75,6 +87,7 @@ export default function List() {
|
||||
fullWidth
|
||||
onChange={handleSearchChange}
|
||||
value={searchText}
|
||||
placeholder='Search Code or Name...'
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
@@ -87,26 +100,174 @@ export default function List() {
|
||||
const createMenu = Boolean(anchorEl);
|
||||
const importForm = useRef<HTMLInputElement>(null);
|
||||
const [currentImportFileName, setCurrentImportFileName] = useState(null);
|
||||
const [importLoading, setImportLoading] = useState(false);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleImportButton = () => {
|
||||
if (importForm?.current) {
|
||||
handleClose();
|
||||
importForm.current ? importForm.current.click() : console.log('No File selected');
|
||||
} else {
|
||||
alert('No file selected');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelImportButton = () => {
|
||||
importForm.current.value = '';
|
||||
importForm.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 (importForm.current?.files.length) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', importForm.current?.files[0]);
|
||||
|
||||
setImportLoading(true);
|
||||
axios
|
||||
.post(`claim-requests/import`, formData)
|
||||
.then((response) => {
|
||||
handleCancelImportButton();
|
||||
loadDataTableData();
|
||||
setImportResult(response.data);
|
||||
// alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows');
|
||||
setImportLoading(false);
|
||||
})
|
||||
.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 = (type :string) => {
|
||||
axios.get('corporates/import-document-example/' + type)
|
||||
.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 handleGetData = (type :string) => {
|
||||
axios.get(`corporates/${corporate_id}/data-plan-benefit`)
|
||||
.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();
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
|
||||
<SearchInput onSearch={applyFilter} />
|
||||
{/* <Button
|
||||
variant="outlined"
|
||||
startIcon={<AddIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={() => {
|
||||
navigate('/claims/create');
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button> */}
|
||||
</Stack>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={importForm}
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleImportChange}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
|
||||
/>
|
||||
{!currentImportFileName && (
|
||||
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
|
||||
<SearchInput onSearch={applyFilter} />
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<UploadIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={handleClick}
|
||||
>
|
||||
Import
|
||||
</Button>
|
||||
<Menu
|
||||
id="import-button"
|
||||
anchorEl={anchorEl}
|
||||
open={createMenu}
|
||||
onClose={handleClose}
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'basic-button',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleImportButton}>Import</MenuItem>
|
||||
<MenuItem onClick={() => {handleGetTemplate('claim-request')}}>Download Template</MenuItem>
|
||||
<MenuItem onClick={() => {handleGetData('data-plan-benefit')}}>Download Claim Request</MenuItem>
|
||||
</Menu>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={() => {
|
||||
navigate('/claim-requests/create');
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{currentImportFileName && (
|
||||
<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>
|
||||
)}
|
||||
{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>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -176,45 +337,46 @@ export default function List() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
|
||||
<TableCell>
|
||||
{/* <TableCell>
|
||||
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
|
||||
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableCell> */ }
|
||||
<TableCell align="left">
|
||||
<Typography
|
||||
onClick={() => {
|
||||
handleShowClaim(row);
|
||||
}}
|
||||
color={themeColorPresets}
|
||||
// onClick={() => {
|
||||
// handleShowClaim(row);
|
||||
// }}
|
||||
>
|
||||
{row.code}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="left">{row.member?.full_name}</TableCell>
|
||||
<TableCell align="left">{row.member?.full_name}</TableCell>
|
||||
<TableCell align="left">{row.submission_date}</TableCell>
|
||||
<TableCell align="left"><Label>{fDateTimesecond(row.submission_date)}</Label></TableCell>
|
||||
<TableCell align="left">{row.service_name}</TableCell>
|
||||
<TableCell align="left">{row.payment_type_name}</TableCell>
|
||||
<TableCell align="right">
|
||||
<Chip label={row.status} />
|
||||
<TableCell align="left">
|
||||
{ row.status == "requested" ?
|
||||
(<Label variant='ghost' color='primary'>{capitalizeFirstLetter(row.status)}</Label>) :
|
||||
(<Label color='success'> {capitalizeFirstLetter(row.status)}</Label>)
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{row.status == 'requested' && (
|
||||
<LoadingButton
|
||||
loading={loadingApprove}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
handleApprove(row);
|
||||
}}
|
||||
>
|
||||
Ajukan Claim <Iconify icon="eva:arrow-forward-outline"></Iconify>
|
||||
</LoadingButton>
|
||||
)}
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => navigate(`/claim-requests/edit/${row.id}`)}>
|
||||
<EditOutlinedIcon />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => navigate ('/claim-requests/detail/'+row.id+'')}>
|
||||
<FindInPageOutlinedIcon />
|
||||
Detail
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</TableCell>
|
||||
{/* <TableCell>
|
||||
|
||||
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
handleShowClaim(row);
|
||||
@@ -236,17 +398,58 @@ export default function List() {
|
||||
>
|
||||
<Box>
|
||||
<Typography fontWeight={600}>Berkas Hasil Penunjang</Typography>
|
||||
{row.files_by_type?.result &&
|
||||
row.files_by_type?.result.map((file, index) => (
|
||||
{/* {row.files_by_type?.claim_kondisi &&
|
||||
row.files_by_type?.claim_kondisi.map((file, index) => (
|
||||
<Stack direction="row" key={index}>
|
||||
<Typography sx={{ marginRight: 2 }}>-</Typography>{' '}
|
||||
<a href={file.url} target="_blank">
|
||||
{file.name}
|
||||
</a>
|
||||
</Stack>
|
||||
))}
|
||||
))} */}
|
||||
|
||||
{!row.files_by_type?.result && <Typography>Tidak ada berkas</Typography>}
|
||||
{row.files_by_type?.claim_kondisi && (
|
||||
<>
|
||||
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Kondisi</Typography>
|
||||
{row.files_by_type?.claim_kondisi.map((file, index) => (
|
||||
|
||||
<Stack direction="row" key={index}>
|
||||
<a href={file.url} target="_blank">
|
||||
{file.name}
|
||||
</a>
|
||||
</Stack>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{row.files_by_type?.claim_diagnosis && (
|
||||
<>
|
||||
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Diagnosa</Typography>
|
||||
{row.files_by_type?.claim_diagnosis.map((file, index) => (
|
||||
|
||||
<Stack direction="row" key={index}>
|
||||
<a href={file.url} target="_blank">
|
||||
{file.name}
|
||||
</a>
|
||||
</Stack>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{row.files_by_type?.claim_result && (
|
||||
<>
|
||||
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Hasil</Typography>
|
||||
{row.files_by_type?.claim_result.map((file, index) => (
|
||||
|
||||
<Stack direction="row" key={index}>
|
||||
<a href={file.url} target="_blank">
|
||||
{file.name}
|
||||
</a>
|
||||
</Stack>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{(!row.files_by_type?.claim_result && !row.files_by_type?.claim_diagnosis && !row.files_by_type?.claim_kondisi)&& <Typography>Tidak ada berkas</Typography>}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
@@ -264,9 +467,9 @@ export default function List() {
|
||||
return (
|
||||
<Table aria-label="collapsible table">
|
||||
{/* ------------------ TABLE HEADER ------------------ */}
|
||||
<TableBody>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left" />
|
||||
{/* <TableCell style={headStyle} align="left" /> */}
|
||||
<TableCell style={headStyle} align="left">
|
||||
Code
|
||||
</TableCell>
|
||||
@@ -274,23 +477,20 @@ export default function List() {
|
||||
Name
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Nomor Polis
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Tanggal Pengajuan
|
||||
Date of Submission
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Service Type
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Claim Type
|
||||
Claim Method
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Status
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="right"></TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</TableHead>
|
||||
{/* ------------------ END TABLE HEADER ------------------ */}
|
||||
|
||||
{/* ------------------ TABLE ROW ------------------ */}
|
||||
@@ -346,23 +546,28 @@ export default function List() {
|
||||
function handleDownloadLog() {}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<ImportForm />
|
||||
<Grid container>
|
||||
<Grid item sm={12}>
|
||||
<ImportForm />
|
||||
</Grid>
|
||||
|
||||
<DataTable
|
||||
isLoading={dataTableIsLoading}
|
||||
lastRequest={0}
|
||||
data={dataTableData}
|
||||
handlePageChange={handlePageChange}
|
||||
TableContent={<TableContent />}
|
||||
/>
|
||||
|
||||
<DialogDetailClaim
|
||||
openDialog={openDialogDetailClaim}
|
||||
setOpenDialog={setOpenDialogDetailClaim}
|
||||
title={{ name: 'Claim Request Detail' }}
|
||||
data={{ claim: currentClaim, isLoading: loadingClaimDetail, handleDownloadLog }}
|
||||
></DialogDetailClaim>
|
||||
</Card>
|
||||
<Grid item sm={12}>
|
||||
<DataTable
|
||||
isLoading={dataTableIsLoading}
|
||||
lastRequest={0}
|
||||
data={dataTableData}
|
||||
handlePageChange={handlePageChange}
|
||||
TableContent={<TableContent />}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item sm={12}>
|
||||
<DialogDetailClaim
|
||||
openDialog={openDialogDetailClaim}
|
||||
setOpenDialog={setOpenDialogDetailClaim}
|
||||
title={{ name: 'Claim Request Detail' }}
|
||||
data={{ claim: currentClaim, isLoading: loadingClaimDetail, handleDownloadLog }}
|
||||
></DialogDetailClaim>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import axios from '@/utils/axios';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { MemberListType } from './Types';
|
||||
import { makeFormData } from '@/utils/jsonToFormData';
|
||||
|
||||
/**
|
||||
* Listing Member
|
||||
*/
|
||||
export const getMemberList = async ( page: number, keyword: string ): Promise<MemberListType[]> => {
|
||||
const response = await axios.get(`/claim-requests/list-member?page=${page}&keyword=${keyword}`)
|
||||
.then((res) =>{
|
||||
return res.data.data.member_list;
|
||||
})
|
||||
.catch((res) => {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add Claim Request
|
||||
*/
|
||||
export const addClaimRequest = async ( data: MemberListType[] ): Promise<boolean> => {
|
||||
// Mapping
|
||||
const formData = new FormData();
|
||||
|
||||
data.map((row, index) => {
|
||||
formData.append(`member_id[${index}]`, row.id.toString());
|
||||
formData.append(`service_code[${index}]`, row.patien_type??'');
|
||||
|
||||
if (row.file_kondisi != undefined) {
|
||||
row.file_kondisi.forEach((file, file_index) => {
|
||||
console.log(file);
|
||||
|
||||
formData.append(`file_kondisi[member_${row.id}][${file_index}]`, file);
|
||||
});
|
||||
}
|
||||
|
||||
if (row.file_diagnosa != undefined) {
|
||||
row.file_diagnosa.forEach((file, file_index) => {
|
||||
console.log(file);
|
||||
|
||||
formData.append(`file_diagnosa[member_${row.id}][${file_index}]`, file);
|
||||
});
|
||||
}
|
||||
|
||||
if (row.file_penunjang != undefined) {
|
||||
row.file_penunjang.forEach((file, file_index) => {
|
||||
console.log(file);
|
||||
|
||||
formData.append(`file_penunjang[member_${row.id}][${file_index}]`, file);
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
// Axios
|
||||
const response = await axios.post(`/claim-requests`, formData)
|
||||
.then((res) =>{
|
||||
enqueueSnackbar("Berhasil membuat data !", {
|
||||
variant: 'success',
|
||||
});
|
||||
|
||||
return true;
|
||||
})
|
||||
.catch((res) => {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
25
frontend/dashboard/src/pages/ClaimRequests/Model/Types.tsx
Normal file
25
frontend/dashboard/src/pages/ClaimRequests/Model/Types.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Search Type
|
||||
*/
|
||||
export type SearchType = {
|
||||
keyword: string,
|
||||
}
|
||||
|
||||
/**
|
||||
* Member List
|
||||
*/
|
||||
export type MemberListType = {
|
||||
id : string,
|
||||
member_id : string,
|
||||
name : string,
|
||||
service_type : ServiceType[],
|
||||
patien_type? : string,
|
||||
file_kondisi? : any[],
|
||||
file_diagnosa? : any[],
|
||||
file_penunjang? : any[],
|
||||
}
|
||||
|
||||
export type ServiceType = {
|
||||
code : string
|
||||
name : string
|
||||
}
|
||||
@@ -28,31 +28,26 @@ export default function ClaimsCreateUpdate() {
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit) {
|
||||
axios.get('/claims/' + id).then((res) => {
|
||||
// console.log('Yeet', res.data);
|
||||
setCurrentClaim(res.data);
|
||||
});
|
||||
axios.get(`/claims/${id}/edit`).then((res) => {
|
||||
console.log('Yeet', res.data.data);
|
||||
setCurrentClaim(res.data.data);
|
||||
});;
|
||||
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
|
||||
return (
|
||||
<Page title={isEdit ? `Edit Claim : ${currentClaim?.id}` : "Create New Claim"}>
|
||||
<Page title={'Edit Claim Management'}>
|
||||
<Container maxWidth={themeStretch ? false : 'xl'}>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<HeaderBreadcrumbs
|
||||
heading={
|
||||
!isEdit
|
||||
? 'Create New Claim'
|
||||
: `Edit Claim : ${currentClaim?.code}`
|
||||
}
|
||||
heading={`Edit Claim Management`}
|
||||
links={[
|
||||
{ name: 'Dashboard', href: '/dashboard' },
|
||||
{
|
||||
name: 'Claim',
|
||||
href: '/claims',
|
||||
name: 'Claim Management',
|
||||
},
|
||||
{ name: !isEdit ? 'Create' : currentClaim?.id ?? '' },
|
||||
]}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
1569
frontend/dashboard/src/pages/Claims/Detail.tsx
Normal file
1569
frontend/dashboard/src/pages/Claims/Detail.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,16 +24,18 @@ import {
|
||||
ListItemAvatar,
|
||||
Avatar,
|
||||
ListItemText,
|
||||
Card,
|
||||
} from '@mui/material';
|
||||
import Iconify from '../../components/Iconify';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { fCurrency } from '../../utils/formatNumber';
|
||||
import MemberSelectDialog from '../../components/dialogs/MemberSelectDialog';
|
||||
import { Add, DeleteOutline } from '@mui/icons-material';
|
||||
import { ClaimsEdit } from '@/@types/claims';
|
||||
|
||||
type Props = {
|
||||
isEdit: boolean;
|
||||
currentClaim?: any;
|
||||
currentClaim?: ClaimsEdit;
|
||||
};
|
||||
|
||||
export default function ClaimForm({ isEdit, currentClaim }: Props) {
|
||||
@@ -42,18 +44,27 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const NewCorporateSchema = Yup.object().shape({
|
||||
name: Yup.string().required('Name is required'),
|
||||
code: Yup.string().required('Corporate Code is required'),
|
||||
active: Yup.boolean().required('Corporate Status is required'),
|
||||
benefit_desc: Yup.string().required('Benefit Desc is required'),
|
||||
amount_incurred: Yup.string().required('Amount Incurred is required'),
|
||||
amount_approved: Yup.number().required('Amount Approved is required'),
|
||||
amount_not_approved: Yup.number().required('Amount Not Approved is required'),
|
||||
excess_paid: Yup.number().required('Excess Paid is required'),
|
||||
// file: Yup.boolean().required('Corporate Status is required'),
|
||||
});
|
||||
|
||||
const defaultValues = useMemo(
|
||||
() => ({
|
||||
member: currentClaim?.member || {},
|
||||
plan_id: currentClaim?.plan_id || null,
|
||||
payor_id: currentClaim?.payor_id || null,
|
||||
corporate_id: currentClaim?.corporate_id || null,
|
||||
policy_number: currentClaim?.policy_number || null,
|
||||
member_id: currentClaim?.member_id || null,
|
||||
diagnosis_id: currentClaim?.diagnosis_id || null,
|
||||
total_claim: currentClaim?.total_claim || 0,
|
||||
benefit_code: currentClaim?.benefit_code || '-',
|
||||
benefit_desc: currentClaim?.benefit_desc || '-',
|
||||
amount_incurred: currentClaim?.amount_incurred || 0,
|
||||
amount_approved: currentClaim?.amount_approved || 0,
|
||||
amount_not_approved: currentClaim?.amount_not_approved || 0,
|
||||
excess_paid: currentClaim?.excess_paid || 0,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[currentClaim]
|
||||
@@ -88,11 +99,11 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
|
||||
console.log('defaultValues', defaultValues);
|
||||
if (isEdit && currentClaim) {
|
||||
reset(defaultValues);
|
||||
setMember(defaultValues.member)
|
||||
// setMember(defaultValues.member)
|
||||
}
|
||||
if (!isEdit) {
|
||||
reset(defaultValues);
|
||||
setMember(defaultValues.member)
|
||||
// setMember(defaultValues.member)
|
||||
}
|
||||
}, [isEdit, currentClaim]);
|
||||
|
||||
@@ -105,13 +116,13 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
|
||||
console.log('currentFiles', getValues('uploaded_files'));
|
||||
};
|
||||
|
||||
const memberSelected = (member) => {
|
||||
setMember(member)
|
||||
};
|
||||
// const memberSelected = (member) => {
|
||||
// setMember(member)
|
||||
// };
|
||||
|
||||
const checkLimit = async () => {
|
||||
console.log('CHECKING LIMIT');
|
||||
};
|
||||
// const checkLimit = async () => {
|
||||
// console.log('CHECKING LIMIT');
|
||||
// };
|
||||
|
||||
const onSubmit = async (data: any) => {
|
||||
try {
|
||||
@@ -122,7 +133,7 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
|
||||
}
|
||||
reset();
|
||||
enqueueSnackbar(
|
||||
!isEdit ? 'Organizations Created Successfully!' : 'Organizations Udpated Successfully!',
|
||||
!isEdit ? 'Organizations Created Successfully!' : 'Claim Udpated Successfully!',
|
||||
{ variant: 'success' }
|
||||
);
|
||||
navigate('/claims');
|
||||
@@ -154,443 +165,86 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
|
||||
const headStyle = {};
|
||||
return (
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<Stack spacing={3}>
|
||||
<Typography variant="h6">Member</Typography>
|
||||
|
||||
<Stack spacing={2} direction="row">
|
||||
<Grid item xs={12}>
|
||||
<RHFTextField
|
||||
name="member_id"
|
||||
label="Member"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={member?.name || ''}
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!isEdit) setIsMemberDialogOpen(true);
|
||||
if (isEdit) enqueueSnackbar('Cannot Change Member', { variant: 'error' });
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
{/* <Grid item xs={2}>
|
||||
<Button variant="outlined" fullWidth sx={{ p: 1.8 }} onClick={() => {
|
||||
setIsMemberDialogOpen(true)
|
||||
}}>
|
||||
{member ? 'Change' : 'Search'}
|
||||
</Button>
|
||||
</Grid> */}
|
||||
</Stack>
|
||||
|
||||
{member?.id && (
|
||||
<Stack>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Table border="light-700">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Name
|
||||
</TableCell>
|
||||
<TableCell align="left">{member?.full_name}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
DOB
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{member?.birth_date} ({member?.age + ' years'})
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Marital Status
|
||||
</TableCell>
|
||||
<TableCell align="left">{member?.marital_status}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Record Type
|
||||
</TableCell>
|
||||
<TableCell align="left">{member?.record_type}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Principal ID
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{member?.principal_id} (
|
||||
{member?.relation_with_principal})
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Table border="light-700">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Plan
|
||||
</TableCell>
|
||||
<TableCell align="left">{member?.current_plan?.code}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Active
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{member?.current_plan?.start} -{' '}
|
||||
{member?.current_plan?.end} (Active)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Corporate Limit
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{fCurrency(0)} / {fCurrency(member?.current_plan?.limit_rules)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Plan Usage
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{fCurrency(0)} / {fCurrency(member?.current_plan?.limit_rules)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Grid>
|
||||
<Card sx={{paddingX:2, paddingY:3}}>
|
||||
<Grid container spacing={2}>
|
||||
{/* Baris ke 1 */}
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Plan ID*</Typography>
|
||||
<RHFTextField name="plan_id" disabled />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Payor ID*</Typography>
|
||||
<RHFTextField name="payor_id" disabled />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Corporate ID*</Typography>
|
||||
<RHFTextField name="corporate_id"disabled />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Policy Number*</Typography>
|
||||
<RHFTextField name="policy_number" disabled />
|
||||
</Grid>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<Controller
|
||||
name="benefit"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Autocomplete
|
||||
options={memberBenefits}
|
||||
getOptionLabel={(option) =>
|
||||
option ? `#${option.id} (${option.code}) ${option.description}` : ''
|
||||
}
|
||||
value={value || ''}
|
||||
onChange={(event: any, newValue: any) => {
|
||||
setValue('benefit_id', newValue?.id);
|
||||
onChange(newValue);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
name="benefit"
|
||||
{...params}
|
||||
label="Benefit"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
// onKeyPress={(event) => {
|
||||
// if (event.key === 'Enter')
|
||||
// searchDiagnosis(event.target.value)
|
||||
// }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* Baris ke 2 */}
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Memeber ID*</Typography>
|
||||
<RHFTextField name="member_id" disabled />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Benefit Code*</Typography>
|
||||
<RHFTextField name="benefit_code" disabled />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Benefit Desc*</Typography>
|
||||
<RHFTextField name="benefit_desc" label="Benefit Desc" />
|
||||
</Grid>
|
||||
|
||||
<Controller
|
||||
name="diagnosis"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Autocomplete
|
||||
options={diagnosisOption}
|
||||
getOptionLabel={(option) => (option ? `(${option.code}) ${option.name}` : '')}
|
||||
value={value || ''}
|
||||
onChange={(event: any, newValue: any) => {
|
||||
setValue('diagnosis_id', newValue?.id);
|
||||
// setValue('diagnosis', newValue)
|
||||
onChange(newValue);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
name="diagnosis"
|
||||
{...params}
|
||||
label="Diagnosis"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
onKeyPress={(event) => {
|
||||
if (event.key === 'Enter') searchDiagnosis(event.target.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{isCheckingLimit && (
|
||||
<Stack
|
||||
sx={{
|
||||
backgroundColor: 'gray',
|
||||
paddingY: 1,
|
||||
paddingX: 1.5,
|
||||
mb: 2,
|
||||
borderRadius: '3-xl',
|
||||
}}
|
||||
>
|
||||
{/* Checking */}
|
||||
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
|
||||
<Iconify
|
||||
icon="bxs:info-circle"
|
||||
width={12}
|
||||
height={13}
|
||||
sx={{ color: '#424242', marginRight: '6px' }}
|
||||
/>
|
||||
<Typography variant="caption" component="span">
|
||||
Please Wait, Checking Eligibilty
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
{false && isCheckingLimit == false && isEligible == null && (
|
||||
<Stack
|
||||
sx={{
|
||||
backgroundColor: 'gray',
|
||||
paddingY: 1,
|
||||
paddingX: 1.5,
|
||||
mb: 2,
|
||||
borderRadius: '3-xl',
|
||||
}}
|
||||
>
|
||||
{/* No Data Selected */}
|
||||
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
|
||||
<Iconify
|
||||
icon="bxs:info-circle"
|
||||
width={12}
|
||||
height={13}
|
||||
sx={{ color: '#424242', marginRight: '6px' }}
|
||||
/>
|
||||
<Typography variant="caption" component="span">
|
||||
Please Select Diagnosis !
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
{!isCheckingLimit && isEligible !== null && isEligible && (
|
||||
<Stack
|
||||
sx={{
|
||||
backgroundColor: '#B2E8E8',
|
||||
paddingY: 1,
|
||||
paddingX: 1.5,
|
||||
mb: 2,
|
||||
borderRadius: '3-xl',
|
||||
}}
|
||||
>
|
||||
{/* Eligible */}
|
||||
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
|
||||
<Iconify
|
||||
icon="bxs:lock-alt"
|
||||
width={12}
|
||||
height={13}
|
||||
sx={{ color: '#424242', marginRight: '6px' }}
|
||||
/>
|
||||
<Typography variant="caption" component="span">
|
||||
Diagnosis is Eligible
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Typography sx={{ typography: 'caption', color: '#637381' }}>
|
||||
125.000.000 / 125.000.000
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
{!isCheckingLimit && isEligible !== null && !isEligible && (
|
||||
<Stack
|
||||
sx={{
|
||||
backgroundColor: '#B2E8E8',
|
||||
paddingY: 1,
|
||||
paddingX: 1.5,
|
||||
mb: 2,
|
||||
borderRadius: '3-xl',
|
||||
}}
|
||||
>
|
||||
{/* Not Eligible */}
|
||||
{/* <Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
|
||||
<Iconify
|
||||
icon="bxs:lock-alt"
|
||||
width={12}
|
||||
height={13}
|
||||
sx={{ color: '#424242', marginRight: '6px' }}
|
||||
/>
|
||||
<Typography variant="caption" component="span">
|
||||
Not Eligible
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Typography sx={{ typography: 'caption', color: '#637381' }}>
|
||||
125.000.000 / 125.000.000
|
||||
</Typography> */}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<RHFTextField type="number" name="total_claim" label="Total Claim" />
|
||||
|
||||
{isEdit && (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6">Documents</Typography>
|
||||
|
||||
<List>
|
||||
{(getValues('uploaded_files.invoice') && getValues('uploaded_files.invoice').length
|
||||
? getValues('uploaded_files.invoice')
|
||||
: []
|
||||
).map((file, index) => (
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteOutline />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
{/* <FileIcon /> */}
|
||||
I
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={file.name} secondary={file.type} />
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
component="label"
|
||||
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
|
||||
>
|
||||
Invoice
|
||||
<input
|
||||
name="invoice"
|
||||
hidden
|
||||
accept="image/*,application/pdf"
|
||||
multiple
|
||||
type="file"
|
||||
onChange={(event) => {
|
||||
fileSelected(event, 'invoice');
|
||||
{/* Baris ke 3 */}
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Amount Incurred*</Typography>
|
||||
<RHFTextField name="amount_incurred" label="Amount Incurred" type="number" />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Amount Approved*</Typography>
|
||||
<RHFTextField name="amount_approved" label="Amount Approved" type="number" />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Amount Not Approved*</Typography>
|
||||
<RHFTextField name="amount_not_approved" label="Amount Not Approved" type="number" />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography marginBottom={2} variant="subtitle1">Excess Paid*</Typography>
|
||||
<RHFTextField name="excess_paid" label="Excess Paid*" type="number" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
<Grid container marginTop={2}>
|
||||
<Grid item xs={12} md={12} >
|
||||
<Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button
|
||||
sx={{
|
||||
margin: 1
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
<List>
|
||||
{(getValues('uploaded_files.prescription') && getValues('uploaded_files.prescription').length
|
||||
? getValues('uploaded_files.prescription')
|
||||
: []
|
||||
).map((file, index) => (
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteOutline />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
{/* <FileIcon /> */}
|
||||
P
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={file.name} secondary={file.type} />
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
component="label"
|
||||
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
|
||||
>
|
||||
Prescription
|
||||
<input
|
||||
name="prescription"
|
||||
hidden
|
||||
accept="image/*,application/pdf"
|
||||
multiple
|
||||
type="file"
|
||||
onChange={(event) => {
|
||||
fileSelected(event, 'prescription');
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<List>
|
||||
{(getValues('uploaded_files.diagnosis') && getValues('uploaded_files.diagnosis').length
|
||||
? getValues('uploaded_files.diagnosis')
|
||||
: []
|
||||
).map((file, index) => (
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteOutline />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
{/* <FileIcon /> */}
|
||||
DR
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={file.name} secondary={file.type} />
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
component="label"
|
||||
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
|
||||
>
|
||||
Doctor Result
|
||||
<input
|
||||
name="invoice"
|
||||
hidden
|
||||
accept="image/*,application/pdf"
|
||||
multiple
|
||||
type="file"
|
||||
onChange={(event) => {
|
||||
fileSelected(event, 'diagnosis');
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
{isEligible === true ? (
|
||||
<LoadingButton
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="contained"
|
||||
color="success"
|
||||
style={{ color: '#ffffff' }}
|
||||
size="large"
|
||||
fullWidth={true}
|
||||
loading={isCheckingLimit}
|
||||
>
|
||||
Create Claim
|
||||
</LoadingButton>
|
||||
) : (
|
||||
<LoadingButton
|
||||
onClick={checkLimit}
|
||||
variant="outlined"
|
||||
size="large"
|
||||
fullWidth={true}
|
||||
loading={isCheckingLimit}
|
||||
>
|
||||
Check Limit
|
||||
</LoadingButton>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
<MemberSelectDialog
|
||||
openDialog={isMemberDialogOpen}
|
||||
setOpenDialog={setIsMemberDialogOpen}
|
||||
onSelect={memberSelected}
|
||||
></MemberSelectDialog>
|
||||
</FormProvider>
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
color='inherit'
|
||||
onClick={() => navigate(`/claims`)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<LoadingButton
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
// fullWidth={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
Save
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,13 +16,10 @@ import {
|
||||
Menu,
|
||||
ButtonGroup,
|
||||
Tooltip,
|
||||
TableHead,
|
||||
} from '@mui/material';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
|
||||
import AssessmentIcon from '@mui/icons-material/Assessment';
|
||||
import UploadIcon from '@mui/icons-material/Upload';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
// hooks
|
||||
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
@@ -35,6 +32,13 @@ 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 { 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';
|
||||
|
||||
export default function List() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
@@ -83,10 +87,16 @@ export default function List() {
|
||||
fullWidth
|
||||
onChange={handleSearchChange}
|
||||
value={searchText}
|
||||
placeholder='Search Code or Member ID...'
|
||||
/>
|
||||
<Tooltip title="Benefit Usage Report">
|
||||
<Button variant="outlined" startIcon={<AssessmentIcon />} sx={{ p: 1.8 }} onClick={handleGetData}/>
|
||||
</Tooltip>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<Download />}
|
||||
onClick={() => handleGetData('DO')}
|
||||
sx={{ p: 1.8 }}
|
||||
>
|
||||
Export
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
@@ -152,7 +162,7 @@ export default function List() {
|
||||
};
|
||||
|
||||
// Called on every row to map the data to the columns
|
||||
function createData(data: any): any {
|
||||
function createData(data: Claims): Claims {
|
||||
return {
|
||||
...data,
|
||||
};
|
||||
@@ -168,41 +178,44 @@ export default function List() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
|
||||
<TableCell>
|
||||
{/* <TableCell>
|
||||
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
|
||||
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
<TableCell align="left">{row.code}</TableCell>
|
||||
<TableCell align="left">{row.member?.full_name}</TableCell>
|
||||
<TableCell align="left">{row.plan?.code}</TableCell>
|
||||
<TableCell align="left">{row.claim_request?.service?.name}</TableCell>
|
||||
<TableCell align="left">
|
||||
({row.diagnoses[0]?.icd?.code}) {row.diagnoses[0]?.icd?.name}
|
||||
</TableCell>
|
||||
<TableCell align="left">{fCurrency(row.total_claim)}</TableCell>
|
||||
</TableCell> */}
|
||||
<TableCell align="left">{row.claim_request?.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">
|
||||
{row.status == 'draft' && (<Chip label='Draft' color="default" variant="outlined" />)}
|
||||
{row.status == 'requested' && (<Chip label='Requested' color="primary" />)}
|
||||
{row.status == 'received' && (<Chip label='Received' color="success" variant='outlined' />)}
|
||||
{row.status == 'approved' && (<Chip label='Approved' color="success" />)}
|
||||
{row.status == 'postpone' && (<Chip label='Postpone' color="primary" variant="outlined" />)}
|
||||
{row.status == 'paid' && (<Chip label='Paid' color="warning" />)}
|
||||
{row.status == 'declined' && (<Chip label='Declined' color="error" />)}
|
||||
{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>)}
|
||||
{row.status == 'approved' && (<Label color='success' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
{row.status == 'postpone' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
{row.status == 'paid' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
{row.status == 'declined' && (<Label color='error' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="right">
|
||||
{['approved', 'paid'].includes(row.status) && (
|
||||
<Iconify icon="eva:eye-fill" onClick={(e) => {
|
||||
navigate('/claims/' + row.id);
|
||||
}}></Iconify>
|
||||
)}
|
||||
{!['approved', 'paid'].includes(row.status) && (
|
||||
<Iconify icon="eva:edit-outline" onClick={(e) => {
|
||||
navigate('/claims/' + row.id);
|
||||
}}></Iconify>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() =>navigate(`/claims/edit/${row.id}`) }>
|
||||
<Edit />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => navigate('/claims/detail/'+row.id+'') }>
|
||||
<FindInPageOutlinedIcon />
|
||||
Detail
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
|
||||
|
||||
</TableRow>
|
||||
{/* COLLAPSIBLE ROW */}
|
||||
<TableRow>
|
||||
@@ -227,35 +240,38 @@ export default function List() {
|
||||
return (
|
||||
<Table aria-label="collapsible table">
|
||||
{/* ------------------ TABLE HEADER ------------------ */}
|
||||
<TableBody>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left" />
|
||||
{/* <TableCell style={headStyle} align="left" /> */}
|
||||
<TableCell style={headStyle} align="left">
|
||||
Code
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Member Name
|
||||
Plan ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Plan
|
||||
Payor ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Benefit
|
||||
Corporate ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Diagnosis
|
||||
Policy Number
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Total Claim
|
||||
Member ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Benefit Desc
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Status
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="right">
|
||||
Action
|
||||
<TableCell style={headStyle} align="left">
|
||||
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</TableHead>
|
||||
{/* ------------------ END TABLE HEADER ------------------ */}
|
||||
|
||||
{/* ------------------ TABLE ROW ------------------ */}
|
||||
|
||||
58
frontend/dashboard/src/pages/Claims/Model/Functions.tsx
Normal file
58
frontend/dashboard/src/pages/Claims/Model/Functions.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import axios from "@/utils/axios";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { BenefitConfigurationListType } from "./Types";
|
||||
|
||||
/**
|
||||
* Get Benefit Configuration List
|
||||
*/
|
||||
export const getBenefitConfigurationList = async ( claim_id: string ): Promise<BenefitConfigurationListType[]> => {
|
||||
const response = await axios.get(`/claims/${claim_id}/benefit-configuration`)
|
||||
.then((res) =>{
|
||||
return res.data.data.benefit_list;
|
||||
})
|
||||
.catch((res) => {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* Edit Benefit Configuration
|
||||
*/
|
||||
export const editBenefitConfiguration = async ( data: BenefitConfigurationListType ): Promise<boolean> => {
|
||||
const response = await axios.put(`/claims/benefit-configuration/edit/${data.claim_service_benefits_id}`, {
|
||||
...data
|
||||
})
|
||||
.then((res) =>{
|
||||
enqueueSnackbar(res.data.message, {
|
||||
variant: 'success',
|
||||
});
|
||||
|
||||
return true;
|
||||
})
|
||||
.catch((res) => {
|
||||
if (res.response.status == 400) {
|
||||
let arr_message = res.response.data.message;
|
||||
|
||||
for (const key in arr_message) {
|
||||
enqueueSnackbar(arr_message[key][0], {
|
||||
variant: 'warning',
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
enqueueSnackbar("server error !", {
|
||||
variant: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
11
frontend/dashboard/src/pages/Claims/Model/Types.tsx
Normal file
11
frontend/dashboard/src/pages/Claims/Model/Types.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Benefit Configuration List Type
|
||||
*/
|
||||
export type BenefitConfigurationListType = {
|
||||
claim_service_benefits_id: number,
|
||||
benefit_name: string,
|
||||
amount_incurred: number,
|
||||
amount_approved: number,
|
||||
amount_not_approved: number,
|
||||
excess_paid: number
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import ClaimDetail from './components/ClaimDetail';
|
||||
import DialogHistoryPerawatan from './components/DialogHistoryPerawatan';
|
||||
import { IconButton } from '@mui/material';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
export default function ClaimsCreateUpdate() {
|
||||
const { themeStretch } = useSettings();
|
||||
@@ -48,6 +49,10 @@ export default function ClaimsCreateUpdate() {
|
||||
const [currentClaim, setCurrentClaim] = useState();
|
||||
const [documents, setDocuments] = useState([]);
|
||||
|
||||
const claimsHistoryData = useSelector((state) => state.claimsHistory)
|
||||
|
||||
console.log(claimsHistoryData)
|
||||
|
||||
const Item = styled(Paper)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
|
||||
...theme.typography.body2,
|
||||
@@ -66,6 +71,12 @@ export default function ClaimsCreateUpdate() {
|
||||
setClaimItems([...claimItems, ...items]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(claimsHistoryData) {
|
||||
setClaimItems(claimsHistoryData)
|
||||
}
|
||||
},[claimsHistoryData])
|
||||
|
||||
const handleSaveClaimItems = () => {
|
||||
console.log('Storing ', claimItems);
|
||||
setLoadingClaimItems(true);
|
||||
@@ -305,6 +316,9 @@ export default function ClaimsCreateUpdate() {
|
||||
</LoadingButton>
|
||||
)}
|
||||
</Stack>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography>Admission Date : {currentClaim?.claim_request?.submission_date}</Typography>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
@@ -347,7 +361,7 @@ export default function ClaimsCreateUpdate() {
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
Nomor Polis
|
||||
</Typography>
|
||||
<Typography variant="body2">{currentClaim?.member?.full_name}</Typography>
|
||||
<Typography variant="body2">{currentClaim?.member?.current_policy?.code}</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
import * as Yup from 'yup';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Box, Grid } from '@mui/material';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import Button from '@mui/material/Button';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import Dialog from '@mui/material/Dialog';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { FormProvider } from '@/components/hook-form';
|
||||
import RHFTextFieldMoney from '@/components/hook-form/v2/RHFTextFieldMoney';
|
||||
|
||||
/**
|
||||
* Custom Style
|
||||
* =====================================================
|
||||
*/
|
||||
const BootstrapDialog = styled(Dialog)(({ theme }) => ({
|
||||
|
||||
}));
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import palette from '@/theme/palette';
|
||||
import { BenefitConfigurationListType } from '../Model/Types';
|
||||
import { editBenefitConfiguration } from '../Model/Functions';
|
||||
|
||||
/**
|
||||
* Props
|
||||
* =====================================================
|
||||
*/
|
||||
type Props = {
|
||||
data?: BenefitConfigurationListType,
|
||||
isOpen: boolean,
|
||||
handleCancleProp: () => void,
|
||||
handleSuccessProp: () => void,
|
||||
};
|
||||
|
||||
export default function BenefitConfigurationDialog({ ...props }: Props) {
|
||||
|
||||
// setup form
|
||||
// ====================================
|
||||
const defaultValues: BenefitConfigurationListType = {
|
||||
claim_service_benefits_id: 0,
|
||||
benefit_name: '',
|
||||
amount_incurred: 0,
|
||||
amount_approved: 0,
|
||||
amount_not_approved: 0,
|
||||
excess_paid: 0,
|
||||
};
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
amount_incurred : Yup.string().typeError('').required(''),
|
||||
amount_approved : Yup.string().typeError('').required(''),
|
||||
amount_not_approved : Yup.string().typeError('').required(''),
|
||||
excess_paid : Yup.string().typeError('').required(''),
|
||||
});
|
||||
|
||||
const methods = useForm<any>({
|
||||
resolver: yupResolver(validationSchema),
|
||||
defaultValues
|
||||
});
|
||||
|
||||
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods;
|
||||
|
||||
console.log(errors);
|
||||
console.log(watch());
|
||||
|
||||
// Submit Form
|
||||
// =====================================
|
||||
const submitHandler = async (data: BenefitConfigurationListType) => {
|
||||
let response = await editBenefitConfiguration(data);
|
||||
|
||||
if (response == true) {
|
||||
props.handleSuccessProp()
|
||||
props.handleCancleProp()
|
||||
}
|
||||
}
|
||||
|
||||
// Set Value Form
|
||||
// =====================================
|
||||
useEffect(() => {
|
||||
setValue('claim_service_benefits_id', props.data?.claim_service_benefits_id)
|
||||
setValue('amount_incurred', props.data?.amount_incurred)
|
||||
setValue('amount_approved', props.data?.amount_approved)
|
||||
setValue('amount_not_approved', props.data?.amount_not_approved)
|
||||
setValue('excess_paid', props.data?.excess_paid)
|
||||
}, [props.data])
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
onClose={props.handleCancleProp}
|
||||
aria-labelledby="customized-dialog-title"
|
||||
open={props.isOpen}
|
||||
maxWidth={'md'}
|
||||
>
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(submitHandler)}>
|
||||
<DialogTitle sx={{ m: 0, p: 2, background: palette.light.primary.main, color: palette.light.grey[0], display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} id="customized-dialog-title">
|
||||
<Typography variant="body2" sx={{ fontWeight: 'bold' }}>
|
||||
Client Benefit Configuration
|
||||
</Typography>
|
||||
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
onClick={props.handleCancleProp}
|
||||
sx={{color: (theme) => theme.palette.grey[0]}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent sx={{ p: '0px' }}>
|
||||
<Box sx={{ py: '24px', px: '32px'}}>
|
||||
<Box sx={{ p: '24px', border: '1px solid rgba(0,0,0,0.125)', borderRadius: '12px'}}>
|
||||
<Grid container spacing={3}>
|
||||
{/* Benefit Name */}
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" sx={{ fontWeight: 'bold'}}>
|
||||
{props.data?.benefit_name}
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={3}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" component="div">
|
||||
Amount Incurred*
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFTextFieldMoney
|
||||
id="amount_incurred"
|
||||
name='amount_incurred'
|
||||
placeholder='Amount Incurred'
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={3}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" component="div">
|
||||
Amount Approved*
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFTextFieldMoney
|
||||
id="amount_approved"
|
||||
name='amount_approved'
|
||||
placeholder='Amount Approved'
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={3}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" component="div">
|
||||
Amount Not Approved*
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFTextFieldMoney
|
||||
id="amount_not_approved"
|
||||
name='amount_not_approved'
|
||||
placeholder='Amount Not Approved'
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={3}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" component="div">
|
||||
Excess Paid*
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
|
||||
<RHFTextFieldMoney
|
||||
id="excess_paid"
|
||||
name='excess_paid'
|
||||
placeholder='Excess Paid'
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button variant='outlined' onClick={props.handleCancleProp} aria-label="close">
|
||||
Cancle
|
||||
</Button>
|
||||
<LoadingButton disabled={!isDirty} type="submit" variant="contained" loading={isSubmitting}>
|
||||
Save Changes
|
||||
</LoadingButton>
|
||||
</DialogActions>
|
||||
</FormProvider>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Core
|
||||
* ============================================
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import { Box, Typography, Grid, MenuItem } from '@mui/material';
|
||||
|
||||
/**
|
||||
* Components
|
||||
* ============================================
|
||||
*/
|
||||
// - Global -
|
||||
import MoreMenu from '@/components/MoreMenu';
|
||||
|
||||
/**
|
||||
* Icon
|
||||
* ============================================
|
||||
*/
|
||||
import { EditOutlined } from '@mui/icons-material';
|
||||
|
||||
/**
|
||||
* Utils, Types, Functions
|
||||
* ============================================
|
||||
*/
|
||||
import { BenefitConfigurationListType } from '../Model/Types';
|
||||
import { getBenefitConfigurationList } from '../Model/Functions';
|
||||
import palette from '@/theme/palette';
|
||||
import BenefitConfigurationDialog from './BenefitConfigurationDialog';
|
||||
import { fNumber } from '@/utils/formatNumber';
|
||||
|
||||
export default function BenefitConfigurationList() {
|
||||
const { id: claim_id } = useParams();
|
||||
|
||||
// State
|
||||
// --------------------
|
||||
const [BenefitConfigurationList, setBenefitConfigurationList] = useState<BenefitConfigurationListType[]>();
|
||||
const [BenefitConfigurationData, setBenefitConfigurationData] = useState<BenefitConfigurationListType>();
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false);
|
||||
|
||||
// Use Effect
|
||||
// --------------------
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, [])
|
||||
|
||||
// Load Data
|
||||
// -------------------
|
||||
const loadDataTableData = async () => {
|
||||
const response = await getBenefitConfigurationList(claim_id??'');
|
||||
|
||||
setBenefitConfigurationList(response);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* row list */}
|
||||
{
|
||||
BenefitConfigurationList?.map((row, index) => {
|
||||
return (
|
||||
<Box key={index} sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '24px', py: '20px', marginBottom: '24px', borderRadius: '12px'}}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" sx={{ fontWeight: 'bold'}}>
|
||||
{row.benefit_name}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
|
||||
<MoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => {
|
||||
setIsDialogOpen(true)
|
||||
setBenefitConfigurationData(row)
|
||||
}}
|
||||
>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ py: '8px', px: '12px', background: palette.light.grey[50012], borderRadius: '6px'}}>
|
||||
<Grid container spacing={1}>
|
||||
|
||||
{/* Amount Incurred */}
|
||||
<Grid item xs={3}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
Amount Incurred
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
{fNumber(row.amount_incurred)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Amount Approved */}
|
||||
<Grid item xs={3}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
Amount Approved
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
{fNumber(row.amount_approved)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Amount Not Approved */}
|
||||
<Grid item xs={3}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
Amount Not Approved
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
{fNumber(row.amount_not_approved)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Excess Paid* */}
|
||||
<Grid item xs={3}>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
Excess Paid*
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
{fNumber(row.excess_paid)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
{/* Dialog */}
|
||||
<BenefitConfigurationDialog data={BenefitConfigurationData} isOpen={isDialogOpen} handleCancleProp={() => setIsDialogOpen(false)} handleSuccessProp={() => loadDataTableData()} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import { Stack } from '@mui/material';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import React, { useState } from 'react';
|
||||
import FormHistoryPerawatan from './FormHistoryPerawatan';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { claimsHistoryAction } from '@/store/claimsHistorySlice';
|
||||
|
||||
type DialogHistoryPerawatanType = {
|
||||
openDialog: boolean;
|
||||
@@ -18,6 +20,7 @@ type DialogHistoryPerawatanType = {
|
||||
export default function DialogHistoryPerawatan({ openDialog, setOpenDialog, onSubmit, claim, encounter } : DialogHistoryPerawatanType) {
|
||||
|
||||
const isEdit = encounter?.id != null
|
||||
const dispatch = useDispatch()
|
||||
// const benefits = member?.current_plan?.benefits ?? [];
|
||||
// const [selectedBenefits, setSelectedBenefits] = useState([]);
|
||||
|
||||
@@ -38,6 +41,10 @@ export default function DialogHistoryPerawatan({ openDialog, setOpenDialog, onSu
|
||||
.then((res) => {
|
||||
enqueueSnackbar(res.data.message, {variant: 'success'})
|
||||
setOpenDialog(false);
|
||||
window.location.reload(); // tolong benerin ya
|
||||
// axios.get(`claims/${claim.id}`).then((res) => {
|
||||
// dispatch(claimsHistoryAction.setClaims(res.data.data.encounter))
|
||||
// })
|
||||
})
|
||||
.catch((err) => {
|
||||
enqueueSnackbar(err.message, {variant: 'error'})
|
||||
@@ -47,6 +54,10 @@ export default function DialogHistoryPerawatan({ openDialog, setOpenDialog, onSu
|
||||
.then((res) => {
|
||||
enqueueSnackbar(res.data.message, {variant: 'success'})
|
||||
setOpenDialog(false);
|
||||
window.location.reload(); // tolong benerin ya
|
||||
// axios.get(`claims/${claim.id}`).then((res) => {
|
||||
// dispatch(claimsHistoryAction.setClaims(res.data.data.encounter))
|
||||
// })
|
||||
})
|
||||
.catch((err) => {
|
||||
enqueueSnackbar(err.message, {variant: 'error'})
|
||||
|
||||
@@ -97,9 +97,6 @@ export default function Documents({ files }) {
|
||||
<Stack sx={{px: 2}}>
|
||||
<Typography variant="body2">Dokumen Diagnosa</Typography>
|
||||
</Stack>
|
||||
<Stack sx={{px: 2}}>
|
||||
<Typography variant="body2">Dokumen Diagnosa</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
|
||||
|
||||
@@ -2,21 +2,43 @@ import * as Yup from 'yup';
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { Card, Collapse, Divider, Grid, Stack, Typography } from "@mui/material";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useNavigate, useParams } 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 CorporateTabNavigations from "../CorporateTabNavigations";
|
||||
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
|
||||
import DivisionsList from "./List";
|
||||
import { useMemo, useState } from 'react';
|
||||
import FormEdit from "./Form";
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Benefit } from '@/@types/corporates';
|
||||
import axios from '@/utils/axios';
|
||||
|
||||
|
||||
|
||||
export default function Divisions() {
|
||||
const { themeStretch } = useSettings();
|
||||
|
||||
const { corporate_id } = useParams();
|
||||
const { corporate_id, benefit_id } = useParams();
|
||||
const [ currentCorporateBenefit, setCurrentCorporateBenefit ] = useState<Benefit>();
|
||||
const navigate = useNavigate();
|
||||
const isEdit = !!benefit_id;
|
||||
useEffect(() => {
|
||||
console.log(benefit_id);
|
||||
if (isEdit) {
|
||||
axios.get('/corporates/'+corporate_id+'/corporate-benefits/'+benefit_id+'/edit')
|
||||
.then((res) => {
|
||||
setCurrentCorporateBenefit(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.response.status === 404) {
|
||||
navigate('/404');
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [corporate_id, benefit_id]);
|
||||
|
||||
|
||||
const NewDivisionSchema = Yup.object().shape({
|
||||
name: Yup.string().required('Name is required'),
|
||||
@@ -51,97 +73,12 @@ export default function Divisions() {
|
||||
console.log(data);
|
||||
};
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const benefits = [
|
||||
{
|
||||
'category' : 'General Practitioner',
|
||||
'childs' : [
|
||||
{
|
||||
'name' : 'External Doctor Online',
|
||||
'code' : 'gp-external-doctor-online'
|
||||
},
|
||||
{
|
||||
'name' : 'External Doctor Offline',
|
||||
'code' : 'gp-external-doctor-offline'
|
||||
},
|
||||
{
|
||||
'name' : 'Internal Doctor Online',
|
||||
'code' : 'gp-internal-doctor-online'
|
||||
},
|
||||
{
|
||||
'name' : 'Internal Doctor Offline',
|
||||
'code' : 'gp-internal-doctor-offline'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'category' : 'Specialist',
|
||||
'childs' : [
|
||||
{
|
||||
'name' : 'External Doctor Online',
|
||||
'code' : 'sp-external-doctor-online'
|
||||
},
|
||||
{
|
||||
'name' : 'External Doctor Offline',
|
||||
'code' : 'sp-external-doctor-offline'
|
||||
},
|
||||
{
|
||||
'name' : 'Internal Doctor Online',
|
||||
'code' : 'sp-internal-doctor-online'
|
||||
},
|
||||
{
|
||||
'name' : 'Internal Doctor Offline',
|
||||
'code' : 'sp-internal-doctor-offline'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'category' : 'Medicines',
|
||||
'childs' : [
|
||||
{
|
||||
'name' : 'Vitamins',
|
||||
'code' : 'medicines-vitamins'
|
||||
},
|
||||
{
|
||||
'name' : 'Delivery Fee',
|
||||
'code' : 'medicines-delivery-fee'
|
||||
},
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
const products = [
|
||||
{
|
||||
'name' : 'Inpatient',
|
||||
'code' : 'IP',
|
||||
},
|
||||
{
|
||||
'name' : 'Outpatient',
|
||||
'code' : 'OP',
|
||||
},
|
||||
{
|
||||
'name' : 'Dental',
|
||||
'code' : 'DT',
|
||||
},
|
||||
{
|
||||
'name' : 'Dental',
|
||||
'code' : 'DTL',
|
||||
},
|
||||
{
|
||||
'name' : 'Matternity',
|
||||
'code' : 'MT',
|
||||
},
|
||||
{
|
||||
'name' : 'Special Benefit',
|
||||
'code' : 'SB',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Page title="Create Benefit">
|
||||
|
||||
<HeaderBreadcrumbs
|
||||
{/* <HeaderBreadcrumbs
|
||||
heading={'Create Benefit'}
|
||||
links={[
|
||||
{ name: 'Dashboard', href: '/dashboard' },
|
||||
@@ -162,9 +99,9 @@ export default function Divisions() {
|
||||
href: '/corporates/'+id+'/benefits/create',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
|
||||
{/*
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Card sx={{ p: 2 }}>
|
||||
@@ -235,7 +172,17 @@ export default function Divisions() {
|
||||
</FormProvider>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid> */}
|
||||
<Stack direction="row" alignItems="center">
|
||||
<ArrowBackIosIcon
|
||||
onClick={() => navigate(`/corporates/${corporate_id}/benefit`)}
|
||||
sx={{ cursor: 'pointer' }}
|
||||
/>
|
||||
<Typography variant="h5" sx={{ marginRight:2, flexGrow: 1 }}>
|
||||
Edit Plan
|
||||
</Typography>
|
||||
</Stack>
|
||||
<FormEdit isEdit={true} currentCorporateBenefit={currentCorporateBenefit}/>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as Yup from 'yup';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { Box, Card, Grid, Stack, Typography } from '@mui/material';
|
||||
import { CorporatePlan } from '../../../@types/corporates';
|
||||
import { Box, Button, Card, Grid, Stack, Typography } from '@mui/material';
|
||||
import { Benefit } from '../../../@types/corporates';
|
||||
import { FormProvider, RHFSwitch, RHFTextField } from '../../../components/hook-form';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
@@ -12,39 +12,36 @@ import axios from '../../../utils/axios';
|
||||
|
||||
type Props = {
|
||||
isEdit: boolean;
|
||||
currentCorporatePlan?: CorporatePlan;
|
||||
currentCorporateBenefit?: Benefit;
|
||||
};
|
||||
|
||||
export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Props) {
|
||||
export default function CorporateBenefitForm({ isEdit, currentCorporateBenefit }: Props) {
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const navigate = useNavigate();
|
||||
const { corporate_id } = useParams();
|
||||
const { corporate_id, benefit_id } = useParams();
|
||||
|
||||
const NewCorporatePlanSchema = Yup.object().shape({
|
||||
name: Yup.string().required('Name is required'),
|
||||
code: Yup.string().required('Corporate Code is required'),
|
||||
const NewCorporateBenefitSchema = Yup.object().shape({
|
||||
budget: Yup.string().required('Budget is required'),
|
||||
});
|
||||
|
||||
const defaultValues = useMemo(
|
||||
() => ({
|
||||
name: currentCorporatePlan?.name || '',
|
||||
code: currentCorporatePlan?.code || '',
|
||||
active: currentCorporatePlan?.active === 1 ? true : false,
|
||||
budget: currentCorporateBenefit?.budget || '',
|
||||
}),
|
||||
[currentCorporatePlan]
|
||||
[currentCorporateBenefit]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit && currentCorporatePlan) {
|
||||
if (isEdit && currentCorporateBenefit) {
|
||||
reset(defaultValues);
|
||||
}
|
||||
if (!isEdit) {
|
||||
reset(defaultValues);
|
||||
}
|
||||
}, [isEdit, currentCorporatePlan]);
|
||||
}, [isEdit, currentCorporateBenefit]);
|
||||
|
||||
const methods = useForm({
|
||||
resolver: yupResolver(NewCorporatePlanSchema),
|
||||
resolver: yupResolver(NewCorporateBenefitSchema),
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
@@ -81,12 +78,12 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
|
||||
});
|
||||
} else {
|
||||
await axios
|
||||
.put('/corporate/' + corporate_id + '/divisions/' + currentCorporatePlan?.id, data)
|
||||
.put('/corporates/' + corporate_id + '/corporate-benefits/' + benefit_id, data)
|
||||
.then((res) => {
|
||||
enqueueSnackbar('Division updated successfully', { variant: 'success' });
|
||||
})
|
||||
.then((res) => {
|
||||
navigate('/corporate/' + corporate_id + '/divisions/', { replace: true });
|
||||
navigate('/corporates/' + corporate_id + '/benefits', { replace: true });
|
||||
})
|
||||
.catch(({ response }) => {
|
||||
enqueueSnackbar('Update Failed : ' + response.data.message, { variant: 'error' });
|
||||
@@ -95,30 +92,44 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<Box sx={{ margin: 1, pb: 2, pl: 4 }}>
|
||||
<Grid container>
|
||||
<Grid item xs={6} sx={{ padding: 2 }}>
|
||||
<Grid item xs={12} sx={{ padding: 2 }}>
|
||||
<Grid container>
|
||||
<Stack direction="row" alignItems="center" sx={{ width: '100%' }}>
|
||||
<Typography variant="subtitle2" sx={{ mr: 2 }}>
|
||||
ASO/Budget
|
||||
</Typography>
|
||||
<RHFTextField name="budget" />
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={6} sx={{ padding: 2 }}>
|
||||
<Grid container>
|
||||
<Stack direction="row" alignItems="center" sx={{ width: '100%' }}>
|
||||
<Typography variant="subtitle2" sx={{ mr: 2 }}>
|
||||
ASO/Budget
|
||||
</Typography>
|
||||
<RHFTextField name="budget" />
|
||||
<RHFTextField name="budget" type='number'/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button
|
||||
sx={{marginTop:2}}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
color='inherit'
|
||||
onClick={() => navigate(`/corporates/${corporate_id}/benefits`)}
|
||||
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<LoadingButton
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
// fullWidth={true}
|
||||
loading={isSubmitting}
|
||||
sx={{marginTop:2, marginLeft:2}}
|
||||
>
|
||||
Save
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</Box>
|
||||
</FormProvider>
|
||||
);
|
||||
|
||||
@@ -35,19 +35,19 @@ export default function Divisions() {
|
||||
},
|
||||
{
|
||||
name: corporate?.name ?? '-',
|
||||
href: '/corporate/' + corporate_id,
|
||||
href: '/corporates/' + corporate_id,
|
||||
},
|
||||
{
|
||||
name: 'Benefit',
|
||||
href: '/corporate/' + corporate_id + '/benefits',
|
||||
href: '/corporates/' + corporate_id + '/benefits',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<Card>
|
||||
{/* <Card> */}
|
||||
<CorporateTabNavigations position={'benefits'} />
|
||||
<DivisionsList />
|
||||
</Card>
|
||||
{/* </Card> */}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -85,7 +85,7 @@ export default function CustomizedAccordions() {
|
||||
(panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
|
||||
setExpanded(newExpanded ? panel : false);
|
||||
};
|
||||
const pageTitle = 'Audittrail Corporate';
|
||||
const pageTitle = 'Corporate Dashboard';
|
||||
|
||||
const { themeStretch } = useSettings();
|
||||
|
||||
@@ -121,11 +121,11 @@ export default function CustomizedAccordions() {
|
||||
},
|
||||
{
|
||||
name: corporate?.name ?? '-',
|
||||
href: '/corporate/' + corporate_id + '/plans',
|
||||
href: '/corporates/' + corporate_id + '/benefits',
|
||||
},
|
||||
{
|
||||
name: 'Audittrail Corporate',
|
||||
href: '/corporate/' + corporate_id + '/plans',
|
||||
name: 'Benefit',
|
||||
href: '/corporates/' + corporate_id + '/benefits',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -155,6 +155,7 @@ export default function CustomizedAccordions() {
|
||||
if (key !== 'reason') {
|
||||
return null; // Melewati iterasi saat key adalah 'deleted_by'
|
||||
}
|
||||
|
||||
renderedValue = item.new_values[key];
|
||||
|
||||
const field = key.charAt(0).toUpperCase() + key.slice(1);
|
||||
|
||||
@@ -33,11 +33,11 @@ export default function Divisions() {
|
||||
},
|
||||
{
|
||||
name: corporate?.name ?? '-',
|
||||
href: '/corporate/' + corporate_id,
|
||||
href: '/corporates/' + corporate_id,
|
||||
},
|
||||
{
|
||||
name: 'Claim History',
|
||||
href: '/corporate/' + corporate_id + '/claim-histories',
|
||||
href: '/corporates/' + corporate_id + '/claim-histories',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -8,6 +8,9 @@ import axios from '../../../utils/axios';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import CorporatePlanForm from './Form';
|
||||
import { CorporatePlan } from '../../../@types/corporates';
|
||||
import { Stack } from "@mui/system";
|
||||
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
|
||||
|
||||
@@ -36,8 +39,8 @@ export default function PlanCreate() {
|
||||
|
||||
return (
|
||||
<Page title="Create Corporate Plan">
|
||||
<HeaderBreadcrumbs
|
||||
heading={'Create Corporate Plan'}
|
||||
{/* <HeaderBreadcrumbs
|
||||
heading={ 'Edit Plan'}
|
||||
links={[
|
||||
{
|
||||
name: 'Corporates',
|
||||
@@ -56,7 +59,16 @@ export default function PlanCreate() {
|
||||
href: '/corporates/'+corporate_id+'/corporate-plans/'+id,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
/> */}
|
||||
<Stack direction="row" alignItems="center">
|
||||
<ArrowBackIosIcon
|
||||
onClick={() => navigate(`/corporates/${corporate_id}/plans`)}
|
||||
sx={{ cursor: 'pointer' }}
|
||||
/>
|
||||
<Typography variant="h5" sx={{ marginRight:2, flexGrow: 1 }}>
|
||||
Edit Plan
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<CorporatePlanForm isEdit={isEdit} currentCorporatePlan={currentCorporatePlan}/>
|
||||
</Page>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as Yup from 'yup';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { Card, Grid, Stack, Typography } from '@mui/material';
|
||||
import { Card, Grid, Stack, Typography, Button } from '@mui/material';
|
||||
import { CorporatePlan } from '../../../@types/corporates';
|
||||
import { FormProvider, RHFEditor, RHFSwitch, RHFTextField } from '../../../components/hook-form';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
@@ -22,16 +22,22 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
|
||||
const { corporate_id } = useParams();
|
||||
|
||||
const NewCorporatePlanSchema = Yup.object().shape({
|
||||
name: Yup.string().required('Name is required'),
|
||||
code: Yup.string().required('Corporate Code is required'),
|
||||
service: Yup.string().required('Corporate Service is required'),
|
||||
plan: Yup.string().required('Corporate Plan is required'),
|
||||
type: Yup.string().required('Corporate Type is required'),
|
||||
limit: Yup.string().required('Corporate Limit is required'),
|
||||
});
|
||||
|
||||
const defaultValues = useMemo(
|
||||
() => ({
|
||||
name: currentCorporatePlan?.name || '',
|
||||
// name: currentCorporatePlan?.name || '',
|
||||
code: currentCorporatePlan?.code || '',
|
||||
active: currentCorporatePlan?.active || true,
|
||||
description: currentCorporatePlan?.description || '',
|
||||
// active: currentCorporatePlan?.active || true,
|
||||
type: currentCorporatePlan?.type || '',
|
||||
limit: currentCorporatePlan?.limit_rules || '',
|
||||
service: currentCorporatePlan?.service_code || '',
|
||||
plan: currentCorporatePlan?.corporate_plan_id || '',
|
||||
}),
|
||||
[currentCorporatePlan]
|
||||
);
|
||||
@@ -62,14 +68,15 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
|
||||
} = methods;
|
||||
|
||||
const onSubmit = async (data: any) => {
|
||||
console.log(data);
|
||||
if (!isEdit) {
|
||||
await axios
|
||||
.post('/corporate/' + corporate_id + '/corporate-plans', data)
|
||||
.post('/corporates/' + corporate_id + '/corporate-plans', data)
|
||||
.then((res) => {
|
||||
enqueueSnackbar('Corporate Plan created successfully', { variant: 'success' });
|
||||
})
|
||||
.then((res) => {
|
||||
navigate('/corporate/' + corporate_id + '/corporate-plans', { replace: true });
|
||||
navigate(`/corporates/${corporate_id}/plans`, { replace: true });
|
||||
})
|
||||
.catch(({ response }) => {
|
||||
if (response.status === 422) {
|
||||
@@ -83,12 +90,12 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
|
||||
});
|
||||
} else {
|
||||
await axios
|
||||
.put('/corporate/' + corporate_id + '/corporate-plans/' + currentCorporatePlan?.id, data)
|
||||
.put('/corporates/' + corporate_id + '/corporate-plans/' + currentCorporatePlan?.id, data)
|
||||
.then((res) => {
|
||||
enqueueSnackbar('Corporate Plan updated successfully', { variant: 'success' });
|
||||
})
|
||||
.then((res) => {
|
||||
navigate('/corporate/' + corporate_id + '/corporate-plans/', { replace: true });
|
||||
navigate('/corporates/' + corporate_id + '/plans', { replace: true });
|
||||
})
|
||||
.catch(({ response }) => {
|
||||
enqueueSnackbar('Update Failed : ' + response.data.message, { variant: 'error' });
|
||||
@@ -98,12 +105,34 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
|
||||
|
||||
return (
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={8}>
|
||||
<Card sx={{ p: 2 }}>
|
||||
<Card sx={{ p: 2, marginTop: 2 }}>
|
||||
<Grid container spacing={4} paddingX={2} paddingY={2}>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="subtitle1">Service*</Typography>
|
||||
<RHFTextField name="service" label="service" sx={{marginTop:2}}/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="subtitle1">Plan*</Typography>
|
||||
<RHFTextField name="plan" label="plan" sx={{marginTop:2}} />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="subtitle1">Code*</Typography>
|
||||
<RHFTextField name="code" label="code" sx={{marginTop:2}} />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="subtitle1">Type*</Typography>
|
||||
<RHFTextField name="type" label="type" sx={{marginTop:2}} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="subtitle1">Plan Limit*</Typography>
|
||||
<RHFTextField name="limit" label="limit" sx={{marginTop:2}} />
|
||||
</Grid>
|
||||
|
||||
{/* <Grid item xs={3}>
|
||||
<Stack spacing={3}>
|
||||
<Typography variant="h6">Corporate Plan Detail</Typography>
|
||||
|
||||
|
||||
<RHFTextField name="name" label="Name" />
|
||||
|
||||
<RHFTextField name="code" label="Code" />
|
||||
@@ -122,17 +151,35 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
|
||||
fullWidth={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
Create Corporate Plan
|
||||
{ isEdit ? 'Update' : 'Create'} Corporate Plan
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Grid> */}
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Card sx={{ p: 2 }}>
|
||||
<RHFSwitch name="active" label="Active" />
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
<Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button
|
||||
sx={{marginTop:2}}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
color='inherit'
|
||||
onClick={() => navigate(`/corporates/${corporate_id}/plans`)}
|
||||
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<LoadingButton
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
// fullWidth={true}
|
||||
loading={isSubmitting}
|
||||
sx={{marginTop:2, marginLeft:2}}
|
||||
>
|
||||
Save
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@ export default function Divisions() {
|
||||
},
|
||||
{
|
||||
name: corporate?.name ?? '-',
|
||||
href: '/corporate/' + corporate_id,
|
||||
href: '/corporates/' + corporate_id,
|
||||
},
|
||||
{
|
||||
name: 'Corporate Plan',
|
||||
href: '/corporate/' + corporate_id + '/corporate-plans',
|
||||
href: '/corporates/' + corporate_id + '/corporate-plans',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user