pengajuan claim

This commit is contained in:
2023-10-09 13:18:38 +07:00
parent 84cf41dba8
commit d69b610c5f
14 changed files with 2017 additions and 184 deletions

View File

@@ -0,0 +1,8 @@
export type CardSubmit = {
rows?: Array<DataType>;
id: number,
name: string,
member_id: string,
usesage_limit: number,
limit: number
}

View File

@@ -0,0 +1,274 @@
/* ---------------------------------- @mui ---------------------------------- */
import { styled } from '@mui/material/styles';
import {
Paper,
Table as TableContent,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Button,
TableSortLabel,
Box,
Card,
Grid,
FormControl,
InputLabel,
Select,
MenuItem,
SelectChangeEvent,
Stack,
Typography,
LinearProgress,
linearProgressClasses,
InputAdornment,
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
/* ---------------------------------- axios --------------------------------- */
import axios from '../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { Fragment, useContext, useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
/* -------------------------------- component ------------------------------- */
import BaseTablePagination from './BaseTablePagination';
/* ---------------------------------- theme --------------------------------- */
import palette from '../theme/palette';
/* ---------------------------------- utils --------------------------------- */
import { UserCurrentCorporateContext } from '../contexts/UserCurrentCorporate';
import { fSplit } from '../utils/formatNumber';
/* ---------------------------------- types --------------------------------- */
import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '../@types/table';
/* ----------------------------------- icon --------------------------------- */
import SearchIcon from '@mui/icons-material/Search';
import { FormControlLabel } from '@mui/material';
import { Checkbox } from '@mui/material';
import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded';
import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded';
import { LoadingButton } from '@mui/lab';
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: '#D1F1F1',
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: '#54D62C',
},
}));
/* -------------------------------------------------------------------------- */
export default function Table<T>({
rows,
loadings,
params,
searchs,
}: TableListProps<T>) {
/* ------------------------------ handle checkbox ----------------------------*/
const handleCheckboxChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
// Anda bisa menambahkan logika di sini
if (event.target.checked) {
// Checkbox dicentang
console.log('Checkbox dicentang');
// Tambahkan kode lain yang ingin Anda jalankan saat checkbox dicentang
} else {
// Checkbox tidak dicentang
console.log('Checkbox tidak dicentang');
// Tambahkan kode lain yang ingin Anda jalankan saat checkbox tidak dicentang
}
};
return (
// <Card>
<Grid container>
{/* Field 1 */}
<Grid item xs={12} paddingX="10px" paddingY="20px">
{searchs && searchs.useSearchs ? (
<Fragment>
<Grid item xs={12} lg={12} xl={12}>
<form onSubmit={searchs.handleSearchSubmit}>
<TextField
id="search-input"
variant="outlined"
onChange={(event) => searchs.setSearchText(event.target.value)}
value={searchs.searchText}
fullWidth
placeholder='Search Name or Member ID... '
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
</form>
</Grid>
</Fragment>
) : null }
</Grid>
{/* End Field 1 */}
{/* Field 2 */}
<Grid item xs={12} paddingX="10px" paddingY="10px">
<Card>
<Fragment>
<Stack direction='row' alignItems='center'>
<Grid item xs={1} lg={1} xl={1} >
<form onSubmit={searchs.handleSearchSubmit}>
<FormControlLabel
value="end"
control={<Checkbox />}
label=""
labelPlacement="end"
sx={{marginLeft: '20px'}}
/>
</form>
</Grid>
<div style={{ position: 'relative', flex: 'none', height: 'fit-content', margin: '15px'}}>
<img
width={52}
height={52}
src="/images/user-profile.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
</div>
<Grid item xs={7} lg={7} xl={7} >
<Typography variant='subtitle1'>Alexandra Rhea Putranto</Typography>
<Typography color={'#637381'}>KM002-01</Typography>
</Grid>
<Grid item xs={3} lg={3} xl={3} >
<BorderLinearProgress
variant="determinate"
value={80}
// color='success'
sx={{ mb: 1 }}
/>
<Stack direction={'row'}>
<Grid item xs={3}>
<Typography variant='overline' sx={{textAlign:'left'}}>
LIMIT
</Typography>
</Grid>
<Grid item xs={7} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Stack direction={'row'}>
<Typography variant='overline'>
{fSplit(8000000)}
</Typography>
<Typography variant='overline'>
/ {fSplit(10000000000)}
</Typography>
</Stack>
</Grid>
</Stack>
</Grid>
<Grid item xs={2} lg={2} xl={2} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Stack direction={'row'}>
<HistoryRoundedIcon/>
<KeyboardArrowRightRoundedIcon
sx={{marginLeft: '30px'}}
/>
</Stack>
</Grid>
{/* <Grid item xs={1} lg={1} xl={1} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<ArrowForwardIosRoundedIcon></ArrowForwardIosRoundedIcon>
</Grid> */}
</Stack>
</Fragment>
</Card>
</Grid>
<Grid item xs={12} paddingX="10px" paddingY="10px">
<Card>
<Fragment>
<Stack direction='row' alignItems='center'>
<Grid item xs={1} lg={1} xl={1} >
<form onSubmit={searchs.handleSearchSubmit}>
<FormControlLabel
value="end"
control={<Checkbox />}
label=""
labelPlacement="end"
sx={{marginLeft: '20px'}}
/>
</form>
</Grid>
<div style={{ position: 'relative', flex: 'none', height: 'fit-content', margin: '15px'}}>
<img
width={52}
height={52}
src="/images/user-profile.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
</div>
<Grid item xs={7} lg={7} xl={7} >
<Typography variant='subtitle1'>Alexandra Rhea Putranto</Typography>
<Typography color={'#637381'}>KM002-01</Typography>
</Grid>
<Grid item xs={3} lg={3} xl={3} >
<BorderLinearProgress
variant="determinate"
value={80}
// color='success'
sx={{ mb: 1 }}
/>
<Stack direction={'row'}>
<Grid item xs={5}>
<Typography variant='overline' sx={{textAlign:'left'}}>
LIMIT
</Typography>
</Grid>
<Grid item xs={7}>
<Stack direction={'row'}>
<Typography variant='overline' style={{ textAlign: 'right' }}>
{fSplit(8000000)}
</Typography>
<Typography variant='overline' style={{ textAlign: 'right' }}>
/ {fSplit(10000000000)}
</Typography>
</Stack>
</Grid>
</Stack>
</Grid>
<Grid item xs={2} lg={2} xl={2} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Stack direction={'row'}>
<HistoryRoundedIcon/>
<KeyboardArrowRightRoundedIcon
sx={{marginLeft: '30px'}}
/>
</Stack>
</Grid>
{/* <Grid item xs={1} lg={1} xl={1} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<ArrowForwardIosRoundedIcon></ArrowForwardIosRoundedIcon>
</Grid> */}
</Stack>
</Fragment>
</Card>
</Grid>
{/* End Field 2 */}
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2, margin: '10px', color:'#212B36', backgroundColor:'#DFE3E8' }}
fullWidth
>
Claim Submit Selected
</LoadingButton>
</Grid>
// </Card>
);
}

