Merge remote-tracking branch 'origin/staging' into origin/production

This commit is contained in:
Linksehat Staging Server
2023-12-25 09:43:39 +07:00
577 changed files with 64446 additions and 14205 deletions

View 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,
}
}

View File

@@ -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
}

View File

@@ -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
};

View File

@@ -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,
};

View File

@@ -0,0 +1,8 @@
export type Drug = {
id: number;
type: string;
code: string;
name: string;
version:string;
active: number;
}

View File

@@ -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>
);
}

View 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;

View 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>
);
}

View 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>
</>
);
}

View File

@@ -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>
);
};

View File

@@ -21,7 +21,7 @@ export default function ThemeColorPresets({ children }: Props) {
...defaultTheme,
palette: {
...defaultTheme.palette,
primary: setColor,
// primary: setColor,
},
customShadows: {
...defaultTheme.customShadows,

View File

@@ -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">

View 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>
);
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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}

View File

@@ -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>
);
}

View File

@@ -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);
}}

View File

@@ -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>
);
}

View File

@@ -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;

View File

@@ -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;

View 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>
);
}}
/>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View 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}
/>
)}
/>
);
}

View File

@@ -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>
)}
/>
);
}

View 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>
)}
/>
);
}

View File

@@ -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>
)}
/>
);
}

View 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}
/>
);
}

View File

@@ -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} />
</>
)}
/>
);
}

View File

@@ -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,
}}
/>
</>
)}
/>
);
}

View File

@@ -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,
}}
/>
</>
)}
/>
);
}

View File

@@ -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,
}}
/>
</>
)}
/>
);
}

View File

@@ -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>
);
}

View 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}
/>
);
}}
/>
);
}

View 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';

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1 @@
export { default as TableMoreMenu } from './TableMoreMenu';

View 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>
</>
);
}

View File

@@ -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)

View 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;

View File

@@ -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',

View File

@@ -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>
);
}

View File

@@ -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>
)
}

View File

@@ -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>
);
}

View File

@@ -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>
)
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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;
};

View File

@@ -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
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
)
}

View File

@@ -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>
);
}

View File

@@ -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>
)
}

View File

@@ -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>
);
}

View File

@@ -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>
)
}

View File

@@ -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>
);
}

View File

@@ -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;
};

View File

@@ -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
}

View File

@@ -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>
);
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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

View File

@@ -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>
);
}

View File

@@ -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>
);

View 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>
);
}

View 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>
);
}

View 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>
))}
</>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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;
};

View 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
}

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -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>
);
}

View File

@@ -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 ------------------ */}

View 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;
};

View 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
}

View File

@@ -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}>

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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'})

View File

@@ -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>

View File

@@ -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>
);
}

View File

@@ -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>
);

View File

@@ -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

View File

@@ -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);

View File

@@ -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',
},
]}
/>

View File

@@ -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>

View File

@@ -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>
);
}

View File

@@ -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