View File

@@ -1,7 +1,10 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Button,
Box,
LinearProgress,
linearProgressClasses,
Stepper,
Step,
StepLabel,
@@ -9,19 +12,36 @@ import {
Typography,
Divider,
Stack,
Grid,
Avatar,
} from '@mui/material';
import { Add } from '@mui/icons-material';
// components
import MuiDialog from '../../components/MuiDialog';
/*------------------------------------ icon ----------------------------------- */
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
// theme
import palette from '../../theme/palette';
// React
import { ReactElement } from 'react';
import { ReactElement, useRef, useState } from 'react';
import { useSearchParams, useNavigate, Link } from 'react-router-dom';
import { fPostFormat } from '../../utils/formatTime';
import { fCurrency } from '../../utils/formatNumber';
type DataContent = {
info: string;
date: string;
time: string;
// -------------------------------- type --------------------------------------
type DataContentType = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type MuiDialogProps = {
@@ -32,15 +52,33 @@ type MuiDialogProps = {
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
data: DataContentType;
};
/* --------------------------------- styles --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
},
}));
/* -------------------------------------------------------------------------- */
const steps = ['Review', 'Approval', 'Disbursement'];
const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
function clickHandler(arg0: string) {
throw new Error('Function not implemented.');
}
const navigate = useNavigate();
const [serviceCode, setServiceCode] = useState('IP');
// const getContent = () => (
@@ -48,29 +86,66 @@ const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialog
return (
<>
<Stack
alignItems="center"
justifyContent="space-between"
direction="row"
sx={{ marginTop: 1 }}
>
<Typography variant="subtitle1" sx={{ height: 'max-content' }}>
Claim Request
</Typography>
<Stack>
<Typography variant="caption">Submission date</Typography>
<Typography variant="caption">15 / 05 / 2022</Typography>
</Stack>
</Stack>
<Box sx={{ width: '100%', marginTop: 2 }}>
<Stepper alternativeLabel>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
<Grid container>
{/* Field 1 */}
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Stack direction="row" alignItems="center">
<ArrowBackIosIcon onClick={() => navigate(`/corporate`)} sx={{ cursor: "pointer" }} />
<Typography variant="h5" sx={{ flexGrow: 1 }}>Claim Submission </Typography>
<Typography variant="inherit" sx={{ textAlign: "center", flexBasis: "15%" }}>Submission Date </Typography>
<Typography textAlign={'right'} variant="h6" sx={{ textAlign: "right" }}>
{fPostFormat(new Date(), 'dd MMM yyyy')}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Stack direction="row" spacing={4}>
<Button
sx={{ padding: 2, width: '50%', border: serviceCode === 'OP' ? '1px solid #919EAB52' : '1px solid #19BBBB' }}
variant={serviceCode == 'IP' ? 'outlined' : ''}
onClick={() => {
setServiceCode('IP');
}}
>
Inpatient
</Button>
<Button
sx={{ padding: 2, width: '50%',border: serviceCode === 'IP' ? '1px solid #919EAB52' : '1px solid #19BBBB' }}
variant={serviceCode == 'OP' ? 'outlined' : ''}
onClick={() => {
setServiceCode('OP');
}}
>
Outpatient
</Button>
</Stack>
</Grid>
<Grid item xs={12}>
<Card sx={{ p: 2, marginBottom: 2 }}>
<Stack direction="row" alignContent={'center'}>
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
alt={'test'}
sx={{ margin: 1, width: 56, height: 56 }}
/>
<Stack sx={{ p: 1 }}>
<Typography variant='h5'>{'Alexandra Rhea Putranto'}</Typography>
<Typography variant='subtitle1' color={'#637381'}>{'KM002-01'}</Typography>
</Stack>
<Stack sx={{ p: 1 }}>
<Typography variant="body1" sx={{ marginBottom: 1, fontWeight: 600 }}>
Total Limit
</Typography>
<BorderLinearProgress variant="determinate" value={80} />
<Typography sx={{ textAlign: 'right', marginTop: 1 }}>
{fCurrency(8000000)} / {fCurrency(100000)}
</Typography>
</Stack>
</Stack>
</Card>
</Grid>
</Grid>
<Stack marginTop={2}>
<Typography variant="subtitle1" paddingY={2}>
17 Mei 2022

View File

@@ -1,7 +1,7 @@
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* ----------------------------------- mui ---------------------------------- */
import { Container, Grid } from '@mui/material';
import { Container, Grid, Typography } from '@mui/material';
/* ------------------------------- components ------------------------------- */
import Page from '../../components/Page';
import TableList from '../../components/Table';
@@ -23,6 +23,9 @@ import Documents from '../Claims/components/Documents';
// theme
import palette from '../../theme/palette';
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
import { Stack } from '@mui/system';
import { fDateSuffix } from '../../utils/formatTime';
import DialogClaimSubmitMember from '../../sections/claim-submit/DialogClaimSubmitMember';
interface ClaimStatusType {
name: string;
@@ -30,6 +33,50 @@ interface ClaimStatusType {
color: string;
}
/* ------------------------------ default data ------------------------------ */
type DataMember = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type CardPolicyProps = {
limit: {
myLimit: {
balance: number;
total: number;
percentage: number;
};
lockLimit: {
balance: number;
percentage: number;
};
};
topUpLimit: {
companyName: string;
policyNumber: number;
totalMembers: number;
totalCases: number;
totalPersen: number;
myLimit: {
balance: number;
total: number;
percentage: number;
};
maxTopUp: number;
};
members?: DataMember[];
};
export default function Drugs() {
const { themeStretch } = useSettings();
const { corporateValue } = useContext(UserCurrentCorporateContext);
@@ -48,9 +95,12 @@ export default function Drugs() {
setIsLoading: setIsLoading,
};
/* ------------------------------ handle params ----------------------------- */
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const [policyData, setPolicyData] = useState<CardPolicyProps>();
const params = {
searchParams: searchParams,
@@ -70,6 +120,11 @@ export default function Drugs() {
orderBy: orderBy,
setOrderBy: setOrderBy,
};
/* ---------------------------- Get Current Date ---------------------------- */
const current = new Date();
const date = fDateSuffix(current);
/* -------------------------------------------------------------------------- */
/* ---------------------------- handle pagination --------------------------- */
@@ -177,7 +232,7 @@ export default function Drugs() {
setSearchParams(parameters);
setListAllMemberByClaimStatus(claim.data.data.allMembersByClaimStatus.data);
// setListAllMemberByClaimStatus(claim.data.data.allMembersByClaimStatus.data);
setPaginationTable(claim.data.data.allMembersByClaimStatus);
setIsLoading(false);
@@ -198,17 +253,21 @@ export default function Drugs() {
/>
<Container maxWidth={themeStretch ? false : 'xl'}>
<Grid container spacing={2}>
<Grid item xs={12} lg={6} md={6}>
<Typography variant="h6" sx={{marginLeft:'10px'}}> Select Employee</Typography>
</Grid>
<Grid item xs={12} lg={6} md={6} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Stack direction='row' alignItems='center'>
<Typography variant="inherit" align='right' sx={{ marginRight: '10px' }}>Submission Date</Typography>
<Typography variant="subtitle1" align='right' sx={{ marginRight: '10px' }}>{date}</Typography>
</Stack>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<List />
{/* <TableList
headCells={headCells}
rows={listAllMemberByClaimStatus}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
/> */}
<DialogClaimSubmitMember
openDialog={true}
setOpenDialog={false}
title={{ name: 'te' }}
/>
</Grid>
</Grid>
</Container>

View File

@@ -8,7 +8,7 @@ import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import TableComponent from '../../components/Table';
import CardClaimSubmit from '../../components/CardClaimSubmit';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
@@ -106,147 +106,30 @@ export default function List() {
handleSearchSubmit: handleSearchSubmit,
};
/* ------------------------------------------------------------------------- */
/*-------------------------------- handlle checkbox ------------------------ */
const handleCheckboxChange = async (event: React.FormEvent<HTMLFormElement>) => {
// Anda bisa menambahkan logika di sini
if (event.target.checked) {
// Checkbox dicentang
console.log('Checkbox dicentang');
// Tambahkan kode lain yang ingin Anda jalankan saat checkbox dicentang
} else {
// Checkbox tidak dicentang
console.log('Checkbox tidak dicentang');
// Tambahkan kode lain yang ingin Anda jalankan saat checkbox tidak dicentang
}
};
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'memberId',
align: 'left',
label: 'Member ID',
isSort: true,
},
{
id: 'codeRequest',
align: 'left',
label: 'Code Request',
isSort: true,
},
{
id: 'submissionDate',
align: 'left',
label: 'Request Date',
isSort: true,
},
{
id: 'fullName',
align: 'left',
label: 'Name',
isSort: true,
},
{
id: 'division',
align: 'left',
label: 'Divisi',
isSort: false,
},
{
id: 'status',
align: 'center',
label: 'Status',
isSort: false,
},
{
id: 'action',
align: 'right',
label: '',
isSort: false,
},
];
useEffect(() => {
(async () => {
setIsLoading(true);
await new Promise((resolve) => setTimeout(resolve, 250));
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
const response = await axios.get(`${corporateValue}/members`, {
params: { ...parameters, type: 'claim-report' },
});
setData(
response.data.data.map((obj: any) => ({
...obj,
status:
obj.status === 'requested' ? (
<Button
onClick={() => navigate('dialog-detail')}
sx={{
backgroundColor: 'rgba(84, 214, 44, 0.16)',
color: palette.dark.success.dark,
paddingX: 1.5,
paddingY: 1,
'&:hover': {
backgroundColor: 'rgba(84, 214, 44, 0.16)',
color: palette.dark.success.dark,
},
}}
>
Request
</Button>
) : obj.status === 'approved' ? (
<Button
sx={{
backgroundColor: (theme) => theme.palette.secondary.main,
color: '#FFFF',
paddingX: 1.5,
paddingY: 1,
'&:hover': {
backgroundColor: (theme) => theme.palette.secondary.dark,
color: '#FFFF',
},
}}
>
Approved
</Button>
) : (
<Button
startIcon={<Iconify icon="fa6-solid:clock" />}
sx={{
backgroundColor: '#CD7B2E',
color: '#FFFF',
paddingX: 1.5,
paddingY: 1,
'&:hover': {
backgroundColor: '#BF6919',
color: '#FFFF',
},
}}
>
Ongoing
</Button>
),
submissionDate:
obj.submissionDate ? fDate(obj.submissionDate) : ''
}))
);
setPaginationTable(response.data);
setRowsPerPage(response.data.per_page);
if (searchParams.get('page')) {
//@ts-ignore
const currentPage = parseInt(searchParams.get('page')) - 1;
paginationTable.current_page = currentPage;
setPage(currentPage);
}
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
return (
<Stack>
<TableComponent
headCells={headCells}
<CardClaimSubmit
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}

View File

@@ -349,7 +349,7 @@ export default function CorporateForm({currentCorporate }: Props) {
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
{/* <Card sx={{ p:3, mb:3, background: 'gray', color: 'white' }}><Typography>Corporate Detail</Typography></Card> */}
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Grid item xs={12} md={8}>
<Card sx={{ p: 3 }}>
<Stack spacing={3}>
<Grid item xs={12}>
@@ -431,7 +431,7 @@ export default function CorporateForm({currentCorporate }: Props) {
</Card>
</Grid>
{/* <Grid item xs={12} md={4}>
<Grid item xs={12} md={4}>
<Stack spacing={3}>
<Card sx={{ p: 3 }}>
<Stack spacing={3} mt={2}>
@@ -439,7 +439,7 @@ export default function CorporateForm({currentCorporate }: Props) {
<Typography align="center">Company Logo</Typography>
<UploadImage setFile={setFile} currentImage={currentImage} />
</Stack>
<Box>
{/* <Box>
<Box
sx={{ display: 'flex', placeContent: 'space-between', placeItems: 'center' }}
>
@@ -452,10 +452,10 @@ export default function CorporateForm({currentCorporate }: Props) {
<Typography>Company Automatic Linking</Typography>
<RHFSwitch name="automatic_linking" label="" labelPlacement="start" />
</Box>
</Box>
</Box> */}
</Stack>
</Card>
<Card sx={{ p: 3 }}>
{/* <Card sx={{ p: 3 }}>
<Stack>
<Typography variant="subtitle2" sx={{ color: 'text.secondary' }}>
Linking Rules
@@ -464,9 +464,9 @@ export default function CorporateForm({currentCorporate }: Props) {
<RHFCustomMultiCheckbox name="linking_rules" options={linking_tools} />
</Stack>
</Stack>
</Card>
</Card> */}
</Stack>
</Grid> */}
</Grid>
<Grid item xs={12} md={11}>
<Stack direction="row" spacing={2} justifyContent="flex-end">

View File

@@ -113,6 +113,26 @@ export default function Router() {
},
],
},
{
path: '/claim-request/:id',
element: (
<AuthProvider>
<AuthGuard>
<DashboardLayout />
</AuthGuard>
</AuthProvider>
),
children: [
{
element: <ClaimRequest />,
index: true,
},
{
path: 'dialog-detail',
element: <DialogDetailClaim/>
},
],
},
{
path: '/claim-report',
element: (
@@ -212,6 +232,8 @@ const DialogDetailClaim = Loadable(lazy(()=> import('../pages/ClaimReport/Dialog
// Claim submit
const ClaimSubmit = Loadable(lazy(() => import('../pages/ClaimSubmit/Index')));
// Claim Request
const ClaimRequest = Loadable(lazy(() => import('../pages/ClaimSubmit/DialogDetailClaim')));
// Corporate
const Corporate = Loadable(lazy(() => import('../pages/Corporate/Index')));

View File

@@ -0,0 +1,148 @@
// @mui
import { styled } from '@mui/material/styles';
import { Button, Card, Typography, Link, Divider, Stack } from '@mui/material';
import { ChevronRight } from '@mui/icons-material';
// components
import Iconify from '../../components/Iconify';
// Section
import DialogNotification from './DialogNotification';
import DialogDetailClaim from './DialogDetailClaim';
// React
import { useState } from 'react';
// ----------------------------------------------------------------------
type DataContent = {
info: string;
date: string;
time: string;
};
type NotificationProps = {
data?: DataContent[];
};
// ----------------------------------------------------------------------
const RootNotificationStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: '1.5rem',
color: 'black',
backgroundColor: theme.palette.grey[200],
height: '100%',
maxHeight: '240px',
}));
const ItemNotificationStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(1),
borderRadius: 0.5,
color: 'black',
marginTop: 2,
overflowY: 'auto',
maxHeight: '154px',
gap: '0.5rem',
}));
// ----------------------------------------------------------------------
export default function CardNotification({ data }: NotificationProps) {
const [openDialog, setOpenDialog] = useState(false);
const [dialogTitle, setDialogTitle] = useState('');
const [isDialog, setIsDialog] = useState('');
const clickHandler = (isDialog: string) => {
switch (isDialog) {
case 'allNotification':
setDialogTitle('Notification');
setIsDialog(isDialog);
setOpenDialog(true);
break;
case 'infoDetail':
setDialogTitle('Claim Details');
setIsDialog(isDialog);
setOpenDialog(true);
break;
default:
break;
}
};
return (
<RootNotificationStyle>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography>
<Typography
variant="body2"
component="span"
sx={{ display: 'flex', alignItems: 'center' }}
>
<Iconify icon="eva:bell-fill" marginRight={0.75} />
Notification
<span
style={{
width: '12px',
height: '12px',
backgroundColor: '#19BBBB',
marginLeft: '0.5rem',
borderRadius: '50%',
}}
/>
</Typography>
</Typography>
<Button
sx={{ typography: 'body2' }}
endIcon={<ChevronRight />}
onClick={() => clickHandler('allNotification')}
>
View All
</Button>
</Stack>
<ItemNotificationStyle>
{data
? data.map(({ info, date, time }, index) => (
<div key={index}>
{index >= 1 ? <Divider sx={{ marginY: 0.5 }} /> : ''}
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
<Typography sx={{ typography: 'caption' }}>{info}</Typography>
<Link
component="button"
variant="caption"
underline="always"
onClick={() => clickHandler('infoDetail')}
>
Info Detail
</Link>
</Stack>
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
<Typography sx={{ typography: 'caption', color: '#656565' }}>{date}</Typography>
<Typography sx={{ typography: 'caption', color: '#656565' }}>{time}</Typography>
</Stack>
</Stack>
</div>
))
: ''}
</ItemNotificationStyle>
{isDialog === 'allNotification' && (
<DialogNotification
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: dialogTitle }}
data={data}
/>
)}
{isDialog === 'infoDetail' && (
<DialogDetailClaim
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: dialogTitle }}
/>
)}
</RootNotificationStyle>
);
}

View File

@@ -0,0 +1,210 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Button,
Card,
Typography,
LinearProgress,
linearProgressClasses,
Stack,
} from '@mui/material';
// components
import Iconify from '../../components/Iconify';
// React
import { useState } from 'react';
// utils
import { fCurrency, fSplit } from '../../utils/formatNumber';
/* -------------------------------- sections -------------------------------- */
import DialogTopUpLimit from './DialogTopUpLimit';
import DialogClaimSubmitMember from './DialogClaimSubmitMember';
/* ---------------------------------- types --------------------------------- */
type DataMember = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type CardPolicyProps = {
data: {
limit: {
myLimit: {
balance: number;
total: number;
percentage: number;
};
lockLimit: {
balance: number;
percentage: number;
};
};
topUpLimit: {
companyName: string;
policyNumber: number;
totalMembers: number;
totalCases: number;
totalPersen: number;
myLimit: {
balance: number;
total: number;
percentage: number;
};
maxTopUp: number;
};
members: {
memberId: string;
memberFullName: string;
};
};
};
/* -------------------------------------------------------------------------- */
/* --------------------------------- styled --------------------------------- */
const RootBalanceStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(3),
color: 'black',
backgroundColor: theme.palette.grey[200],
maxHeight: '240px',
}));
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: theme.palette.primary.main,
},
}));
/* -------------------------------------------------------------------------- */
export default function CardPolicy(props: CardPolicyProps) {
const [openDialog, setOpenDialog] = useState(false);
const [dialogTitle, setDialogTitle] = useState('');
const [isDialog, setIsDialog] = useState('');
const { limit, topUpLimit, members } = props.data || {};
if (!limit || !topUpLimit) {
return null;
}
const clickHandler = (isDialog: string) => {
switch (isDialog) {
case 'submitClaim':
setDialogTitle('Add Claim');
setIsDialog(isDialog);
setOpenDialog(true);
break;
case 'topUpLimit':
setDialogTitle('Top Up Limit');
setIsDialog(isDialog);
setOpenDialog(true);
break;
default:
break;
}
};
return (
<RootBalanceStyle>
<>
<Stack direction="row" justifyContent="space-between" sx={{ mb: 1 }}>
<div>
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
Total Limit
</Typography>
<Typography sx={{ typography: 'body2' }}>
{fCurrency(limit.myLimit ? limit.myLimit.balance : 0)}
</Typography>
<Typography sx={{ typography: 'caption', color: '#919EAB' }}>
/ {fSplit(limit.myLimit ? limit.myLimit.total : 0)}
</Typography>
</div>
<Stack direction="row" alignItems="center" justifyContent="center">
<Typography variant="h5" sx={{ ml: 0.5 }}>
{limit.myLimit ? limit.myLimit.percentage : 0}%
</Typography>
</Stack>
</Stack>
<BorderLinearProgress
variant="determinate"
value={limit.myLimit ? limit.myLimit.percentage : 0}
sx={{ mb: 1 }}
/>
<Stack sx={{ backgroundColor: '#B2E8E8', paddingY: 1, paddingX: 1.5, mb: 2 }}>
<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">
Lock Fund ( {limit.lockLimit ? limit.lockLimit.percentage : 0}% )
</Typography>
</Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
{fSplit(limit.lockLimit ? limit.lockLimit.balance : 0)} /{' '}
{fSplit(limit.myLimit ? limit.myLimit.total : 0)}
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Button
variant="outlined"
startIcon={<Iconify icon="bi:clipboard-check-fill" />}
fullWidth={true}
onClick={() => clickHandler('submitClaim')}
>
Submit Claim
</Button>
<Button
variant="contained"
startIcon={<Iconify icon="heroicons-solid:cash" />}
fullWidth={true}
onClick={() => clickHandler('topUpLimit')}
>
Top Up
</Button>
</Stack>
</>
{isDialog === 'submitClaim' && (
<DialogClaimSubmitMember
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: dialogTitle }}
/>
)}
{isDialog === 'topUpLimit' && (
<DialogTopUpLimit
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: dialogTitle, icon: 'heroicons-solid:cash' }}
data={topUpLimit}
/>
)}
</RootBalanceStyle>
);
}

View File

@@ -0,0 +1,266 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Typography,
LinearProgress,
linearProgressClasses,
Stack,
TextField,
InputAdornment,
Card,
Grid,
IconButton,
FormControlLabel,
Checkbox,
} from '@mui/material';
import { Search as SearchIcon } from '@mui/icons-material';
// components
import MuiDialog from '../../components/MuiDialog';
import Iconify from '../../components/Iconify';
import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded';
// React
import { ReactElement, useContext, useEffect, useState } from 'react';
import DialogRequestLog from './DialogRequestLog';
import axios from '../../utils/axios';
import { useSearchParams, useNavigate, Link } from 'react-router-dom';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { fSplit } from '../../utils/formatNumber';
import { LoadingButton } from '@mui/lab';
// ----------------------------------------------------------------------
type DataContentType = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
// data?: DataContent[];
};
// ----------------------------------------------------------------------
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: '#D1F1F1',
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: '#54D62C',
},
}));
// ----------------------------------------------------------------------
export default function DialogClaimSubmitMember({
title,
openDialog,
setOpenDialog,
}: MuiDialogProps) {
const { corporateValue } = useContext(UserCurrentCorporateContext);
/* ---------------------------------- data ---------------------------------- */
const [data, setData] = useState([]);
const [dataMemberClaim, setDataMemberClaim] = useState<DataContentType>({
id: 0,
fullName: '',
memberId: '',
limit: {
current: 0,
total: 0,
percentage: 0,
},
});
const navigate = useNavigate();
/* -------------------------------------------------------------------------- */
/* --------------------------------- Search --------------------------------- */
const [searchText, setSearchText] = useState('');
const [appliedParams, setAppliedParams] = useState({});
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (searchText === '') {
setAppliedParams({});
} else {
setAppliedParams({ search: searchText });
}
await new Promise((resolve) => setTimeout(resolve, 500));
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ Icon On Click ----------------------------- */
const clickHandler = ({ id, fullName, memberId, limit, avatar }: DataContentType) => {
setDataMemberClaim({
id: id,
fullName: fullName,
memberId: memberId,
limit: {
current: limit.current,
total: limit.total,
percentage: limit.percentage,
},
avatar: {
url: avatar && avatar.url,
title: avatar && avatar.title,
},
});
};
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
if (openDialog === true) {
const response = await axios.get(`${corporateValue}/members`, {
params: { ...appliedParams, type: 'claim-submit' },
});
setData(response.data.data);
}
})();
}, [corporateValue, openDialog, appliedParams]);
const getContent = () => (
<Stack>
<Stack marginTop={2} spacing={1}>
{data.map((row: DataContentType, key) => (
<Card
key={key}
sx={{ paddingY: 1, paddingX: 2 }}
onClick={() =>
clickHandler({
id: row.id,
fullName: row.fullName,
memberId: row.memberId,
limit: {
current: row.limit.current,
total: row.limit.total,
percentage: row.limit.percentage,
},
})
}
>
<Stack direction="row" alignItems="center" >
<Grid item xs={1} lg={1} xl={1} >
<form>
<FormControlLabel
value="end"
control={<Checkbox />}
label=""
labelPlacement="end"
sx={{marginLeft: '20px'}}
/>
</form>
</Grid>
<div style={{ position: 'relative', flex: 'none', height: 'fit-content', margin: '15px'}}>
<img
width={52}
height={52}
src="/images/user-profile.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
</div>
<Grid item xs={7} lg={7} xl={7} >
<Typography variant="subtitle1">{row.fullName}</Typography>
<Typography color="#637381" variant="body2" sx={{ fontWeight: 500 }}>
{row.memberId}
</Typography>
</Grid>
<Grid item xs={3} lg={3} xl={3} >
<BorderLinearProgress
variant="determinate"
value={row.limit && row.limit.percentage}
// color='success'
sx={{ mb: 1 }}
/>
<Stack direction={'row'}>
<Grid item xs={3}>
<Typography variant='overline' sx={{textAlign:'left'}}>
LIMIT
</Typography>
</Grid>
<Grid item xs={7} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Stack direction={'row'}>
<Typography variant='overline'>
{fSplit(row.limit && row.limit.current)}
</Typography>
<Typography variant='overline'>
/ {fSplit(row.limit && row.limit.total)}
</Typography>
</Stack>
</Grid>
</Stack>
</Grid>
<Grid item xs={2} lg={2} xl={2} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<IconButton >
<Iconify icon="ic:history" />
</IconButton>
<IconButton sx={{marginLeft: '10px'}} onClick={() => navigate(`/claim-request/${row.id}`)} >
<Iconify icon="ic:round-chevron-right" />
</IconButton>
</Grid>
</Stack>
</Card>
))}
</Stack>
</Stack>
);
return (
<Grid container>
<Grid item xs={12} paddingX="10px" paddingY="20px">
<form onSubmit={handleSearchSubmit}>
<TextField
id="search-input"
variant="outlined"
fullWidth
onChange={(event) => setSearchText(event?.target.value)}
value={searchText}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
placeholder="Search Name or Member ID... "
sx={{ marginTop: 2 }}
/>
</form>
{getContent()}
</Grid>
<Grid item xs={12}>
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2, margin: '10px', color:'#212B36', backgroundColor:'#DFE3E8' }}
fullWidth
>
Claim Submit Selected
</LoadingButton>
</Grid>
</Grid>
)
}

View File

@@ -0,0 +1,175 @@
// @mui
import {
Button,
Box,
Stepper,
Step,
StepLabel,
Card,
Typography,
Divider,
Stack,
} from '@mui/material';
import { Add } from '@mui/icons-material';
// components
import MuiDialog from '../../components/MuiDialog';
// theme
import palette from '../../theme/palette';
// React
import { ReactElement } from 'react';
type DataContent = {
info: string;
date: string;
time: string;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
};
const steps = ['Review', 'Approval', 'Disbursement'];
const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
const getContent = () => (
<>
<Stack
alignItems="center"
justifyContent="space-between"
direction="row"
sx={{ marginTop: 1 }}
>
<Typography variant="subtitle1" sx={{ height: 'max-content' }}>
Claim Request
</Typography>
<Stack>
<Typography variant="caption">Submission date</Typography>
<Typography variant="caption">15 / 05 / 2022</Typography>
</Stack>
</Stack>
<Box sx={{ width: '100%', marginTop: 2 }}>
<Stepper alternativeLabel>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
<Stack marginTop={2}>
<Typography variant="subtitle1" paddingY={2}>
17 Mei 2022
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{/* Item 1 */}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">09:10 WIB</Typography>
<Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Approval
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : mohon melengkapi kekurangan dokumen
</Typography>
<Typography variant="caption" color="#757575" sx={{ marginTop: 2, marginBottom: 1 }}>
Lab pemeriksaan darah
</Typography>
<Button
variant="outlined"
startIcon={<Add />}
fullWidth
sx={{ typography: 'subtitle2', borderColor: '#F5F5F5' }}
>
Hasil Pemeriksaan Laboratorium
</Button>
</Stack>
</Card>
{/* Item 2 */}
<Card sx={{ flex: 1, maxWidth: '100%', paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">09:00 WIB</Typography>
<Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Approval
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : Penilaian Dokter
</Typography>
</Stack>
</Card>
{/* Item 3 */}
<Card sx={{ flex: 1, maxWidth: '100%', paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">08:00 WIB</Typography>
<Typography
sx={{
backgroundColor: '#F5F5F5',
color: '#757575',
borderColor: '#757575',
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Review
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : Klaim Diajukan
</Typography>
</Stack>
</Card>
</Stack>
</Stack>
</>
);
return (
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
/>
);
};
export default DialogDetailClaim;

View File

@@ -0,0 +1,93 @@
// react
import { ReactElement, useState } from 'react';
// mui
import { Card, Divider, Link, Stack, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
// Component
import MuiDialog from '../../components/MuiDialog';
// Sections
import DialogDetailClaim from './DialogDetailClaim';
type DataContent = {
info: string;
date: string;
time: string;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
};
const ItemNotificationStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(1),
borderRadius: 0.5,
color: 'black',
}));
const DialogNotification = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
const [openDialogClaim, setOpenDialogClaim] = useState(false);
const [dialogTitleClaim, setDialogTitleClaim] = useState('');
const clickHandler = () => {
setDialogTitleClaim('Claim Details');
setOpenDialogClaim(true);
};
const getContent = () => (
<Stack sx={{ marginTop: 2 }}>
<ItemNotificationStyle>
{data
? data.map(({ info, date, time }: DataContent, key) => (
<div key={key}>
{key >= 1 ? <Divider sx={{ marginY: 0.5 }} /> : ''}
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
<Typography sx={{ typography: 'caption' }}>{info}</Typography>
<Link
component="button"
variant="caption"
underline="always"
onClick={clickHandler}
>
Info Detail
</Link>
</Stack>
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
<Typography sx={{ typography: 'caption', color: '#656565' }}>{date}</Typography>
<Typography sx={{ typography: 'caption', color: '#656565' }}>{time}</Typography>
</Stack>
</Stack>
</div>
))
: ''}
</ItemNotificationStyle>
</Stack>
);
return (
<>
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
/>
<DialogDetailClaim
openDialog={openDialogClaim}
setOpenDialog={setOpenDialogClaim}
title={{ name: dialogTitleClaim }}
/>
</>
);
};
export default DialogNotification;

View File

@@ -0,0 +1,355 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Typography,
LinearProgress,
linearProgressClasses,
Stack,
Card,
Button,
Divider,
Avatar,
} from '@mui/material';
// components
import MuiDialog from '../../components/MuiDialog';
import Iconify from '../../components/Iconify';
// React
import { ReactElement, useRef, useState } from 'react';
// form
import { LoadingButton } from '@mui/lab';
import axios from '../../utils/axios';
import { enqueueSnackbar } from 'notistack';
import { fPostFormat } from '../../utils/formatTime';
import { fCurrency } from '../../utils/formatNumber';
import { makeFormData } from '../../utils/jsonToFormData';
/* ---------------------------------- types --------------------------------- */
type DataContentType = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data: DataContentType;
};
/* -------------------------------------------------------------------------- */
/* --------------------------------- styles --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
},
}));
/* -------------------------------------------------------------------------- */
const DialogRequestLog = ({ openDialog, setOpenDialog, data }: MuiDialogProps) => {
const [serviceCode, setServiceCode] = useState('IP');
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) => fileIndex != index));
};
const fileKondisiInput = useRef<HTMLInputElement>(null);
const [fileKondisis, setFileKondisis] = useState([]);
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) => fileIndex != index));
};
const fileHasilPenunjangInput = useRef<HTMLInputElement>(null);
const [fileHasilPenunjangs, setFileHasilPenunjangs] = 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) => fileIndex != index));
};
const [submitLoading, setSubmitLoading] = useState(false);
function submitRequest() {
setSubmitLoading(true);
const formData = makeFormData({
member_id: data.id,
result_files: fileHasilPenunjangs,
diagnosa_files: fileDiagnosas,
kondisi_files: fileKondisis,
service_code: serviceCode,
});
axios
.post('/claim-requests', formData)
.then((response) => {
enqueueSnackbar(response.data.message ?? 'Berhasil membuat data', { variant: 'success' });
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
})
.then(() => {
setSubmitLoading(false);
});
}
const getContent = () => (
<Stack paddingY={1}>
<Stack direction="row" justifyContent={'end'} sx={{ marginBottom: 2 }}>
<Typography textAlign={'right'} variant="body2">
Submission Date : <br /> {fPostFormat(new Date(), 'dd/MM/yyyy')}
</Typography>
</Stack>
<Card sx={{ p: 1, background: '#f4f6f8', marginBottom: 2 }}>
<Stack direction="row" spacing={2}>
<Button
sx={{ padding: 2, width: '100%' }}
variant={serviceCode == 'IP' ? 'contained' : ''}
onClick={() => {
setServiceCode('IP');
}}
>
Rawat Inap
</Button>
<Button
sx={{ padding: 2, width: '100%' }}
variant={serviceCode == 'OP' ? 'contained' : ''}
onClick={() => {
setServiceCode('OP');
}}
>
Rawat Jalan
</Button>
</Stack>
</Card>
<Card sx={{ p: 1, background: '#f4f6f8', marginBottom: 2 }}>
<Stack direction="row">
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
alt={data.fullName}
sx={{ marginTop: 1, width: 48, height: 48 }}
/>
<Stack sx={{ p: 1 }}>
<Typography>{data.fullName}</Typography>
<Typography>{data.memberId}</Typography>
</Stack>
</Stack>
</Card>
<Card sx={{ paddingY: 1, paddingX: 2 }}>
<Typography variant="body1" sx={{ marginBottom: 1, fontWeight: 600 }}>
Total Limit
</Typography>
<BorderLinearProgress variant="determinate" value={data.limit.percentage} />
<Typography sx={{ textAlign: 'right', marginTop: 1 }}>
{fCurrency(data.limit.current)} / {fCurrency(data.limit.total)}
</Typography>
</Card>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={4}
sx={{ marginY: 2 }}
>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Kondisi
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileKondisis &&
fileKondisis.map((file, index) => (
<Stack 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);
}}
/>
</Stack>
))}
</Stack>
<input
type="file"
id="file"
ref={fileKondisiInput}
style={{ display: 'none' }}
multiple
onChange={handleKondisiInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileKondisiInput?.current?.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Diagnosa
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileDiagnosas &&
fileDiagnosas.map((file, index) => (
<Stack 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);
}}
/>
</Stack>
))}
</Stack>
<input
type="file"
id="file"
ref={fileDiagnosaInput}
style={{ display: 'none' }}
multiple
onChange={handleDiagnosaInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileDiagnosaInput?.current?.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Hasil Penunjang
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileHasilPenunjangs &&
fileHasilPenunjangs.map((file, index) => (
<Stack 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);
}}
/>
</Stack>
))}
</Stack>
<input
type="file"
id="file"
ref={fileHasilPenunjangInput}
style={{ display: 'none' }}
multiple
onChange={handleResultInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileHasilPenunjangInput?.current?.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add File
</LoadingButton>
</Stack>
</Stack>
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2 }}
onClick={() => {
submitRequest();
}}
loading={submitLoading}
>
LOG Request
</LoadingButton>
</Stack>
);
return (
// <>
// <MuiDialog
// title={{ name: data.fullName }}
// openDialog={openDialog}
// setOpenDialog={setOpenDialog}
// content={getContent()}
// maxWidth="sm"
// />
// </>
getContent()
);
};
export default DialogRequestLog;

View File

@@ -0,0 +1,265 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Typography,
LinearProgress,
linearProgressClasses,
Stack,
FormControlLabel,
} from '@mui/material';
import { LoadingButton } from '@mui/lab';
import Checkbox from '@mui/material/Checkbox';
// components
import MuiDialog from '../../components/MuiDialog';
import { FormProvider, RHFTextField } from '../../components/hook-form';
// React
import { useContext, ReactElement, useEffect, useState } from 'react';
import { fCurrency } from '../../utils/formatNumber';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
// yup
import * as Yup from 'yup';
// form
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import axios from '../../utils/axios';
import { enqueueSnackbar } from 'notistack';
/* ---------------------------------- types --------------------------------- */
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataProps;
};
type DataProps = {
companyName: string;
policyNumber: number;
totalMembers: number;
totalCases: number;
totalPersen: number;
myLimit: {
balance: number;
total: number;
percentage: number;
};
maxTopUp: number;
};
type FormValuesProps = {
topup: string;
};
/* -------------------------------------------------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
},
}));
// ----------------------------------------------------------------------
export default function DialogTopUpLimit({
title,
openDialog,
setOpenDialog,
data,
}: MuiDialogProps) {
const [isDisabledInput, setIsDisabledInput] = useState(false);
const [isDisabledButton, setIsDisabledButton] = useState(true);
const [isCheckboxChecked, setIsCheckboxChecked] = useState(false);
const [ message, setMessage ] = useState ('');
const { corporateValue } = useContext(UserCurrentCorporateContext);
const TopUpSchema = Yup.object().shape({
topup: Yup.number().max(
data?.maxTopUp,
`Maximum top-up amount is ${fCurrency(data?.maxTopUp)}`
),
});
const defaultValues = {
topup: 0,
};
const methods = useForm<FormValuesProps>({
resolver: yupResolver(TopUpSchema),
// @ts-ignore
defaultValues,
});
const {
setValue,
reset,
handleSubmit,
formState: { errors, isSubmitting },
} = methods;
useEffect(() => {
if (openDialog === false) {
setIsDisabledInput(false);
setIsDisabledButton(true);
setIsCheckboxChecked(false);
reset();
}
}, [openDialog, reset]);
const onSubmit = async (data: FormValuesProps) => {
await new Promise((resolve) => setTimeout(resolve, 500));
setIsDisabledInput(false);
setIsDisabledButton(true);
setIsCheckboxChecked(false);
try {
// Send the HTTP POST request to the backend
await axios.post(corporateValue + '/topup', {
topup: data.topup,
});
// Show a success notification
enqueueSnackbar('The request has been sent', { variant: 'success' });
setOpenDialog(false);
reset();
} catch (error) {
// Show an error notification
enqueueSnackbar('An error occurred', { variant: 'error' });
setOpenDialog(false);
}
};
const onCheckHandler = (value: string) => {
setIsDisabledInput(!isDisabledInput);
value === '0' || value === '' ? setIsDisabledButton(true) : setIsDisabledButton(false);
setIsCheckboxChecked(!isCheckboxChecked);
// @ts-ignore
setValue('topup', data.maxTopUp.toString());
};
const onTopupHandler = (value: string) => {
//console.log(!!errors);
let newValue;
if (value.startsWith('0')) {
newValue = '0';
} else {
newValue = value;
}
newValue === '0' || newValue === '' ? setIsDisabledButton(true) : setIsDisabledButton(false);
setValue('topup', newValue);
};
const getContent = () => (
<Stack spacing={1} marginTop={2}>
<Stack>
<Typography variant="caption" color="#637381">
Company Name
</Typography>
<Typography variant="body2">{data ? data.companyName : ''}</Typography>
</Stack>
<Stack>
<Typography variant="caption" color="#637381">
Policy Number
</Typography>
<Typography variant="body2">{data ? data.policyNumber : 0}</Typography>
</Stack>
<Stack direction="row" spacing={22}>
<Stack>
<Typography variant="caption" color="#637381">
Total Member
</Typography>
<Typography variant="body2">{data ? data.totalMembers : 0} Person</Typography>
</Stack>
<Stack>
<Typography variant="caption" color="#637381">
Total Cases
</Typography>
<Typography variant="body2">{data ? data.totalCases : 0} Cases</Typography>
</Stack>
</Stack>
<Stack spacing={1} sx={{ backgroundColor: '#F4F6F8', borderRadius: 1.5, padding: 2 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack>
<Typography variant="body2">Company Pooled Fund</Typography>
<Typography variant="body2">{fCurrency(data ? data.myLimit.balance : 0)}</Typography>
<Typography variant="caption" color="#919EAB">
/ {data ? data.myLimit.total : 0}
</Typography>
</Stack>
<Stack>
<Typography variant="h5">{data ? data.myLimit.percentage : 0}%</Typography>
</Stack>
</Stack>
<BorderLinearProgress variant="determinate" value={data ? data.myLimit.percentage : 0} />
</Stack>
<Stack spacing={2}>
<Typography variant="subtitle1" marginTop={3}>
Top Up Limit
</Typography>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<RHFTextField
name="topup"
label="Top Up"
type="number"
disabled={isDisabledInput}
onChange={(e) => onTopupHandler(e.target.value)}
error={!!errors.topup}
helperText={errors.topup?.message}
/>
<FormControlLabel
name="checkboxTopUp"
sx={{ typography: 'caption' }}
control={
<Checkbox
checked={isCheckboxChecked}
onChange={(e) => onCheckHandler(e.target.value)}
/>
}
label={'Max ' + fCurrency(data ? data.maxTopUp : 0)}
/>
<LoadingButton
fullWidth
size="large"
type="submit"
variant="contained"
loading={isSubmitting}
sx={{ marginTop: 2 }}
disabled={isDisabledButton}
>
Ajukan Permintaan
</LoadingButton>
</FormProvider>
</Stack>
</Stack>
);
return (
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
maxWidth="xs"
/>
);
}