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

@@ -74,6 +74,7 @@
"react-hook-form": "^7.43.0",
"react-intersection-observer": "^8.34.0",
"react-lazy-load-image-component": "^1.5.6",
"react-number-format": "^5.3.1",
"react-quill": "2.0.0-beta.4",
"react-router": "^6.8.0",
"react-router-dom": "^6.8.0",

View File

@@ -1681,6 +1681,7 @@ packages:
/@emotion/memoize@0.7.4:
resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
requiresBuild: true
dev: false
optional: true

View File

@@ -1,15 +0,0 @@
{
"greeting": "Hello",
"buttonText": "Click Me",
"infoLogin": "Enter the registered account",
"txtLogin1" : "Sign in to Hospital Portal",
"txtLogin2" : "Enter your details below",
"txtCardSearchMember1" : "Guarantee Submission",
"txtCardSearchMember2" : "Find Member",
"txtCardSearchMember3" : "Date Birth",
"txtCardSearchMember4" : "Member ID",
"txtCardSearchMember5" : "Member",
"txtDialogMember1" : "Benefit Summary",
"txtDialogMember2" : "Request LOG",
"txtDialogMember3" : "Member Detail"
}

View File

@@ -0,0 +1 @@
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><g fill="#637381"><path d="m16.8125 2.83333h-1.1459v-.91667c0-.50599-.4097-.91666-.9166-.91666s-.9167.41067-.9167.91666v.91667h-7.33331v-.91667c0-.50599-.40975-.91666-.91667-.91666-.50691 0-.91666.41067-.91666.91666v.91667h-1.14583c-1.39058 0-2.52083 1.13025-2.52083 2.52083v10.31244c0 1.5162 1.23383 2.75 2.74999 2.75h4.58333c.50691 0 .91666-.4106.91666-.9166s-.40975-.9167-.91666-.9167h-4.58333c-.50599 0-.91666-.4116-.91666-.9167v-7.33328h14.66667c0 .506.4097.91666.9166.91666s.9167-.41066.9167-.91666v-2.97916c0-1.39058-1.1302-2.52083-2.5208-2.52083z"/><path d="m17.0413 11.0834c-3.2853 0-5.9583 2.673-5.9583 5.9583s2.673 5.9583 5.9583 5.9583 5.9583-2.673 5.9583-5.9583-2.673-5.9583-5.9583-5.9583zm2.7555 4.9545-2.9791 3.4375c-.1669.1925-.4061.3062-.66.3163-.011 0-.022 0-.033 0-.243 0-.4758-.0963-.6481-.2686l-1.6042-1.6042c-.3584-.3584-.3584-.9377 0-1.2961.3584-.3585.9378-.3585 1.2962 0l.9084.9084 2.3338-2.6932c.3318-.3831.9112-.4226 1.2934-.0926.3823.331.4235.9103.0926 1.2925z" opacity=".48"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><g transform="translate(2 4)"><path d="m18.38 4.57-1.23 1.85c1.2049 2.40314304 1.1222508 5.2507848-.22 7.58h-13.86c-1.76350331-3.059309-1.31261601-6.91298595 1.10947843-9.48257235s6.24242627-3.24722187 9.40052157-1.66742765l1.85-1.23c-3.8761922-2.48556317-8.94860517-2.00294347-12.28650726 1.16901179-3.3379021 3.17195526-4.07833512 8.21319061-1.79349274 12.21098821.35510459.6150891 1.00977788.9957131 1.72 1.0000158h13.85c.7173695.0028322 1.3813181-.3787474 1.74-1.0000158 1.8786438-3.25433 1.7743473-7.28712667-.27-10.44z"/><path d="m8.59 11.41c.37513651.3755541.8841815.5865733 1.415.5865733s1.0398635-.2110192 1.415-.5865733l5.66-8.49-8.49 5.66c-.37555409.37513651-.58657331.8841815-.58657331 1.415s.21101922 1.0398635.58657331 1.415z" opacity=".48"/></g></svg>

After

Width:  |  Height:  |  Size: 849 B

View File

@@ -1,15 +0,0 @@
{
"greeting": "Halo",
"buttonText": "Klik Saya",
"infoLogin": "Masukan akun yang telah terdaftar",
"txtLogin1" : "Masuk ke Hospital Portal",
"txtLogin2" : "Masukkan detail Anda di bawah ini",
"txtCardSearchMember1" : "Pengajuan Jaminan",
"txtCardSearchMember2" : "Cari Anggota",
"txtCardSearchMember3" : "Tanggal Lahir",
"txtCardSearchMember4" : "Member ID",
"txtCardSearchMember5" : "Member",
"txtDialogMember1" : "Ringkasan Manfaat",
"txtDialogMember2" : "Request LOG",
"txtDialogMember3" : "Detail Member"
}

View File

@@ -4,12 +4,35 @@
"infoLogin": "Enter the registered account",
"txtLogin1" : "Sign in to Hospital Portal",
"txtLogin2" : "Enter your details below",
"txtCardSearchMember1" : "Guarantee Submission",
"txtCardSearchMember2" : "Find Member",
"txtCardSearchMember3" : "Date Birth",
"txtCardSearchMember1" : "Membership Query",
"txtCardSearchMember2" : "Search Member",
"txtCardSearchMember3" : "Date of Birth",
"txtCardSearchMember4" : "Member ID",
"txtCardSearchMember5" : "Member",
"txtDialogMember1" : "Benefit Summary",
"txtDialogMember1" : "Services",
"txtDialogMember2" : "Request LOG",
"txtDialogMember3" : "Member Detail"
"txtDialogMember3" : "Detail",
"txtDialogMember4" : "Please select services",
"txtDialogMember5" : "Submission Date",
"txtDateBirth" : "Date of Birth",
"txtGender" : "Gender",
"txtMaritalStatus" : "Marital Status",
"txtLanguage" : "Language",
"txtRelationship" : "Relationship",
"txtRequestDate" : "Request Date",
"txtMemberID" : "Member ID",
"txtClaimCode" : "Claim Code",
"txtRequestCode" : "Request Code",
"txtName" : "Name",
"txtStatus" : "Status",
"txtSearch" : "Search Name or Member ID...",
"txtAll" : "All",
"txtSubmissionDate" : "Submission Date",
"txtDataNotFound" : "Data Not Found",
"txtConditionDocument" : "Condition Document",
"txtDiagnosisDokument" : "Diagnosis Dokument",
"txtSupportingResultDocument" : "Supporting Result Document",
"txtAddResult" : "Add Result",
"txtServiceType" : "Service Type",
"txtAdditionalDocuments" : "Additional Documents"
}

View File

@@ -9,7 +9,30 @@
"txtCardSearchMember3" : "Tanggal Lahir",
"txtCardSearchMember4" : "Member ID",
"txtCardSearchMember5" : "Member",
"txtDialogMember1" : "Ringkasan Manfaat",
"txtDialogMember1" : "Layanan",
"txtDialogMember2" : "Request LOG",
"txtDialogMember3" : "Detail Member"
"txtDialogMember3" : "Detail",
"txtDialogMember4" : "Mohon pilih layanan",
"txtDialogMember5" : "Tanggal Pengajuan",
"txtDateBirth" : "Tanggal Lahir",
"txtGender" : "Jenis Kelamin",
"txtMaritalStatus" : "Status Perkawinan",
"txtLanguage" : "Bahasa",
"txtRelationship" : "Hubungan",
"txtRequestDate" : "Tanggal Permintaan",
"txtMemberID" : "ID Anggota",
"txtClaimCode" : "Kode Klaim",
"txtRequestCode" : "Kode Pengajuan",
"txtName" : "Nama",
"txtStatus" : "Status",
"txtSearch" : "Cari Nama atau ID Anggota...",
"txtAll" : "Semua",
"txtSubmissionDate" : "Tanggal Pengajuan",
"txtDataNotFound" : "Data Tidak Ditemukan",
"txtConditionDocument" : "Dokumen Kondisi",
"txtDiagnosisDokument" : "Dokumen Diagnosa",
"txtSupportingResultDocument" : "Dokumen Hasil Pendukung",
"txtAddResult" : "Tambah Hasil",
"txtServiceType" : "Tipe Layanan",
"txtAdditionalDocuments" : "Dokumen Tambahan"
}

View File

@@ -1,5 +1,5 @@
const getLocalizedData = async (locale) => {
const response = await fetch(`../public/image/${locale}.json`); // Mengambil file lokal berdasarkan bahasa yang dipilih
const response = await fetch(`/lang/${locale}.json`); // Mengambil file lokal berdasarkan bahasa yang dipilih
const data = await response.json();
return data;
};

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

@@ -17,7 +17,7 @@ export default function Logo({ disabledLink = false, sx }: Props) {
const logo = (
<Box sx={{ width: 40, height: 40, ...sx }}>
<img src="/logo/logo_single.svg" alt="LinkSehat" />
<img src="/logo/logo-linksehat.png" alt="LinkSehat" />
</Box>
);

View File

@@ -0,0 +1,389 @@
/* ---------------------------------- @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,
} 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';
import { Download, Search as SearchIcon, Upload } from '@mui/icons-material';
/* ---------------------------------- types --------------------------------- */
import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '../@types/table';
import { InputAdornment } from '@mui/material';
import GetAppIcon from '@mui/icons-material/GetApp';
import { LanguageContext } from '@/contexts/LanguageContext';
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: '#D1F1F1',
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: theme.palette.primary.main,
},
}));
/* -------------------------------------------------------------------------- */
export default function Table<T>({
headCells,
rows,
paginations,
orders,
loadings,
params,
filters,
filterStatus,
filterStartDate,
filterEndDate,
searchs,
exportReport,
}: TableListProps<T>) {
/* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
const isAsc = orders?.orderBy === property && orders?.order === 'asc';
orders?.setOrder(isAsc ? 'desc' : 'asc');
orders?.setOrderBy(property);
const parameters = Object.fromEntries([
...params.searchParams.entries(),
['order', isAsc ? 'desc' : 'asc'],
['orderBy', property],
]);
params.setAppliedParams(parameters);
};
const { localeData }: any = useContext(LanguageContext);
/* -------------------------------------------------------------------------- */
/* -------------------------- enchanced table head -------------------------- */
const EnhancedTableHead = () => {
const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
handleRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
{headCells &&
headCells.map((headCell, index) => (
<TableCell
key={index}
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
// @ts-ignore
align={headCell.align}
sx={{ padding: 2 }}
width={headCell.width ? headCell.width : 'auto'}
>
{headCell.isSort ? (
<TableSortLabel
active={orders?.orderBy === headCell.id}
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orders?.orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</Box>
) : null}
</TableSortLabel>
) : (
headCell.label
)}
</TableCell>
))}
</TableRow>
</TableHead>
);
};
/* -------------------------------------------------------------------------- */
/* ------------------------ button change pagination ------------------------ */
const onPageChangeHandle = async (
event: React.MouseEvent<HTMLButtonElement> | null,
newPage: number
) => {
const parameters = Object.fromEntries([
...params.searchParams.entries(),
['page', newPage + 1],
['per_page', paginations.rowsPerPage]
]);
paginations.setPage(newPage);
await new Promise((resolve) => setTimeout(resolve, 500));
params.setAppliedParams(parameters);
};
/* -------------------------------------------------------------------------- */
/* --------------------------- row page per limit --------------------------- */
const onRowsPerPageChangeHandle = async (event: React.ChangeEvent<HTMLInputElement>) => {
params.searchParams.delete('page');
const parameters = Object.fromEntries([
...params.searchParams.entries(),
['per_page', parseInt(event.target.value, 10)],
]);
paginations.setPage(0);
paginations.setRowsPerPage(parseInt(event.target.value, 10));
await new Promise((resolve) => setTimeout(resolve, 500));
params.setAppliedParams(parameters);
};
/* -------------------------------------------------------------------------- */
return (
// <Card>
<Grid container>
{/* Field 1 */}
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Grid container spacing={2}>
{filters && filters.useFilter ? (
<Fragment>
<Grid item xs={12} lg={3} xl={2}>
<FormControl fullWidth>
<InputLabel id="simple-division-select-lable">Division</InputLabel>
<Select
labelId="simple-division-select-lable"
id="division-select-lable"
value={filters.config.divisionValue}
label="Division"
onChange={filters.config.handleDivisionChange}
>
<MenuItem value="all">All</MenuItem>
{filters.config.divisionData.map((row: DivisionDataProps, index) => (
<MenuItem key={index} value={row.id}>
{row.name}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item xs={12} lg={9} xl={10}>
<form onSubmit={searchs.handleSearchSubmit}>
<TextField
id="search-input"
label="Search"
variant="outlined"
onChange={(event) => searchs.setSearchText(event.target.value)}
value={searchs.searchText}
fullWidth
/>
</form>
</Grid>
</Fragment>
) : null }
{searchs && searchs.useSearchs ? (
<Fragment>
{filterStatus && filterStatus.useFilter ? (
<Grid item xs={12} lg={10} xl={10}>
<form onSubmit={searchs.handleSearchSubmit}>
<TextField
id="search-input"
variant="outlined"
onChange={(event) => searchs.setSearchText(event.target.value)}
value={searchs.searchText}
fullWidth
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
placeholder={localeData.txtSearch}
/>
</form>
</Grid>
) :
<Grid item xs={12} lg={6} xl={6}>
<form onSubmit={searchs.handleSearchSubmit}>
<TextField
id="search-input"
variant="outlined"
onChange={(event) => searchs.setSearchText(event.target.value)}
value={searchs.searchText}
fullWidth
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
placeholder={localeData.txtSearch}
/>
</form>
</Grid>
}
</Fragment>
) : null }
{/* Start date */}
{filterStartDate && filterStartDate.useFilter ? (
<Grid item xs={12} lg={2} xl={2}>
<form onChange={(event) => filterStartDate.handleStartDateChange(event)}>
<TextField
id="date-input"
type="date"
variant="outlined"
value={filterStartDate.startDate}
fullWidth
label="Start Date"
InputLabelProps={{
shrink: true,
}}
/>
</form>
</Grid>
) : null }
{/* End Date */}
{filterEndDate && filterEndDate.useFilter ? (
<Grid item xs={12} lg={2} xl={2}>
<form onChange={(event) => filterEndDate.handleEndDateChange(event)}>
<TextField
id="date-input"
type="date"
variant="outlined"
value={filterEndDate.endDate}
fullWidth
label="End Date"
InputLabelProps={{
shrink: true,
}}
/>
</form>
</Grid>
) : null }
{/* Filter status */}
{filterStatus && filterStatus.useFilter ? (
<Grid item xs={12} lg={2} xl={2}>
<FormControl fullWidth>
<InputLabel id="simple-status-select-lable">Status</InputLabel>
<Select
labelId="simple-status-select-lable"
id="status-select-lable"
value={filterStatus.config.statusValue}
label="Status"
onChange={filterStatus.config.handleStatusChange}
>
<MenuItem value="all">{localeData.txtAll}</MenuItem>
{filterStatus.config.filterData.map((row: StatusDataProps, index) => (
<MenuItem key={index} value={row.id}>
{row.name}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
) : null }
{/* Export Report */}
{exportReport && exportReport.useExport ? (
<Grid item xs={12} lg={2} xl={2}>
<FormControl fullWidth>
<Button variant='contained' sx={{p:2}}>
<Download />
<Typography variant='inherit' sx={{marginLeft: 1}}>Export</Typography>
</Button>
</FormControl>
</Grid>
) : null }
</Grid>
</Grid>
{/* End Field 1 */}
{/* Field 2 */}
<Grid item xs={12}>
{/* Table */}
<TableContainer component={Paper}>
<TableContent aria-label="collapsible table" size="small">
{/* Table Header */}
<EnhancedTableHead />
{/* End Table Header */}
{/* Table Body */}
<TableBody>
{loadings.isLoading ? (
<TableRow>
<TableCell colSpan={headCells?.length} align="center">
Loading . . .
</TableCell>
</TableRow>
) : rows && rows.length >= 1 ? (
rows.map((row, rowIndex) => (
<TableRow key={rowIndex}>
{headCells &&
//@ts-ignore
headCells.map((head, headIndex) => (
//@ts-ignore
<TableCell align={head.align} key={headIndex}>
{row[head.id]}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={6} align="center">
{localeData.txtDataNotFound}
</TableCell>
</TableRow>
)}
</TableBody>
{/* End Table Body */}
</TableContent>
</TableContainer>
{/* End Table */}
{/* Pagination */}
<BaseTablePagination
count={paginations.paginationTable.total}
onPageChange={onPageChangeHandle}
page={paginations.page}
rowsPerPage={paginations.rowsPerPage}
onRowsPerPageChange={onRowsPerPageChangeHandle}
/>
{/* End Pagination */}
</Grid>
{/* End Field 2 */}
</Grid>
// </Card>
);
}

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

@@ -10,6 +10,7 @@ import {
Divider,
Stack,
CircularProgress,
ButtonBase,
} from '@mui/material';
import { Add } from '@mui/icons-material';
// components
@@ -17,7 +18,7 @@ import MuiDialog from '../../components/MuiDialog';
// theme
import palette from '../../theme/palette';
// React
import { ReactElement, useEffect, useState } from 'react';
import { ReactElement, useEffect, useState, useRef } from 'react';
import { fDate } from '@/utils/formatTime';
import { addMinutes, format } from 'date-fns';
import { LoadingButton } from '@mui/lab';
@@ -44,6 +45,7 @@ type MuiDialogProps = {
const steps = ['Review', 'Approval', 'Disbursement'];
const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
console.log(data);
const claim = data.claim ?? null;
// ---------------------------------------------
@@ -103,11 +105,11 @@ const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialog
<Typography variant="subtitle1" sx={{ height: 'max-content' }}>
Claim Request
</Typography>
<Stack>
<Typography variant="caption">Submission date</Typography>
<Stack direction="row" spacing={1}>
<Typography variant="caption">Submission Date</Typography>
{/* {JSON.stringify(data)} */}
<Typography variant="caption">
{claim.created_at && fDate(claim.created_at)}
<Typography variant="caption" sx={{fontWeight: 'bold'}}>
{claim.created_at && format(new Date(claim.created_at), "d MMM yyyy")}
</Typography>
</Stack>
</Stack>
@@ -122,47 +124,19 @@ const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialog
))}
</Stepper>
</Box>
{/* { claim.status == 'approved' && (
<Stack sx={{ marginTop: 4}}>
<LoadingButton loading={false}
variant="contained"
startIcon={<Add />}
fullWidth
// sx={{ typography: 'subtitle2', borderColor: '#F5F5F5' }}
onClick={() => {handleUploadInvoice()}}
>
Upload Invoice
</LoadingButton>
</Stack>
)} */}
<Stack direction="row" spacing={2} sx={{ marginTop: 2 }}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{/* ---------------------------------TYPE INFO------------------------------------ */}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Pengajuan Penjaminan
Request Claim
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<LoadingButton loading={false}
variant="contained"
startIcon={<Add />}
fullWidth
// sx={{ typography: 'subtitle2', borderColor: '#F5F5F5' }}
onClick={() => {}}
>
Upload Invoice
</LoadingButton>
</Stack>
</Card>
<Buttons variant="contained" type="invoice" />
</Stack>
</Stack>
{console.log(claim.histories_by_date)}
{claim.histories_by_date &&
claim.histories_by_date.map((historiesByDate) => (
@@ -292,8 +266,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">
@@ -322,4 +296,46 @@ const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialog
);
};
type ButtonIProp = {
row: ClaimSubmission;
type: 'invoice' | 'another' | 'lab';
handle: (event: any, row: any, type: any) => void;
};
const Buttons = ({ handle, row, type }: ButtonIProp) => {
const ref = useRef<HTMLInputElement>(null);
return (
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%',}} onClick={() => ref.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 Invoice
</Typography>
</Box>
<input
ref={ref}
hidden
accept="application/pdf"
type="file"
name="file"
multiple
onChange={(event) => handle(event, row, type)}
/>
</ButtonBase>
);
};
export default DialogDetailClaim;

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,42 @@
// 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}
onFocus={() => { (values[name] === '0') && setValue(name, '') }}
onBlur={() => { (values[name] === '') && setValue(name, '0') }}
{...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

@@ -35,14 +35,14 @@ function CollapseDrawerProvider({ children }: CollapseDrawerProviderProps) {
hover: false
});
// useEffect(() => {
// if (isDesktop) {
// setCollapse({
// click: false,
// hover: false
// });
// }
// }, [isDesktop]);
useEffect(() => {
if (isDesktop) {
setCollapse({
click: false,
hover: false
});
}
}, [isDesktop]);
const handleToggleCollapse = () => {
setCollapse({ ...collapse, click: !collapse.click });

View File

@@ -2,7 +2,7 @@ import { createContext, ReactNode, useEffect, useReducer } from 'react';
// utils
import axios from '@/utils/axios';
// import { isValidToken, setSession } from '@/utils/jwt';
import { setSession, getSession, getUser } from '@/utils/token';
import { setSession, getSession, setUser, getUser } from '@/utils/token';
// @types
import { ActionMap, AuthState, AuthUser, JWTContextType } from '@/@types/auth';
// ----------------------------------------------------------------------
@@ -135,6 +135,7 @@ function AuthProvider({ children }: AuthProviderProps) {
.then((response) => {
const { user, token } = response.data.data;
setSession(token);
setUser(user);
dispatch({
type: Types.Login,
@@ -168,6 +169,7 @@ function AuthProvider({ children }: AuthProviderProps) {
const logout = async () => {
setSession(null);
setUser(null);
dispatch({ type: Types.Logout });
};

View File

@@ -19,10 +19,10 @@ const MENU_OPTIONS = [
label: 'Profile',
linkTo: '/profile',
},
{
label: 'Settings',
linkTo: '/',
},
// {
// label: 'Settings',
// linkTo: '/',
// },
];
// ----------------------------------------------------------------------

View File

@@ -39,6 +39,7 @@ export default function LanguagePopover() {
const handleChangeLanguage = (language) => {
localStorage.setItem('currentLocale', language);
setCurrentLocale(language);
window.location.reload();
};
return (

View File

@@ -1,5 +1,5 @@
import { noCase } from 'change-case';
import { useState } from 'react';
import { useState, useEffect } from 'react';
// @mui
import {
Box,
@@ -14,23 +14,40 @@ import {
ListSubheader,
ListItemAvatar,
ListItemButton,
Stack
} from '@mui/material';
// utils
import { fToNow } from '@/utils/formatTime';
// _mock_
import { _notifications } from '@/_mock';
//import { _notifications } from '@/_mock';
// components
import Iconify from '@/components/Iconify';
import Scrollbar from '@/components/Scrollbar';
import MenuPopover from '@/components/MenuPopover';
import { IconButtonAnimate } from '@/components/animate';
import axios from '@/utils/axios';
import { useSnackbar } from 'notistack';
// ----------------------------------------------------------------------
export default function NotificationsPopover() {
const [notifications, setNotifications] = useState(_notifications);
const [notifications, setNotifications] = useState([]);
const {enqueueSnackbar} = useSnackbar();
const getDataNotifications = async () => {
axios
.get('notifications/1')
.then((response) => {
setNotifications(response.data.data.notifications);
})
.catch((error) => {
enqueueSnackbar(error.response.data.meta.message, {variant : "error"});
});
};
useEffect(() => {
getDataNotifications();
}, []);
const totalUnRead = notifications.filter((item) => item.isUnRead === true).length;
const totalUnRead = notifications.filter((item) => item.isUnRead === 1).length;
const [open, setOpen] = useState<HTMLElement | null>(null);
@@ -46,7 +63,7 @@ export default function NotificationsPopover() {
setNotifications(
notifications.map((notification) => ({
...notification,
isUnRead: false,
isUnRead: 0,
}))
);
};
@@ -77,52 +94,57 @@ export default function NotificationsPopover() {
</Typography>
</Box>
{totalUnRead > 0 && (
{/* {totalUnRead > 0 && (
<Tooltip title=" Mark all as read">
<IconButtonAnimate color="primary" onClick={handleMarkAllAsRead}>
<Iconify icon="eva:done-all-fill" width={20} height={20} />
</IconButtonAnimate>
</Tooltip>
)}
)} */}
</Box>
<Divider sx={{ borderStyle: 'dashed' }} />
<Scrollbar sx={{ height: { xs: 340, sm: 'auto' } }}>
<List
disablePadding
subheader={
<ListSubheader disableSticky sx={{ py: 1, px: 2.5, typography: 'overline' }}>
New
</ListSubheader>
}
>
{notifications.slice(0, 2).map((notification) => (
<NotificationItem key={notification.id} notification={notification} />
))}
</List>
{notifications && notifications.length > 0 ? (
<>
<List
disablePadding
subheader={
<ListSubheader disableSticky sx={{ py: 1, px: 2.5, typography: 'overline' }}>
New
</ListSubheader>
}
>
{notifications.slice(0, 2).map((notification) => (
<NotificationItem key={notification.id} notification={notification} getDataNotifications={getDataNotifications}/>
))}
</List>
<List
disablePadding
subheader={
<ListSubheader disableSticky sx={{ py: 1, px: 2.5, typography: 'overline' }}>
Before that
</ListSubheader>
}
>
{notifications.slice(2, 5).map((notification) => (
<NotificationItem key={notification.id} notification={notification} />
))}
</List>
<List
disablePadding
subheader={
<ListSubheader disableSticky sx={{ py: 1, px: 2.5, typography: 'overline' }}>
Before that
</ListSubheader>
}
>
{notifications.slice(2, 5).map((notification) => (
<NotificationItem key={notification.id} notification={notification} getDataNotifications={getDataNotifications}/>
))}
</List>
</>
): ''}
</Scrollbar>
<Divider sx={{ borderStyle: 'dashed' }} />
<Box sx={{ p: 1 }}>
{/* <Box sx={{ p: 1 }}>
<Button fullWidth disableRipple>
View All
</Button>
</Box>
</Box> */}
</MenuPopover>
</>
);
@@ -140,11 +162,31 @@ type NotificationItemProps = {
isUnRead: boolean;
};
function NotificationItem({ notification }: { notification: NotificationItemProps }) {
function NotificationItem({ notification, getDataNotifications }: { notification: NotificationItemProps, getDataNotifications: () => void}) {
const { avatar, title } = renderContent(notification);
const {enqueueSnackbar} = useSnackbar();
const handleClick = () => {
const data = {
'hospital_id' : 1,
'id' : notification.id,
};
if(notification.isUnRead)
{
axios
.post('set-read-notification', data)
.then((response) => {
enqueueSnackbar(response.data.meta.message, {variant : "success"});
getDataNotifications();
})
.catch((error) => {
enqueueSnackbar(error.response.data.meta.message, {variant : "error"});
});
}
};
return (
<ListItemButton
onClick={handleClick}
sx={{
py: 1.5,
px: 2.5,
@@ -182,12 +224,12 @@ function NotificationItem({ notification }: { notification: NotificationItemProp
function renderContent(notification: NotificationItemProps) {
const title = (
<Typography variant="subtitle2">
{notification.title}
<Typography component="span" variant="body2" sx={{ color: 'text.secondary' }}>
&nbsp; {noCase(notification.description)}
</Typography>
</Typography>
<Stack direction="column">
<Typography variant="subtitle2">{notification.title}</Typography>
<Typography component="span" variant="body2" sx={{ color: 'text.secondary' }}>
{notification.description}
</Typography>
</Stack>
);
if (notification.type === 'order_placed') {
@@ -212,7 +254,7 @@ function renderContent(notification: NotificationItemProps) {
title,
};
}
if (notification.type === 'mail') {
if (notification.type === 'mail' || notification.type === 'request_document') {
return {
avatar: (
<img

View File

@@ -93,8 +93,8 @@ export default function DashboardHeader({
<Stack direction="row" alignItems="center" spacing={{ xs: 0.5, sm: 1.5 }}>
<LanguagePopover />
{/*<NotificationsPopover />
<ContactsPopover />*/}
<NotificationsPopover />
{/* <ContactsPopover /> */}
<AccountPopover />
</Stack>
</Toolbar>

View File

@@ -4,7 +4,7 @@ import SvgIconStyle from '@/components/SvgIconStyle';
// ----------------------------------------------------------------------
const getIcon = (name: string) => (
<SvgIconStyle src={`/icons/${name}.svg`} sx={{ width: 1, height: 1 }} />
<SvgIconStyle src={`/image/${name}.svg`} sx={{ width: 1, height: 1 }} />
);
const ICONS = {
@@ -12,7 +12,7 @@ const ICONS = {
ecommerce: getIcon('ic_ecommerce'),
analytics: getIcon('ic_analytics'),
dashboard: getIcon('ic_dashboard'),
hospital: getIcon('ic_banking'),
ic_booking: getIcon('ic_booking'),
};
const navConfig = [
@@ -21,6 +21,9 @@ const navConfig = [
{
items: [{ title: 'Dashboard', path: '/dashboard', icon: ICONS.dashboard }],
},
{
items: [{ title: 'Claim', path: '/claim', icon: ICONS.ic_booking }],
},
// Membership
// ----------------------------------------------------------------------

View File

@@ -7,23 +7,26 @@ import { DocIllustration } from '@/assets';
export default function NavbarDocs() {
return (
<Stack
spacing={3}
sx={{ px: 5, pb: 5, mt: 10, width: 1, textAlign: 'center', display: 'block' }}
>
<DocIllustration sx={{ width: 1 }} />
<>
<Stack
spacing={3}
sx={{ px: 5, pb: 5, mt: 10, width: 1, textAlign: 'center', display: 'block' }}
>
<DocIllustration sx={{ width: 1 }} />
{/* <div>
<Typography gutterBottom variant="subtitle1">
Hi, Rayan Moran
</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
Need help?
<br /> Please check our docs
</Typography>
</div>
<div>
<Typography gutterBottom variant="subtitle1">
Hi, Rayan Moran
</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
Need help?
<br /> Please check our docs
</Typography>
</div>
<Button variant="contained">Documentation</Button> */}
</Stack>
<Button variant="contained">Documentation</Button>
<Typography variant='body2'>Hak Cipta © 2023 - 2024 Link Medis Sehat</Typography>
</Stack>
</>
);
}

View File

@@ -0,0 +1,119 @@
// @mui
import { Typography, Container, Grid, Card } from '@mui/material';
// hooks
import useSettings from '@/hooks/useSettings';
// components
import Page from '@/components/Page';
// theme
import CardNotification from '@/sections/dashboard/CardNotification';
import CardSearchMember from '@/sections/dashboard/CardSearchMember'
import { useContext, useEffect, useState } from 'react';
import axios from '@/utils/axios';
import { Stack } from '@mui/system';
import { Input } from '@mui/material';
//sections
import TableList from '@/sections/claim/TableList';
import { fDate } from '@/utils/formatTime';
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
import HeaderBreadcrumbs from "@/components/HeaderBreadcrumbs";
// ----------------------------------------------------------------------
// const [notifications, setNotifications] = useState([])
const itemList = [
{ info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '08:00 WIB' },
{ info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '09:00 WIB' },
{ info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '10:00 WIB' },
{ info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '11:00 WIB' },
];
// ----------------------------------------------------------------------
/* ---------------------------------- types --------------------------------- */
type PolicyProps = {
myLimit: {
balance: number;
total: number;
percentage: number;
};
lockLimit: {
balance: number;
percentage: number;
};
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ default data ------------------------------ */
const defaultPolicyData = {
myLimit: {
balance: 0,
total: 0,
percentage: 0,
},
lockLimit: {
balance: 0,
percentage: 0,
},
};
/* -------------------------------------------------------------------------- */
export default function Claim() {
const { themeStretch } = useSettings();
// const [tableData, setTableData] = useState([]);
const [policyData, setPolicyData] = useState<PolicyProps>(defaultPolicyData);
// TODO Remove This
//const [itemList, setItemList] = useState([]);
function handleDataLoaded(dataTable:any) {
let dummyData = [];
dataTable.map(function(data:any) {
if (data.status == 'approved') {
dummyData.push({
info: `LOG Approved for member ${data.member.full_name}`,
date: fDate(data.created_at, "dd MMMM"),
time: fDate(data.created_at, "HH:mm")
})
}
})
//setItemList(dummyData);
}
return (
<Page title="Claim">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Grid container spacing={2}>
{/* <Grid item xs={12} lg={12} md={12}>
<CardSearchMember/>
</Grid> */}
{/*<Grid item xs={12} lg={6} md={6}>
<CardNotification data={itemList} />
</Grid>*/}
<Grid item xs={12} lg={12} md={12}>
<HeaderBreadcrumbs
heading={'Claim'}
links={[
{
name: 'Dashboard',
href: '/dashboard',
},
{
name: 'Claim',
href: '/claim',
},
]}
/>
<Card>
<TableList/>
</Card>
</Grid>
</Grid>
</Container>
</Page>
);
}

View File

@@ -86,12 +86,12 @@ export default function Dashboard() {
<Page title="Dashboard">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Grid container spacing={2}>
<Grid item xs={12} lg={6} md={6}>
<CardSearchMember handleSubmitSuccess={() => {console.log('submit success')}}></CardSearchMember>
<Grid item xs={12} lg={12} md={12}>
<CardSearchMember/>
</Grid>
<Grid item xs={12} lg={6} md={6}>
{/*<Grid item xs={12} lg={6} md={6}>
<CardNotification data={itemList} />
</Grid>
</Grid>*/}
<Grid item xs={12} lg={12} md={12}>
<TableList dataLoaded={handleDataLoaded}/>
</Grid>

View File

@@ -76,6 +76,31 @@ export default function Router() {
path: 'dashboard',
element: <Dashboard />,
},
{
path: '/detail/:id',
element: <DetailClaimReport />,
},
],
},
{
path: '/',
element: (
<AuthProvider>
<AuthGuard>
<DashboardLayout />
</AuthGuard>
</AuthProvider>
),
children: [
{ element: <Navigate to="/claim" replace />, index: true },
{
path: 'claim',
element: <Claim />,
},
{
path: '/detail/:id',
element: <DetailClaimReport />,
},
],
},
@@ -97,4 +122,7 @@ const ForgetPassword = Loadable(lazy(() => import('@/pages/auth/ForgetPassword')
// Dashboard
const Dashboard = Loadable(lazy(() => import('@/pages/Dashboard')));
const Claim = Loadable(lazy(() => import('@/pages/Claim')));
const NotFound = Loadable(lazy(() => import('@/pages/Page404')));
const DetailClaimReport = Loadable(lazy(()=> import('@/sections/dashboard/Detail')));

View File

@@ -0,0 +1,365 @@
/* ---------------------------------- @mui ---------------------------------- */
import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from '@mui/material';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import TableComponent from '../../components/Table';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
//import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { fDate, fDateSuffix } from '../../utils/formatTime';
import Typography from '@mui/material/Typography';
import { format } from 'date-fns';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import HistoryIcon from '@mui/icons-material/History';
import SearchIcon from '@mui/icons-material/Search';
import Label from '../../components/Label';
import { enqueueSnackbar } from 'notistack';
import { LoadingButton, TabPanel } from "@mui/lab";
import { LanguageContext } from '@/contexts/LanguageContext';
import MuiDialog from '@/components/MuiDialog';
import DialogMember from '@/sections/dashboard/DialogMember';
import DialogClaimSubmit from '@/sections/dashboard/DialogClaimSubmit';
import { fPostFormat } from '@/utils/formatTime';
export default function TableList() {
const navigate = useNavigate();
const { localeData }: any = useContext(LanguageContext);
const [data, setData] = useState([]);
// Download LOG
async function handleDownloadLog(claimRequest:any) {
return axios
.get(`claim-requests/${claimRequest}/log`, {
responseType: 'blob',
})
.then((response) => {
window.open(URL.createObjectURL(response.data));
// setLoadingLog(false);
})
// .then((blobFile) => {
// new File([blobFile], 'asdads.pdf', { type: blobFile.type })
// setLoadingLog(false);
// })
.catch((response) => {
console.log(response);
enqueueSnackbar(response.message, { variant: 'error' });
// setLoadingLog(false);
});
}
/* -------------------------------------------------------------------------- */
/* setting up for the table */
/* -------------------------------------------------------------------------- */
const [isLoading, setIsLoading] = useState(true);
const loadings = {
isLoading: isLoading,
setIsLoading: setIsLoading,
};
/* ------------------------------ handle params ----------------------------- */
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const params = {
searchParams: searchParams,
setSearchParams: setSearchParams,
appliedParams: appliedParams,
setAppliedParams: setAppliedParams,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle order ------------------------------ */
const [order, setOrder] = useState<Order>('desc');
const [orderBy, setOrderBy] = useState('submission_date');
const orders = {
order: order,
setOrder: setOrder,
orderBy: orderBy,
setOrderBy: setOrderBy,
};
/* -------------------------------------------------------------------------- */
/* ---------------------------- handle pagination --------------------------- */
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
current_page: 0,
from: 0,
last_page: 0,
links: [],
path: '',
per_page: 0,
to: 0,
total: 0,
});
const paginations = {
page: page,
setPage: setPage,
rowsPerPage: rowsPerPage,
setRowsPerPage: setRowsPerPage,
paginationTable: paginationTable,
setPaginationTable: setPaginationTable,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (searchText === '') {
searchParams.delete('search');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
setAppliedParams(params);
}
};
const searchs = {
useSearchs: true,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* ------------------------------ handle filter ----------------------------- */
const [statusValue, setStatusValue] = useState('all');
const [filterData, setStatusData] = useState([]);
// handle status
const handleStatusChanges = (event: SelectChangeEvent) => {
setStatusValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('status');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['status', event.target.value as string],
]);
setAppliedParams(params);
}
};
const filterStatus = {
useFilter: true,
config: {
label: 'Status',
statusValue: statusValue,
filterData: filterData,
handleStatusChange: handleStatusChanges,
},
};
// handle start date
const [startDateValue, setStartDateValue] = useState('');
const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newStartDateValue = event.currentTarget.elements['date-input'].value;
setStartDateValue(newStartDateValue);
if (newStartDateValue === '') {
searchParams.delete('start_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['start_date', newStartDateValue]]);
setAppliedParams(params);
}
};
const filterStartDate = {
useFilter: true,
startDate: startDateValue,
setStartDate: setStartDateValue,
handleStartDateChange: handleStartDateChanges,
};
// handle end date
const [endDateValue, setEndDateValue] = useState('');
const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newEndDateValue = event.currentTarget.elements['date-input'].value;
setEndDateValue(newEndDateValue);
if (newEndDateValue === '') {
searchParams.delete('end_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['end_date', newEndDateValue]]);
setAppliedParams(params);
}
};
const filterEndDate = {
useFilter: true,
endDate: endDateValue,
setEndDate: setEndDateValue,
handleEndDateChange: handleEndDateChanges,
};
/* -------------------------------- headCell Claim -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'code',
align: 'left',
label: localeData.txtRequestCode,
isSort: true,
},
{
id: 'full_name',
align: 'left',
label: localeData.txtName,
isSort: true,
},
{
id: 'submission_date',
align: 'center',
label: localeData.txtSubmissionDate,
isSort: true,
},
{
id: 'service_type',
align: 'center',
label: localeData.txtServiceType,
isSort: true,
},
{
id: 'status',
align: 'center',
label: localeData.txtStatus,
isSort: true,
},
{
id: 'action',
align: 'right',
label: '',
isSort: false,
},
];
useEffect(() => {
getData();
}, [appliedParams, searchParams, order, orderBy, setSearchParams]);
function getData()
{
(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(`/get-claim-requests`, {
params: { ...parameters, type: 'final-log' },
});
setData(
response.data.data.map((obj: any) => ({
...obj,
status:
obj.status === 'requested' ? (
<Label color='primary'>
Request
</Label>
) : obj.status === 'approved' ? (
<Label color='success' >
Approval
</Label>
) : obj.status === 'declined' ? (
<Label color='error'>
Decline
</Label>
) : obj.status === 'reviewed' ? (
<Label color='info'>
Review
</Label>
) : (
<Label color='primary'>
Pending
</Label>
),
submission_date:
<Label>
{obj.submission_date ? fDateSuffix(obj.submission_date) : ''}
</Label>
,
action:
<TableMoreMenu actions={
<>
<MenuItem>
<Iconify icon="eva:eye-fill" />
View
</MenuItem>
</>
} />
}))
);
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);
}
const status:any = [
{"id": "requested", "name": "Request" },
{"id": "reviewed", "name": "Review" },
{"id": "approved", "name": "Approval" },
{"id": "declined", "name": "Decline" },
];
setStatusData(status)
setIsLoading(false);
})();
}
return (
<>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
filterStatus={filterStatus}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>
</>
);
}

View File

@@ -46,8 +46,8 @@ const ItemNotificationStyle = styled(Card)(({ theme }) => ({
// ----------------------------------------------------------------------
export default function CardSearchMember(handleSubmitSuccess) {
const { localeData } = useContext(LanguageContext);
export default function CardSearchMember(handleSubmitSuccess:()=> void) {
const { localeData }: any = useContext(LanguageContext);
const {enqueueSnackbar} = useSnackbar();
const [noPolis, setNoPolis] = useState('AW001-01');
@@ -69,7 +69,7 @@ export default function CardSearchMember(handleSubmitSuccess) {
.then((response) => {
setOpenDialogBenefit(true)
setCurrentMember(response.data.data)
setNameMember(response.data.data.name);
setNameMember(response.data.data.members.name);
})
.catch(({response}) => {
enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"})
@@ -88,59 +88,53 @@ export default function CardSearchMember(handleSubmitSuccess) {
alignItems="center"
sx={{ paddingBottom: 2, paddingTop: 1 }}
>
<Typography>
<Typography
<Typography
variant="body2"
component="span"
sx={{ display: 'flex', alignItems: 'center' }}
>
{localeData.txtCardSearchMember1}
</Typography>
</Typography>
</Stack>
<Stack gap={2}>
<TextField variant="outlined" label={localeData.txtCardSearchMember4} value={noPolis} onChange={(event) => {
<Stack spacing={2} direction="row" justifyContent="space-between">
<TextField
variant="outlined"
label={localeData.txtCardSearchMember4}
value={noPolis}
onChange={(event) => {
setNoPolis(event.target.value)
}} required></TextField>
}}
sx={{width:'40%'}}
required
/>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label={localeData.txtCardSearchMember3}
value={birthDate}
onChange={(newValue) => {
setTanggalLahir( (newValue));
onChange={(newValue:any) => {
setBirthDate( (newValue));
}}
inputFormat="dd-MM-yyyy"
renderInput={(params) => <TextField {...params} required/>}
renderInput={(params) => <TextField sx={{width:'40%'}} {...params} required/>}
/>
</LocalizationProvider>
<Stack gap={2} flexDirection="row">
<LoadingButton
variant="outlined"
sx={{
background: '#fff',
p: 1,
width: '100%',
}}
loading={loadingBenefit}
onClick={() => {
handleSearchMember()
}}
>
<Iconify icon="eva:eye-fill" marginRight={0.75} />
{localeData.txtCardSearchMember2}
</LoadingButton>
{/* <LoadingButton
variant="contained"
sx={{
p: 1,
width: '100%',
}}
loading={loadingClaim}
>
Ajukan Penjaminan
</LoadingButton> */}
</Stack>
<LoadingButton
sx={{
backgroundColor: '#19BBBB',
p: 1,
width: '20%',
color: '#FFFF'
}}
loading={loadingBenefit}
onClick={() => {
handleSearchMember()
}}
>
<Iconify icon="eva:search-fill" marginRight={0.75} sx={{width: '24px', height: '24px'}} />
{localeData.txtCardSearchMember2}
</LoadingButton>
</Stack>
</RootNotificationStyle>
{/*
@@ -150,7 +144,7 @@ export default function CardSearchMember(handleSubmitSuccess) {
openDialog={openDialogBenefit}
setOpenDialog={setOpenDialogBenefit}
content={DialogMember(currentMember, () => {setOpenDialogBenefit(false); handleSubmitSuccess()})}
maxWidth="md"
maxWidth="sm"
/>
</div>
);

View File

@@ -0,0 +1,67 @@
// mui
import { Container, Grid, Stack, Typography } from '@mui/material';
// components
import Page from '../../components/Page';
// utils
import useSettings from '../../hooks/useSettings';
// section
import CardFamilyInformation from '../../sections/alarm-center/user-profile/CardFamilyInformation';
// react
import { useNavigate, useParams } from 'react-router-dom';
import ButtonBack from '../../components/ButtonBack';
import { useEffect, useState, useContext } from 'react';
import axios from '../../utils/axios';
// pages
import DetailTimeline from '../../sections/dashboard/DetailTimeline';
import DetailStepper from '../../sections/dashboard/DetailStepper';
import { format } from 'date-fns';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
// ----------------------------------------------------------------------
export default function Detail() {
const navigate = useNavigate();
const { themeStretch } = useSettings();
const [data, setData] = useState();
const { id } = useParams();
useEffect(() => {
axios
.get('/detail-claim-requests/' + id)
.then((response) => {
setData(response.data);
})
.catch((error) => {
console.error(error);
});
}, []);
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}}>Detail</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}>
<DetailTimeline data={data}/>
</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,386 @@
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 { LoadingButton } from '@mui/lab';
import axios from '../../utils/axios';
import { makeFormData } from '@/utils/jsonToFormData';
import { enqueueSnackbar } from 'notistack';
import { useParams} from 'react-router-dom';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
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);
useEffect(() => {
if (data && data.data) {
setTimeline(data.data.timeline);
setRequestFile(data.data.request_files);
}
}, [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" spacing={2} 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>
</TimelineContent>
</TimelineItem>
</Timeline>
))}
</>
);
}

View File

@@ -0,0 +1,164 @@
import { styled } from '@mui/material/styles';
import Iconify from '@/components/Iconify';
import { fCurrency } from '@/utils/formatNumber';
import { LoadingButton } from '@mui/lab';
import { Avatar, Button, Divider, LinearProgress, linearProgressClasses, ButtonBase, Box } from '@mui/material';
import { Card } from '@mui/material';
import { Stack, Typography } from '@mui/material';
import { fPostFormat } from '@/utils/formatTime';
import axios from '@/utils/axios';
import { enqueueSnackbar } from 'notistack';
import { useRef, useState, useContext } from 'react';
import { makeFormData } from '@/utils/jsonToFormData';
import { format } from 'date-fns';
import { LanguageContext } from '@/contexts/LanguageContext';
export default function DialogClaimSubmit({ member, getData, onClose, handleSubmitSuccess }: any) {
const { localeData }: any = useContext(LanguageContext);
// ----------------------------------------------------------------------
// Files Result Tambahan
const fileAdditionalInput = useRef<HTMLInputElement>(null);
const [fileAdditionals, setFileAdditionals] = useState<any>([]);
const handleKondisiInputChange = (event:any) => {
if (event.target.files[0]) {
setFileAdditionals([...fileAdditionals, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeKondisiFiles = (filesState:any, index:any) => {
setFileAdditionals(
filesState.filter((file:any, fileIndex:any) => {
return fileIndex != index;
})
);
};
// --------------------------------------------------------------
// Submit Form
const [submitLoading, setSubmitLoading] = useState(false);
function submitRequestClaim() {
setSubmitLoading(true);
const formData = makeFormData({
request_logs_id: member.id,
service_code: member.service_code,
member_id: member.member_id,
additional_files: fileAdditionals
});
axios
.post('/claim-requests', formData)
.then((response) => {
enqueueSnackbar(response.data.meta.message ?? 'Berhasil membuat data', { variant: 'success' });
handleSubmitSuccess();
onClose({ someData: 'example data' }, getData);
})
.catch(({ response }) => {
enqueueSnackbar(response.data.meta.message ?? 'Something Went Wrong', { variant: 'error' });
})
.then(() => {
setSubmitLoading(false);
});
}
return (
<Stack>
<Stack direction="row" justifyContent={'end'} sx={{ marginBottom: 2, marginTop: 2 }} spacing={2}>
<Typography variant='body2' sx={{color: '#757575'}}>
{localeData.txtDialogMember5}
</Typography>
<Typography variant='body2' sx={{fontWeight:'bold'}}>{format(member?.submission_date ? new Date(member.submission_date) : new Date(), "d MMM yyyy")}</Typography>
</Stack>
<Card sx={{ p: 1, background: '#f4f6f8'}}>
<Stack direction="row">
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
alt={member?.full_name ?? ''}
sx={{ marginTop: 1, width: 48, height: 48 }}
/>
<Stack sx={{ p: 1 }}>
<Typography variant="body2">{member?.full_name ?? ''}</Typography>
<Typography variant="body2" sx={{color:'#637381'}}>{member?.no_polis ?? ''}</Typography>
</Stack>
</Stack>
</Card>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={4}
sx={{ marginY: 2, marginBottom: 6 }}
>
{/* -------------------------------Upload Dokumen Tambahan------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" sx={{fontWeight:'bold'}}>
{localeData.txtAdditionalDocuments}
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileAdditionals &&
fileAdditionals.map((file:any, index:any) => (
<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(fileAdditionals, index);
}}
></Iconify>
</Stack>
))}
</Stack>
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%', height: '60px'}} onClick={() => fileAdditionalInput.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">
{localeData.txtAddResult}
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileAdditionalInput}
style={{ display: 'none' }}
multiple
onChange={handleKondisiInputChange}
accept="application/pdf"
/>
</ButtonBase>
</Stack>
</Stack>
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2, backgroundColor: '#19BBBB' }}
onClick={() => {
submitRequestClaim();
}}
loading={submitLoading}
>
Submit Claim
</LoadingButton>
</Stack>
);
}

View File

@@ -0,0 +1,328 @@
import { styled } from '@mui/material/styles';
import Iconify from '@/components/Iconify';
import { fCurrency } from '@/utils/formatNumber';
import { LoadingButton } from '@mui/lab';
import { Avatar, Button, Divider, LinearProgress, linearProgressClasses, ButtonBase, Box } from '@mui/material';
import { Card } from '@mui/material';
import { Stack, Typography } from '@mui/material';
import { fPostFormat } from '@/utils/formatTime';
import axios from '@/utils/axios';
import { enqueueSnackbar } from 'notistack';
import { useRef, useState, useContext } from 'react';
import { makeFormData } from '@/utils/jsonToFormData';
import { format } from 'date-fns';
import { LanguageContext } from '@/contexts/LanguageContext';
export default function DialogFinalLog({ member, getData, onClose, handleSubmitSuccess }: any) {
const { localeData }: any = useContext(LanguageContext);
// ----------------------------------------------------------------------
// Files Diagnosa
const fileDiagnosaInput = useRef<HTMLInputElement>(null);
const [fileDiagnosas, setFileDiagnosas] = useState<any>([]);
const handleDiagnosaInputChange = (event:any) => {
if (event.target.files[0]) {
setFileDiagnosas([...fileDiagnosas, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeDiagnosaFiles = (filesState:any, index:any) => {
setFileDiagnosas(
filesState.filter((file:any, fileIndex:any) => {
return fileIndex != index;
})
);
};
// ----------------------------------------------------------------------
// Files Result Kondisi
const fileKondisiInput = useRef<HTMLInputElement>(null);
const [fileKondisis, setFileKondisis] = useState<any>([]);
const handleKondisiInputChange = (event:any) => {
if (event.target.files[0]) {
setFileKondisis([...fileKondisis, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeKondisiFiles = (filesState:any, index:any) => {
setFileKondisis(
filesState.filter((file:any, fileIndex:any) => {
return fileIndex != index;
})
);
};
// ----------------------------------------------------------------------
// Files Result Hasil Penunjang
const fileHasilPenunjangInput = useRef<HTMLInputElement>(null);
const [fileHasilPenunjangs, setFileHasilPenunjangs] = useState<any>([]);
const handleResultInputChange = (event:any) => {
if (event.target.files[0]) {
setFileHasilPenunjangs([...fileHasilPenunjangs, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeFiles = (filesState:any, index:any) => {
setFileHasilPenunjangs(
filesState.filter((file:any, fileIndex:any) => {
return fileIndex != index;
})
);
};
// --------------------------------------------------------------
// Submit Form
const [submitLoading, setSubmitLoading] = useState(false);
function submitRequestFinalLog() {
setSubmitLoading(true);
const formData = makeFormData({
request_logs_id: member.id,
result_files: fileHasilPenunjangs,
diagnosa_files: fileDiagnosas,
kondisi_files: fileKondisis
});
axios
.post('/request-final-log', formData)
.then((response) => {
enqueueSnackbar(response.data.meta.message ?? 'Berhasil membuat data', { variant: 'success' });
handleSubmitSuccess();
onClose({ someData: 'example data' }, getData);
})
.catch(({ response }) => {
enqueueSnackbar(response.data.meta.message ?? 'Something Went Wrong', { variant: 'error' });
})
.then(() => {
setSubmitLoading(false);
});
}
return (
<Stack>
<Stack direction="row" justifyContent={'end'} sx={{ marginBottom: 2, marginTop: 2 }} spacing={2}>
<Typography variant='body2' sx={{color: '#757575'}}>
{localeData.txtDialogMember5}
</Typography>
<Typography variant='body2' sx={{fontWeight:'bold'}}>{format(member?.submission_date ? new Date(member.submission_date) : new Date(), "d MMM yyyy")}</Typography>
</Stack>
<Card sx={{ p: 1, background: '#f4f6f8'}}>
<Stack direction="row">
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
alt={member?.full_name ?? ''}
sx={{ marginTop: 1, width: 48, height: 48 }}
/>
<Stack sx={{ p: 1 }}>
<Typography variant="body2">{member?.full_name ?? ''}</Typography>
<Typography variant="body2" sx={{color:'#637381'}}>{member?.no_polis ?? ''}</Typography>
</Stack>
</Stack>
</Card>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={4}
sx={{ marginY: 2, marginBottom: 6 }}
>
{/* -------------------------------Upload Dokumen Kondisi------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" sx={{fontWeight:'bold'}}>
{localeData.txtConditionDocument}
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileKondisis &&
fileKondisis.map((file:any, index:any) => (
<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);
}}
></Iconify>
</Stack>
))}
</Stack>
<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">
{localeData.txtAddResult}
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileKondisiInput}
style={{ display: 'none' }}
multiple
onChange={handleKondisiInputChange}
accept="application/pdf"
/>
</ButtonBase>
</Stack>
{/* -------------------------------Upload Dokumen Diagnosa------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" sx={{fontWeight:'bold'}}>
{localeData.txtDiagnosisDokument}
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileDiagnosas &&
fileDiagnosas.map((file:any, index:any) => (
<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);
}}
></Iconify>
</Stack>
))}
{/* <Stack direction="row" justifyContent={'space-between'}>
<Typography>Nama File .pdf</Typography>
<Iconify icon="eva:trash-2-outline" color={'darkred'}></Iconify>
</Stack> */}
</Stack>
{/* { JSON.stringify(filesResult) } */}
<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">
{localeData.txtAddResult}
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileDiagnosaInput}
style={{ display: 'none' }}
multiple
onChange={handleDiagnosaInputChange}
accept="application/pdf"
/>
</ButtonBase>
</Stack>
{/* -------------------------------Upload Dokumen Hasil Penunjang------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" sx={{fontWeight:'bold'}}>
{localeData.txtSupportingResultDocument}
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileHasilPenunjangs &&
fileHasilPenunjangs.map((file:any, index:any) => (
<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);
}}
></Iconify>
</Stack>
))}
{/* <Stack direction="row" justifyContent={'space-between'}>
<Typography>Nama File .pdf</Typography>
<Iconify icon="eva:trash-2-outline" color={'darkred'}></Iconify>
</Stack> */}
</Stack>
{/* { JSON.stringify(filesResult) } */}
<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">
{localeData.txtAddResult}
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileHasilPenunjangInput}
style={{ display: 'none' }}
multiple
onChange={handleResultInputChange}
accept="application/pdf"
/>
</ButtonBase>
</Stack>
</Stack>
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2, backgroundColor: '#19BBBB' }}
onClick={() => {
submitRequestFinalLog();
}}
loading={submitLoading}
>
Request Final LOG
</LoadingButton>
</Stack>
);
}

View File

@@ -1,7 +1,22 @@
// mui
import { styled } from '@mui/material/styles';
import { LoadingButton, TabPanel } from "@mui/lab";
import { Button, Card, Divider, Grid, LinearProgress, linearProgressClasses, Typography } from "@mui/material";
import {
Button,
Card,
Divider,
Grid,
LinearProgress,
linearProgressClasses,
Typography,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Collapse, } from "@mui/material";
import { Tab, Tabs } from "@mui/material";
import { Box, Stack } from "@mui/material";
import React, { useEffect, useState, useContext } from "react";
@@ -10,11 +25,15 @@ import { fPostFormat } from '@/utils/formatTime';
import { Avatar } from '@mui/material';
import Iconify from '@/components/Iconify';
import FormRequestClaim from './FormRequestClaim';
import FormRequestLog from './FormRequestLog';
import { LanguageContext } from '@/contexts/LanguageContext';
import { format } from 'date-fns';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
export default function DialogMember(member, handleSubmitSuccess) {
const { localeData } = useContext(LanguageContext);
const [currentTab, setCurrentTab] = useState('request')
export default function DialogMember(member:any, handleSubmitSuccess:() => void) {
const { localeData }: any = useContext(LanguageContext);
const [currentTab, setCurrentTab] = useState('request');
// ----------------------------------------------------------------------
@@ -40,10 +59,8 @@ export default function DialogMember(member, handleSubmitSuccess) {
},
}));
function TabPanel(props) {
function TabPanel(props:any) {
const { children, value, index, ...other } = props;
console.log('current', value)
return (
<div
role="tabpanel"
@@ -60,6 +77,15 @@ export default function DialogMember(member, handleSubmitSuccess) {
</div>
);
}
const [openRows, setOpenRows] = useState<any>({});
const handleRowToggle = (index:number) => {
setOpenRows((prevOpenRows:any) => ({
...prevOpenRows,
[index]: !prevOpenRows[index],
}));
};
return (
<div>
@@ -70,112 +96,100 @@ export default function DialogMember(member, handleSubmitSuccess) {
aria-label="wrapped label tabs example"
>
<Tab value="detail" label={localeData.txtDialogMember3} />
<Tab value="benefit" label={localeData.txtDialogMember1} />
<Tab value="request" label={localeData.txtDialogMember2} />
<Tab value="service" label={localeData.txtDialogMember1} />
{member?.type !== 'view' ? (
<Tab value="request" label={localeData.txtDialogMember2} />
) : ''}
</Tabs>
<TabPanel value={currentTab} index={'detail'}>
<Stack spacing={2}>
<Grid container sx={{ pb: 2, mb: 2, borderBottom: 0 }}>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Mapping ID
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Policy Number
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
NRIC
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
NIK
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Email
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
</Grid>
</Grid>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Birth Date
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Gender
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Marital Status
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Language
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Race
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Relationship
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
</Grid>
</Grid>
</Grid>
<Stack direction="column" spacing={2}>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">Member ID</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{ member?.members.member_id ?? '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">Policy Number</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{ member?.members.policy_id ?? '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">NRIC</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{member?.members.nik ?? '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">NIK</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{member?.members.nik ?? '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">Email</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{member?.members.email ?? '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">{localeData.txtDateBirth}</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{member?.members.birth_date ? format(new Date(member.members.birth_date), "d MMM yyyy") : '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">{localeData.txtGender}</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{member?.members.gender ?? '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">{localeData.txtMaritalStatus}</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{member?.members.marital_status ?? '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">{localeData.txtLanguage}</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{member?.members.language ?? '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">Race</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{member?.members.race ?? '-'}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{width:'50%'}} variant="body2">{localeData.txtRelationship}</Typography>
<Typography sx={{width:'50%', fontWeight: 'bold'}} variant="body2">{member?.members.relation_with_principal != '' ? member?.members.relation_with_principal : '-'}</Typography>
</Stack>
</Stack>
</TabPanel>
<TabPanel value={currentTab} index={'benefit'}>
<Grid container spacing={2}>
{ member && member?.current_plan?.corporate_benefits?.map((corporateBenefit, index) => {return (
<Grid item sm={6} key={index}>
<Card sx={{p: 2}}>
<Typography variant="body1" sx={{fontWeight: 500}}>{corporateBenefit.benefit.description}</Typography>
<Typography variant="body2">Member ID : {corporateBenefit.benefit.code}</Typography>
<Typography variant="body2" sx={{ marginTop: 2 }}>Yearly Limits</Typography>
<BorderLinearProgress variant="determinate" value={100 - (corporateBenefit.limit_amount ? ((corporateBenefit.usage ?? 0) / corporateBenefit.limit_amount) : 0)} sx={{ mb: 1 }} />
<Typography sx={{ textAlign: 'right'}}>{corporateBenefit.usage ?? 0} /
{corporateBenefit.limit_ammount < 9999999999 ? fCurrency(corporateBenefit.limit_amount ?? 0) : 'As Charge' }</Typography>
</Card>
</Grid>
)})}
</Grid>
<TabPanel value={currentTab} index={'service'}>
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
{member && member.groupServices && Object.keys(member.groupServices).map((serviceCode, index) => (
<TableBody key={index}>
<TableRow sx={{backgroundColor: '#FFFFFF', borderBottom: openRows[index] ? '' : '1px solid #e0e0e0'}}>
<TableCell align="left" sx={{fontWeight: 'bold', width: '95%'}}><Typography variant="subtitle1">{serviceCode}</Typography></TableCell>
<TableCell align="left" sx={{width: '5%'}}>
{openRows[index] ? (
<KeyboardArrowDownIcon sx={{ cursor: 'pointer' }} onClick={() => handleRowToggle(index)} />
) : (
<KeyboardArrowRightIcon sx={{ cursor: 'pointer' }} onClick={() => handleRowToggle(index)} />
)}
</TableCell>
</TableRow>
<TableRow sx={{display: openRows[index] ? '' : 'none', borderBottom: openRows[index] ? '1px solid #e0e0e0' : ''}}>
<TableCell colSpan={2}>
{/* COLLAPSIBLE ROW */}
<Collapse in={openRows[index]} timeout="auto" unmountOnExit>
<Grid container spacing={2}>
{/* Loop through the array for the current serviceCode */}
{member.groupServices[serviceCode].map((item:any, innerIndex:number) => (
<Grid item sm={6} key={innerIndex}>
<Card sx={{ p: 2 }}>
<Typography variant="body2" sx={{ fontWeight: 'bold' }}>{item.description}</Typography>
<Typography variant="body2" sx={{ color: '#919EAB' }}>{item.code}</Typography>
</Card>
</Grid>
))}
</Grid>
</Collapse>
</TableCell>
</TableRow>
</TableBody>
))}
</Table>
</TableContainer>
</TabPanel>
<TabPanel value={currentTab} index={'request'}>
<FormRequestClaim member={member} handleSubmitSuccess={handleSubmitSuccess} />
<FormRequestLog member={member} handleSubmitSuccess={handleSubmitSuccess} />
</TabPanel>
</Box>
</div>

View File

@@ -2,7 +2,7 @@ import { styled } from '@mui/material/styles';
import Iconify from '@/components/Iconify';
import { fCurrency } from '@/utils/formatNumber';
import { LoadingButton } from '@mui/lab';
import { Avatar, Button, Divider, LinearProgress, linearProgressClasses } from '@mui/material';
import { Avatar, Button, Divider, LinearProgress, linearProgressClasses, ButtonBase, Box } from '@mui/material';
import { Card } from '@mui/material';
import { Stack, Typography } from '@mui/material';
import { fPostFormat } from '@/utils/formatTime';
@@ -10,6 +10,7 @@ import axios from '@/utils/axios';
import { enqueueSnackbar } from 'notistack';
import { useRef, useState } from 'react';
import { makeFormData } from '@/utils/jsonToFormData';
import { format } from 'date-fns';
// TODO Fix any
export default function FormRequestClaim({ member, handleSubmitSuccess }) {
@@ -25,7 +26,7 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
},
}));
const [serviceCode, setServiceCode] = useState('IP');
const [serviceCode, setServiceCode] = useState('');
// ----------------------------------------------------------------------
// Files Diagnosa
@@ -95,9 +96,14 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
const [submitLoading, setSubmitLoading] = useState(false);
function submitRequest() {
if(serviceCode == '')
{
enqueueSnackbar('Please select services', { variant: 'error' });
return false;
}
setSubmitLoading(true);
const formData = makeFormData({
member_id: member.id,
member_id: member.members.id,
result_files: fileHasilPenunjangs,
diagnosa_files: fileDiagnosas,
kondisi_files: fileKondisis,
@@ -106,11 +112,11 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
axios
.post('/claim-requests', formData)
.then((response) => {
enqueueSnackbar(response.data.message ?? 'Berhasil membuat data', { variant: 'success' });
enqueueSnackbar(response.data.data.message ?? 'Berhasil membuat data', { variant: 'success' });
handleSubmitSuccess();
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
enqueueSnackbar(response.data.data.message ?? 'Something Went Wrong', { variant: 'error' });
})
.then(() => {
setSubmitLoading(false);
@@ -119,37 +125,51 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
return (
<Stack>
<Stack direction="row" justifyContent={'end'} sx={{ marginBottom: 2 }}>
<Typography textAlign={'right'} variant='body2'>
Submission Date : <br /> {fPostFormat(new Date(), 'dd/MM/yyyy')}
<Stack direction="row" justifyContent={'end'} sx={{ marginBottom: 2 }} spacing={2}>
<Typography variant='body2' sx={{color: '#757575'}}>
Submission Date
</Typography>
<Typography variant='body2' sx={{fontWeight:'bold'}}>{format(new Date(), "d MMM yyyy")}</Typography>
</Stack>
<Card sx={{ p: 1, background: '#f4f6f8', marginBottom: 2 }}>
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
<Typography variant="body1" sx={{fontWeight:'bold'}}>
Services
</Typography>
<Card sx={{ p: 1, background: '#f4f6f8', marginBottom: 2 }}>
<Stack direction="row" spacing={2}>
{member && member?.services?.map((dataServices, index) => (
<Button
key={index}
sx={{padding: 2, width: '100%'}}
color="primary"
variant={serviceCode === dataServices.service_code ? 'outlined' : ''}
onClick={() => { setServiceCode(dataServices.service_code) }}
>
{dataServices.name}
</Button>
))}
</Stack>
</Card>
<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 }}>
<Card sx={{ p: 1, background: '#f4f6f8'}}>
<Stack direction="row">
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
alt={member?.full_name ?? ''}
alt={member?.members.name ?? ''}
sx={{ marginTop: 1, width: 48, height: 48 }}
/>
<Stack sx={{ p: 1 }}>
<Typography>{member?.full_name ?? ''}</Typography>
<Typography>{member?.member_id ?? ''}</Typography>
<Typography variant="body2">{member?.members.name ?? ''}</Typography>
<Typography variant="body2" sx={{color:'#637381'}}>{member?.members.member_id ?? ''}</Typography>
</Stack>
</Stack>
</Card>
<Card sx={{ paddingY: 1, paddingX: 2 }}>
{/*<Card sx={{ paddingY: 1, paddingX: 2 }}>
<Typography variant="body1" sx={{ marginBottom: 1, fontWeight: 600 }}>
Total Limit
</Typography>
@@ -166,17 +186,17 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
{fCurrency(member?.current_plan?.usage ?? 0)} /{' '}
{member?.current_plan?.limit_rules < 9999999999 ? fCurrency(member?.current_plan?.limit_rules ?? 0) : 'As Charge' }
</Typography>
</Card>
</Card>*/}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={4}
sx={{ marginY: 2 }}
sx={{ marginY: 2, marginBottom: 6 }}
>
{/* -------------------------------Upload Dokumen Kondisi------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Dokumen Kondisi
<Typography variant="body1" sx={{fontWeight:'bold'}}>
Condition Document
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
@@ -197,36 +217,42 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
></Iconify>
</Stack>
))}
{/* <Stack direction="row" justifyContent={'space-between'}>
<Typography>Nama File .pdf</Typography>
<Iconify icon="eva:trash-2-outline" color={'darkred'}></Iconify>
</Stack> */}
</Stack>
{/* { JSON.stringify(filesResult) } */}
<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"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileKondisiInput.current.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
<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 Result
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileKondisiInput}
style={{ display: 'none' }}
multiple
onChange={handleKondisiInputChange}
accept="application/pdf"
/>
</ButtonBase>
</Stack>
{/* -------------------------------Upload Dokumen Diagnosa------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Dokumen Diagnosa
<Typography variant="body1" sx={{fontWeight:'bold'}}>
Diagnosis Dokument
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
@@ -253,30 +279,41 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
</Stack> */}
</Stack>
{/* { JSON.stringify(filesResult) } */}
<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"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileDiagnosaInput.current.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
<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>
</Stack>
{/* -------------------------------Upload Dokumen Hasil Penunjang------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Dokumen Hasil Penunjang
<Typography variant="body1" sx={{fontWeight:'bold'}}>
Supporting Result Document
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
@@ -303,36 +340,47 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
</Stack> */}
</Stack>
{/* { JSON.stringify(filesResult) } */}
<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"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileHasilPenunjangInput.current.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add File
</LoadingButton>
<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>
</Stack>
</Stack>
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2 }}
sx={{ marginTop: 2, p: 2, backgroundColor: '#19BBBB' }}
onClick={() => {
submitRequest();
}}
loading={submitLoading}
>
LOG Request
Request LOG
</LoadingButton>
</Stack>
);

View File

@@ -0,0 +1,128 @@
import { LoadingButton } from '@mui/lab';
import
{
Avatar,
FormControl,
InputLabel,
Select,
FormHelperText,
MenuItem
} from '@mui/material';
import { Card } from '@mui/material';
import { Stack, Typography } from '@mui/material';
import axios from '@/utils/axios';
import { enqueueSnackbar } from 'notistack';
import { useRef, useState, useContext } from 'react';
import { makeFormData } from '@/utils/jsonToFormData';
import { format } from 'date-fns';
import { LanguageContext } from '@/contexts/LanguageContext';
interface MemberType {
members: any;
services: any;
}
interface FormRequestClaimProps {
member: MemberType;
handleSubmitSuccess: () => void;
}
export default function FormRequestClaim({ member, handleSubmitSuccess }: FormRequestClaimProps) {
const { localeData }: any = useContext(LanguageContext);
const [serviceCode, setServiceCode] = useState<string>('');
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
function submitRequest() {
if(serviceCode == '')
{
enqueueSnackbar(localeData.txtDialogMember4, { variant: 'warning' });
return false;
}
setSubmitLoading(true);
const formData = {
member_id: member.members.id,
service_code: serviceCode
};
axios
.post('/request-log', formData)
.then((response) => {
console.log(response);
if (response && response.data && response.data.meta) {
enqueueSnackbar(response.data.meta.message, { variant: 'success' });
handleSubmitSuccess();
}
})
.catch(({ response }) => {
if (response && response.data && response.data.meta) {
enqueueSnackbar(response.data.meta.message, { variant: 'error' });
}
})
.then(() => {
setSubmitLoading(false);
});
}
interface MemberService {
service_code: string;
name: string;
}
return (
<Stack direction="column" spacing={4}>
<Stack direction="row" justifyContent={'end'} sx={{ marginBottom: 2 }} spacing={2}>
<Typography variant='body2' sx={{color: '#757575'}}>
{localeData.txtDialogMember5}
</Typography>
<Typography variant='body2' sx={{fontWeight:'bold'}}>{format(new Date(), "d MMM yyyy")}</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Stack spacing={2} sx={{width:'100%'}}>
<Typography variant='subtitle1'>{localeData.txtDialogMember1}*</Typography>
<FormControl>
<InputLabel htmlFor="service_type">
{localeData.txtDialogMember1}
</InputLabel>
<Select
id="service_type"
value={serviceCode}
fullWidth
label={localeData.txtDialogMember1}
onChange={(e) => {
setServiceCode(e.target.value);
}}
>
{member && member?.services?.map((item: MemberService, index:number) => (
<MenuItem key={index} value={item.service_code}>{item.name}</MenuItem>
))}
</Select>
<FormHelperText style={{ color: 'red' }}></FormHelperText>
</FormControl>
</Stack>
</Stack>
<Card sx={{ p: 1, background: '#f4f6f8'}}>
<Stack direction="row">
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
alt={member?.members.name ?? ''}
sx={{ marginTop: 1, width: 48, height: 48 }}
/>
<Stack sx={{ p: 1 }}>
<Typography variant="body2">{member?.members.name ?? ''}</Typography>
<Typography variant="body2" sx={{color:'#637381'}}>{member?.members.member_id ?? ''}</Typography>
</Stack>
</Stack>
</Card>
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2, backgroundColor: '#19BBBB' }}
onClick={() => {
submitRequest();
}}
loading={submitLoading}
>
{localeData.txtDialogMember2}
</LoadingButton>
</Stack>
);
}

View File

@@ -1,344 +1,59 @@
/* ---------------------------------- @mui ---------------------------------- */
import { styled } from '@mui/material/styles';
import {
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Button,
TableSortLabel,
Box,
IconButton,
Card,
Grid,
FormControl,
InputLabel,
Select,
MenuItem,
SelectChangeEvent,
Stack,
Typography,
LinearProgress,
linearProgressClasses,
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from '@mui/material';
/* ---------------------------------- axios --------------------------------- */
import axios from '@/utils/axios';
// import axios from 'axios';
import axios from '../../utils/axios';
import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '@/components/Iconify';
import BaseTablePagination from '@/components/BaseTablePagination';
import Iconify from '../../components/Iconify';
import TableComponent from '../../components/Table';
/* ---------------------------------- theme --------------------------------- */
import palette from '@/theme/palette';
import { useSearchParams } from 'react-router-dom';
// import { UserCurrentCorporateContext } from '@/contexts/UserCurrentCorporate';
import { fSplit } from '@/utils/formatNumber';
import { Chip } from '@mui/material';
import palette from '../../theme/palette';
//import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { fDate, fDateSuffix } from '../../utils/formatTime';
import Typography from '@mui/material/Typography';
import { format } from 'date-fns';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import HistoryIcon from '@mui/icons-material/History';
import SearchIcon from '@mui/icons-material/Search';
import Label from '../../components/Label';
import { enqueueSnackbar } from 'notistack';
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
import { LoadingButton, TabPanel } from "@mui/lab";
import { LanguageContext } from '@/contexts/LanguageContext';
import MuiDialog from '@/components/MuiDialog';
import DialogMember from './DialogMember';
import DialogFinalLog from './DialogFinalLog';
import { fPostFormat } from '@/utils/formatTime';
import TableListFinalLog from './TableListFinalLog';
/* ---------------------------------- types --------------------------------- */
type PaginationTableProps = {
current_page: number;
from: number;
last_page: number;
links: [];
path: string;
per_page: number;
to: number;
total: number;
};
type DataTableProps = {
fullName: string;
memberId: string;
division: string;
limit: {
current: number;
total: number;
percentage: number;
};
status: string;
};
type Order = 'asc' | 'desc';
interface HeadCell {
id: string;
align: string;
label: string;
isSort: boolean;
}
interface EnhancedTableProps {
onRequestSort: (event: React.MouseEvent<unknown>, property: string) => void;
order: Order;
orderBy: string;
}
type DivisionDataProps = {
id: number;
name: string;
};
/* -------------------------------------------------------------------------- */
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: '#D1F1F1',
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: theme.palette.primary.main,
},
}));
/* -------------------------------------------------------------------------- */
/* -------------------------- enchanced table head -------------------------- */
const headCells: readonly HeadCell[] = [
{
id: 'code',
align: 'left',
label: 'Request Code',
isSort: true,
},
{
id: 'member.name',
align: 'center',
label: 'Member',
isSort: false,
},
{
id: 'submission_date',
align: 'center',
label: 'Submission Date',
isSort: true,
},
{
id: 'log_url',
align: 'right',
label: 'Download LOG',
isSort: false,
},
{
id: 'status',
align: 'right',
label: 'Status',
isSort: false,
},
];
function EnhancedTableHead({ order, orderBy, onRequestSort }: EnhancedTableProps) {
const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
sortDirection={orderBy === headCell.id ? order : false}
// @ts-ignore
align={headCell.align}
sx={{ padding: 2 }}
>
{headCell.isSort ? (
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</Box>
) : null}
</TableSortLabel>
) : (
headCell.label
)}
</TableCell>
))}
<TableCell align="center">{''}</TableCell>
</TableRow>
</TableHead>
);
}
/* -------------------------------------------------------------------------- */
// TODO Don't use emit
export default function TableList(props: any) {
const [dataTable, setDataTable] = useState([]);
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
current_page: 0,
from: 0,
last_page: 0,
links: [],
path: '',
per_page: 0,
to: 0,
total: 0,
});
const [isLoading, setIsLoading] = useState(true);
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const [order, setOrder] = useState<Order>('asc');
const [orderBy, setOrderBy] = useState('name');
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
/* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
const isAsc = orderBy === property && order === 'asc';
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(property);
const params = Object.fromEntries([
...searchParams.entries(),
['order', isAsc ? 'desc' : 'asc'],
['orderBy', property],
]);
setAppliedParams(params);
};
/* -------------------------------------------------------------------------- */
/* ----------------------------- status field ----------------------------- */
const [filterStatus, setFilterStatus] = useState('All');
const [statusOptions, setStatusOptions] = useState(['All', 'Requested', 'Approved', 'Declined']);
const handleStatusChange = (event: SelectChangeEvent) => {
setFilterStatus(event.target.value as string);
if (event.target.value === 'All') {
searchParams.delete('status');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['status', event.target.value.toLowerCase() as string],
]);
setAppliedParams(params);
}
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ Search field ------------------------------ */
const [searchText, setSearchText] = useState('');
const [searchStatus, setSearchStatus] = useState('all');
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setIsLoading(true);
// Filter by Search Text
if (searchText === '') {
searchParams.delete('search');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
setAppliedParams(params);
}
await new Promise((resolve) => setTimeout(resolve, 500));
setIsLoading(false);
};
/* -------------------------------------------------------------------------- */
/* ------------------------ button change pagination ------------------------ */
const onPageChangeHandle = async (
event: React.MouseEvent<HTMLButtonElement> | null,
newPage: number
) => {
setIsLoading(true);
const params = Object.fromEntries([...searchParams.entries(), ['page', newPage + 1]]);
setPage(newPage);
await new Promise((resolve) => setTimeout(resolve, 500));
setAppliedParams(params);
setIsLoading(false);
};
/* -------------------------------------------------------------------------- */
/* --------------------------- row page per limit --------------------------- */
const onRowsPerPageChangeHandle = async (event: React.ChangeEvent<HTMLInputElement>) => {
setIsLoading(true);
searchParams.delete('page');
const params = Object.fromEntries([
...searchParams.entries(),
['per_page', parseInt(event.target.value, 10)],
]);
setRowsPerPage(parseInt(event.target.value, 10));
await new Promise((resolve) => setTimeout(resolve, 500));
setAppliedParams(params);
setIsLoading(false);
};
/* -------------------------------------------------------------------------- */
export default function TableList() {
const navigate = useNavigate();
const { localeData }: any = useContext(LanguageContext);
const [currentTab, setCurrentTab] = useState<string>('request_log');
useEffect(() => {
(async () => {
setIsLoading(true);
setCurrentTab('request_log')
}, [])
// const division = await axios.get(`${corporateValue}/division`);
// setDivisionData(division.data);
function handleChangeTab(event: React.SyntheticEvent, newValue: string) {
setCurrentTab(newValue)
}
const params =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
//const { corporateValue } = useContext(UserCurrentCorporateContext);
//const { corporateValue } = useContext(null);
const response = await axios.get(`/claim-requests`, {
params: { ...params },
});
const [data, setData] = useState([]);
setSearchParams(params);
setDataTable(response.data.data.data);
setPaginationTable(response.data.data);
setRowsPerPage(response.data.data.per_page);
setIsLoading(false);
// TODO Remove This
props.dataLoaded(response.data.data.data);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams]);
// ---------------------------------------------------------
// Dialog Detail Claim
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}) => {
console.log(data.data);
setCurrentClaim(data.data);
setLoadingClaimDetail(false);
})
.catch((err) => {
enqueueSnackbar(err.message, {variant: 'error'})
})
}
// -----------------------------------------------------------------
// Download LOG
async function handleDownloadLog(claimRequest) {
async function handleDownloadLog(claimRequest:any) {
return axios
.get(`claim-requests/${claimRequest.id}/log`, {
.get(`claim-requests/${claimRequest}/log`, {
responseType: 'blob',
})
.then((response) => {
@@ -350,184 +65,436 @@ export default function TableList(props: any) {
// setLoadingLog(false);
// })
.catch((response) => {
console.log(response);
enqueueSnackbar(response.message, { variant: 'error' });
// setLoadingLog(false);
});
}
/* -------------------------------------------------------------------------- */
/* setting up for the table */
/* -------------------------------------------------------------------------- */
const [isLoading, setIsLoading] = useState(true);
const loadings = {
isLoading: isLoading,
setIsLoading: setIsLoading,
};
/* ------------------------------ handle params ----------------------------- */
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const params = {
searchParams: searchParams,
setSearchParams: setSearchParams,
appliedParams: appliedParams,
setAppliedParams: setAppliedParams,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle order ------------------------------ */
const [order, setOrder] = useState<Order>('desc');
const [orderBy, setOrderBy] = useState('submission_date');
const orders = {
order: order,
setOrder: setOrder,
orderBy: orderBy,
setOrderBy: setOrderBy,
};
/* -------------------------------------------------------------------------- */
/* ---------------------------- handle pagination --------------------------- */
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
current_page: 0,
from: 0,
last_page: 0,
links: [],
path: '',
per_page: 0,
to: 0,
total: 0,
});
const paginations = {
page: page,
setPage: setPage,
rowsPerPage: rowsPerPage,
setRowsPerPage: setRowsPerPage,
paginationTable: paginationTable,
setPaginationTable: setPaginationTable,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (searchText === '') {
searchParams.delete('search');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
setAppliedParams(params);
}
};
const searchs = {
useSearchs: true,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* ------------------------------ handle filter ----------------------------- */
const [statusValue, setStatusValue] = useState('all');
const [filterData, setStatusData] = useState([]);
// handle status
const handleStatusChanges = (event: SelectChangeEvent) => {
setStatusValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('status');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['status', event.target.value as string],
]);
setAppliedParams(params);
}
};
const filterStatus = {
useFilter: true,
config: {
label: 'Status',
statusValue: statusValue,
filterData: filterData,
handleStatusChange: handleStatusChanges,
},
};
// handle start date
const [startDateValue, setStartDateValue] = useState('');
const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newStartDateValue = event.currentTarget.elements['date-input'].value;
setStartDateValue(newStartDateValue);
if (newStartDateValue === '') {
searchParams.delete('start_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['start_date', newStartDateValue]]);
setAppliedParams(params);
}
};
const filterStartDate = {
useFilter: true,
startDate: startDateValue,
setStartDate: setStartDateValue,
handleStartDateChange: handleStartDateChanges,
};
// handle end date
const [endDateValue, setEndDateValue] = useState('');
const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newEndDateValue = event.currentTarget.elements['date-input'].value;
setEndDateValue(newEndDateValue);
if (newEndDateValue === '') {
searchParams.delete('end_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['end_date', newEndDateValue]]);
setAppliedParams(params);
}
};
const filterEndDate = {
useFilter: true,
endDate: endDateValue,
setEndDate: setEndDateValue,
handleEndDateChange: handleEndDateChanges,
};
/* -------------------------------- headCell Request LOG -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'code',
align: 'left',
label: localeData.txtRequestCode,
isSort: true,
},
{
id: 'full_name',
align: 'left',
label: localeData.txtName,
isSort: true,
},
{
id: 'submission_date',
align: 'center',
label: localeData.txtSubmissionDate,
isSort: true,
},
{
id: 'status',
align: 'center',
label: localeData.txtStatus,
isSort: true,
},
{
id: 'action',
align: 'right',
label: '',
isSort: false,
},
];
function handleSearchMember(noPolis:any, birthDate:any) {
setLoadingClaim(false)
axios.post('/search-member', {
no_polis: noPolis,
birth_date: birthDate ? fPostFormat(birthDate, 'yyyy-MM-dd') : null,
type: 'view'
})
.then((response) => {
setOpenDialogBenefit(true)
setCurrentMember(response.data.data)
setNameMember(response.data.data.members.name);
})
.catch(({response}) => {
enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"})
})
.then(() => {
});
}
function handleRequestFinalLog(id:any, full_name:any, no_polis:any, submission_date:any)
{
setOpenDialogFinalLog(true);
const datas_view = {
'id' : id,
'full_name' : full_name,
'no_polis' : no_polis,
'submission_date' : submission_date
};
setDataViewFinalDialog(datas_view);
}
useEffect(() => {
getData();
}, [appliedParams, searchParams, order, orderBy, setSearchParams]);
function getData()
{
(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(`/get-request-log`, {
params: { ...parameters, type: 'request-log' },
});
setData(
response.data.data.map((obj: any) => ({
...obj,
status:
obj.status === 'requested' ? (
<Label color='primary'>
Request
</Label>
) : obj.status === 'approved' ? (
<Label color='success' >
Approval
</Label>
) : obj.status === 'declined' ? (
<Label color='error'>
Decline
</Label>
) : obj.status === 'reviewed' ? (
<Label color='info'>
Review
</Label>
) : (
<Label color='primary'>
Pending
</Label>
),
submission_date:
<Label>
{obj.submission_date ? fDateSuffix(obj.submission_date) : ''}
</Label>
,
action:
<TableMoreMenu actions={
<>
<MenuItem onClick={() => handleSearchMember(obj.no_polis, obj.birth_date) }>
<Iconify icon="eva:eye-fill" />
View
</MenuItem>
{obj.status === 'approved' ? (
<MenuItem onClick={() => handleDownloadLog(obj.claim_request_id)}>
<Iconify icon="eva:download-fill" />
Download LOG
</MenuItem>
):''}
{obj.final_log === 0 ? (
<MenuItem onClick={() => handleRequestFinalLog(obj.id, obj.full_name, obj.no_polis, obj.submission_date) }>
<Iconify icon="fa:file-text" />
Request Final LOG
</MenuItem>
):''}
</>
} />
}))
);
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);
}
const status:any = [
{"id": "requested", "name": "Request" },
{"id": "reviewed", "name": "Review" },
{"id": "approved", "name": "Approval" },
{"id": "declined", "name": "Decline" },
];
setStatusData(status)
setIsLoading(false);
})();
}
const RootNotificationStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: '1rem 0.5rem',
color: 'black',
borderRadius: 0,
backgroundColor: theme.palette.grey[200],
// maxHeight: '240px',
}));
function TabPanel(props:any) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<div>{children}</div>
</Box>
)}
</div>
);
}
// const [noPolis, setNoPolis] = useState('AW001-01');
//const [birthDate, setBirthDate] = useState('1991-01-10');
const [loadingBenefit, setLoadingBenefit] = useState(false);
const [loadingClaim, setLoadingClaim] = useState(false);
const [openDialogBenefit, setOpenDialogBenefit] = useState(false);
const [openDialogFinalLog, setOpenDialogFinalLog] = useState(false);
const [openDialogClaim, setOpenDialogClaim] = useState(false);
const [currentMember, setCurrentMember] = useState(null);
const [nameMember, setNameMember] = useState('');
const [setIsRequestFinalLog, isRequestFinalLog] = useState(false);
const [dataViewFinalDialog, setDataViewFinalDialog] = useState<any>(null);
return (
<Card>
<Grid container>
{/* Field 1 */}
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Grid container spacing={2}>
<Grid item xs={12} lg={3} xl={2}>
{/* <FormControl fullWidth>
<InputLabel id="simple-division-select-lable">Division</InputLabel>
<Select
labelId="simple-division-select-lable"
id="division-select-lable"
value={divisionValue}
label="Division"
onChange={handleDivisionChange}
>
<MenuItem value="all">All</MenuItem>
{divisionData.map((row: DivisionDataProps, index) => (
<MenuItem key={index} value={row.id}>
{row.name}
</MenuItem>
))}
</Select>
</FormControl> */}
</Grid>
<Grid item xs={12}>
<form onSubmit={handleSearchSubmit}>
<Grid container spacing={2}>
<Grid item xs={9}>
<TextField
id="search-input"
label="Search"
variant="outlined"
onChange={(event) => setSearchText(event.target.value)}
value={searchText}
fullWidth
/>
</Grid>
<Grid item xs={3}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Status</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={filterStatus}
label="Status"
onChange={handleStatusChange}
sx={{ width: '100%' }}
>
{statusOptions &&
statusOptions.map((option, index) => (
<MenuItem
value={option}
sx={{ textTransform: 'capitalize' }}
key={index}
>
{option}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
</Grid>
<input type="submit" style={{ display: 'none' }}></input>
</form>
</Grid>
</Grid>
</Grid>
{/* End Field 1 */}
{/* Field 2 */}
<Grid item xs={12}>
<TableContainer component={Paper}>
<Table aria-label="collapsible table" size="small">
<EnhancedTableHead
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
/>
<TableBody>
{isLoading ? (
<TableRow>
<TableCell colSpan={6} align="center">
Loading . . .
</TableCell>
</TableRow>
) : dataTable.length >= 1 ? (
dataTable.map((row: DataTableProps, index) => (
<TableRow key={index}>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="center">{row.member?.full_name ?? ''}</TableCell>
<TableCell align="center">{row.submission_date}</TableCell>
<TableCell align="right">
{row.status == 'approved' ? (
<Button
sx={{
backgroundColor: palette.light.grey[400],
color: palette.dark.grey[900],
paddingY: 0,
'&:hover': {
backgroundColor: 'rgba(84, 214, 44, 0.32)',
color: palette.dark.success.darker,
},
}}
onClick={() => {
handleDownloadLog(row);
}}
>
Download LOG
</Button>
) : (
<Typography>Belum Tersedia</Typography>
)}
</TableCell>
<TableCell align="right">
<Chip
color={
row.status == 'requested'
? 'default'
: row.status == 'approved'
? 'success'
: row.status == 'declined'
? 'error'
: 'default'
}
size="small"
label={row.status ?? 'unknown'}
sx={{ textTransform: 'capitalize' }}
/>
</TableCell>
<RootNotificationStyle sx={{ p: 2, height: 'auto' }}>
<Tabs
value={currentTab}
onChange={handleChangeTab}
aria-label="wrapped label tabs example"
>
<Tab value="request_log" label= "Request LOG" />
<Tab value="final_log" label= "Final LOG" />
</Tabs>
</RootNotificationStyle>
<TabPanel value={currentTab} index={'request_log'}>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
filterStatus={filterStatus}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>
</TabPanel>
<TabPanel value={currentTab} index={'final_log'}>
<TableListFinalLog/>
</TabPanel>
<MuiDialog
title={{name: nameMember}}
openDialog={openDialogBenefit}
setOpenDialog={setOpenDialogBenefit}
content={
DialogMember(currentMember, () => setOpenDialogBenefit(false))
}
maxWidth="sm"
/>
<TableCell align="right">
<IconButton
onClick={() => {
handleShowClaim(row);
}}
>
<Iconify icon="eva:eye-fill" />
</IconButton>
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={6} align="center">
No Data Found
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
{/* Pagination */}
<BaseTablePagination
count={paginationTable.total}
onPageChange={onPageChangeHandle}
page={page}
rowsPerPage={rowsPerPage}
onRowsPerPageChange={onRowsPerPageChangeHandle}
<MuiDialog
title={{name: dataViewFinalDialog?.full_name}}
openDialog={openDialogFinalLog}
setOpenDialog={setOpenDialogFinalLog}
content={
<DialogFinalLog
member={dataViewFinalDialog}
getData={getData}
onClose={(data:any, getData:any) => {
console.log('Data returned:', data);
getData();
setOpenDialogFinalLog(false);
}}
handleSubmitSuccess={() => {
}}
/>
</Grid>
{/* End Field 2 */}
</Grid>
<DialogDetailClaim
openDialog={openDialogDetailClaim}
setOpenDialog={setOpenDialogDetailClaim}
title={{ name: 'Claim Request Detail' }}
data={{ claim: currentClaim, isLoading: loadingClaimDetail, handleDownloadLog }}
></DialogDetailClaim>
}
maxWidth="sm"
/>
</Card>
);
}

View File

@@ -0,0 +1,441 @@
/* ---------------------------------- @mui ---------------------------------- */
import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from '@mui/material';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import TableComponent from '../../components/Table';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
//import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { fDate, fDateSuffix } from '../../utils/formatTime';
import Typography from '@mui/material/Typography';
import { format } from 'date-fns';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import HistoryIcon from '@mui/icons-material/History';
import SearchIcon from '@mui/icons-material/Search';
import Label from '../../components/Label';
import { enqueueSnackbar } from 'notistack';
import { LoadingButton, TabPanel } from "@mui/lab";
import { LanguageContext } from '@/contexts/LanguageContext';
import MuiDialog from '@/components/MuiDialog';
import DialogMember from './DialogMember';
import DialogClaimSubmit from './DialogClaimSubmit';
import { fPostFormat } from '@/utils/formatTime';
export default function TableListFinalLog() {
const navigate = useNavigate();
const { localeData }: any = useContext(LanguageContext);
const [data, setData] = useState([]);
// Download LOG
async function handleDownloadLog(claimRequest:any) {
return axios
.get(`claim-requests/${claimRequest}/log`, {
responseType: 'blob',
})
.then((response) => {
window.open(URL.createObjectURL(response.data));
// setLoadingLog(false);
})
// .then((blobFile) => {
// new File([blobFile], 'asdads.pdf', { type: blobFile.type })
// setLoadingLog(false);
// })
.catch((response) => {
console.log(response);
enqueueSnackbar(response.message, { variant: 'error' });
// setLoadingLog(false);
});
}
/* -------------------------------------------------------------------------- */
/* setting up for the table */
/* -------------------------------------------------------------------------- */
const [isLoading, setIsLoading] = useState(true);
const loadings = {
isLoading: isLoading,
setIsLoading: setIsLoading,
};
/* ------------------------------ handle params ----------------------------- */
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const params = {
searchParams: searchParams,
setSearchParams: setSearchParams,
appliedParams: appliedParams,
setAppliedParams: setAppliedParams,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle order ------------------------------ */
const [order, setOrder] = useState<Order>('desc');
const [orderBy, setOrderBy] = useState('submission_date');
const orders = {
order: order,
setOrder: setOrder,
orderBy: orderBy,
setOrderBy: setOrderBy,
};
/* -------------------------------------------------------------------------- */
/* ---------------------------- handle pagination --------------------------- */
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
current_page: 0,
from: 0,
last_page: 0,
links: [],
path: '',
per_page: 0,
to: 0,
total: 0,
});
const paginations = {
page: page,
setPage: setPage,
rowsPerPage: rowsPerPage,
setRowsPerPage: setRowsPerPage,
paginationTable: paginationTable,
setPaginationTable: setPaginationTable,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (searchText === '') {
searchParams.delete('search');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
setAppliedParams(params);
}
};
const searchs = {
useSearchs: true,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* ------------------------------ handle filter ----------------------------- */
const [statusValue, setStatusValue] = useState('all');
const [filterData, setStatusData] = useState([]);
// handle status
const handleStatusChanges = (event: SelectChangeEvent) => {
setStatusValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('status');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['status', event.target.value as string],
]);
setAppliedParams(params);
}
};
const filterStatus = {
useFilter: true,
config: {
label: 'Status',
statusValue: statusValue,
filterData: filterData,
handleStatusChange: handleStatusChanges,
},
};
// handle start date
const [startDateValue, setStartDateValue] = useState('');
const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newStartDateValue = event.currentTarget.elements['date-input'].value;
setStartDateValue(newStartDateValue);
if (newStartDateValue === '') {
searchParams.delete('start_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['start_date', newStartDateValue]]);
setAppliedParams(params);
}
};
const filterStartDate = {
useFilter: true,
startDate: startDateValue,
setStartDate: setStartDateValue,
handleStartDateChange: handleStartDateChanges,
};
// handle end date
const [endDateValue, setEndDateValue] = useState('');
const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newEndDateValue = event.currentTarget.elements['date-input'].value;
setEndDateValue(newEndDateValue);
if (newEndDateValue === '') {
searchParams.delete('end_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['end_date', newEndDateValue]]);
setAppliedParams(params);
}
};
const filterEndDate = {
useFilter: true,
endDate: endDateValue,
setEndDate: setEndDateValue,
handleEndDateChange: handleEndDateChanges,
};
/* -------------------------------- headCell Final LOG -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'code',
align: 'left',
label: localeData.txtRequestCode,
isSort: true,
},
{
id: 'full_name',
align: 'left',
label: localeData.txtName,
isSort: true,
},
{
id: 'submission_date',
align: 'center',
label: localeData.txtSubmissionDate,
isSort: true,
},
{
id: 'service_type',
align: 'center',
label: localeData.txtServiceType,
isSort: true,
},
{
id: 'status',
align: 'center',
label: localeData.txtStatus,
isSort: true,
},
{
id: 'action',
align: 'right',
label: '',
isSort: false,
},
];
function handleSearchMember(noPolis:any, birthDate:any) {
setLoadingClaim(false)
axios.post('/search-member', {
no_polis: noPolis,
birth_date: birthDate ? fPostFormat(birthDate, 'yyyy-MM-dd') : null,
type: 'view'
})
.then((response) => {
setOpenDialogBenefit(true)
setCurrentMember(response.data.data)
setNameMember(response.data.data.members.name);
})
.catch(({response}) => {
enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"})
})
.then(() => {
});
}
const [openDialogClaimSubmit, setOpenDialogClaimSubmit] = useState(false);
const [dataViewClaimSubmit, setDataViewClaimSubmit] = useState<any>(null);
function handleRequestClaimSubmit(member_id:any, service_code:any, id:any, full_name:any, no_polis:any, submission_date:any)
{
setOpenDialogClaimSubmit(true);
const datas_view = {
'member_id' : member_id,
'service_code' : service_code,
'id' : id,
'full_name' : full_name,
'no_polis' : no_polis,
'submission_date' : submission_date
};
setDataViewClaimSubmit(datas_view);
}
useEffect(() => {
getData();
}, [appliedParams, searchParams, order, orderBy, setSearchParams]);
function getData()
{
(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(`/get-final-log`, {
params: { ...parameters, type: 'final-log' },
});
setData(
response.data.data.map((obj: any) => ({
...obj,
status:
obj.status === 'requested' ? (
<Label color='primary'>
Request
</Label>
) : obj.status === 'approved' ? (
<Label color='success' >
Approval
</Label>
) : obj.status === 'declined' ? (
<Label color='error'>
Decline
</Label>
) : obj.status === 'reviewed' ? (
<Label color='info'>
Review
</Label>
) : (
<Label color='primary'>
Pending
</Label>
),
submission_date:
<Label>
{obj.submission_date ? fDateSuffix(obj.submission_date) : ''}
</Label>
,
action:
<TableMoreMenu actions={
<>
<MenuItem onClick={() => handleSearchMember(obj.no_polis, obj.birth_date) }>
<Iconify icon="eva:eye-fill" />
View
</MenuItem>
{obj.status === 'approved' ? (
<MenuItem onClick={() => handleDownloadLog(obj.claim_request_id)}>
<Iconify icon="eva:download-fill" />
Download LOG
</MenuItem>
):''}
{!obj.check_claim ? (
<MenuItem onClick={() => handleRequestClaimSubmit(obj.member_id, obj.service_code, obj.id, obj.full_name, obj.no_polis, obj.submission_date) }>
<Iconify icon="fa:file-text" />
Submit Claim
</MenuItem>
):''}
</>
} />
}))
);
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);
}
const status:any = [
{"id": "requested", "name": "Request" },
{"id": "reviewed", "name": "Review" },
{"id": "approved", "name": "Approval" },
{"id": "declined", "name": "Decline" },
];
setStatusData(status)
setIsLoading(false);
})();
}
const [loadingClaim, setLoadingClaim] = useState(false);
const [openDialogBenefit, setOpenDialogBenefit] = useState(false);
const [currentMember, setCurrentMember] = useState(null);
const [nameMember, setNameMember] = useState('');
return (
<>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
filterStatus={filterStatus}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>
<MuiDialog
title={{name: nameMember}}
openDialog={openDialogBenefit}
setOpenDialog={setOpenDialogBenefit}
content={
DialogMember(currentMember, () => setOpenDialogBenefit(false))
}
maxWidth="sm"
/>
<MuiDialog
title={{name: dataViewClaimSubmit?.full_name}}
openDialog={openDialogClaimSubmit}
setOpenDialog={setOpenDialogClaimSubmit}
content={
<DialogClaimSubmit
member={dataViewClaimSubmit}
getData={getData}
onClose={(data:any, getData:any) => {
console.log('Data returned:', data);
getData();
setOpenDialogClaimSubmit(false);
}}
handleSubmitSuccess={() => {
}}
/>
}
maxWidth="sm"
/>
</>
);
}

View File

@@ -0,0 +1,425 @@
/* ---------------------------------- @mui ---------------------------------- */
import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from '@mui/material';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import TableComponent from '../../components/Table';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
//import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { fDate, fDateSuffix } from '../../utils/formatTime';
import Typography from '@mui/material/Typography';
import { format } from 'date-fns';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import HistoryIcon from '@mui/icons-material/History';
import SearchIcon from '@mui/icons-material/Search';
import Label from '../../components/Label';
import { enqueueSnackbar } from 'notistack';
import { LoadingButton, TabPanel } from "@mui/lab";
import { LanguageContext } from '@/contexts/LanguageContext';
export default function TableList() {
const navigate = useNavigate();
const { localeData }: any = useContext(LanguageContext);
const [currentTab, setCurrentTab] = useState<string>('request_log');
useEffect(() => {
setCurrentTab('request_log')
}, [])
function handleChangeTab(event: React.SyntheticEvent, newValue: string) {
setCurrentTab(newValue)
}
//const { corporateValue } = useContext(UserCurrentCorporateContext);
//const { corporateValue } = useContext(null);
const [data, setData] = useState([]);
// Download LOG
async function handleDownloadLog(claimRequest) {
return axios
.get(`claim-requests/${claimRequest}/log`, {
responseType: 'blob',
})
.then((response) => {
window.open(URL.createObjectURL(response.data));
// setLoadingLog(false);
})
// .then((blobFile) => {
// new File([blobFile], 'asdads.pdf', { type: blobFile.type })
// setLoadingLog(false);
// })
.catch((response) => {
console.log(response);
enqueueSnackbar(response.message, { variant: 'error' });
// setLoadingLog(false);
});
}
/* -------------------------------------------------------------------------- */
/* setting up for the table */
/* -------------------------------------------------------------------------- */
const [isLoading, setIsLoading] = useState(true);
const loadings = {
isLoading: isLoading,
setIsLoading: setIsLoading,
};
/* ------------------------------ handle params ----------------------------- */
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const params = {
searchParams: searchParams,
setSearchParams: setSearchParams,
appliedParams: appliedParams,
setAppliedParams: setAppliedParams,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle order ------------------------------ */
const [order, setOrder] = useState<Order>('desc');
const [orderBy, setOrderBy] = useState('submission_date');
const orders = {
order: order,
setOrder: setOrder,
orderBy: orderBy,
setOrderBy: setOrderBy,
};
/* -------------------------------------------------------------------------- */
/* ---------------------------- handle pagination --------------------------- */
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
current_page: 0,
from: 0,
last_page: 0,
links: [],
path: '',
per_page: 0,
to: 0,
total: 0,
});
const paginations = {
page: page,
setPage: setPage,
rowsPerPage: rowsPerPage,
setRowsPerPage: setRowsPerPage,
paginationTable: paginationTable,
setPaginationTable: setPaginationTable,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (searchText === '') {
searchParams.delete('search');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
setAppliedParams(params);
}
};
const searchs = {
useSearchs: true,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* ------------------------------ handle filter ----------------------------- */
const [statusValue, setStatusValue] = useState('all');
const [filterData, setStatusData] = useState([]);
// handle status
const handleStatusChanges = (event: SelectChangeEvent) => {
setStatusValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('status');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['status', event.target.value as string],
]);
setAppliedParams(params);
}
};
const filterStatus = {
useFilter: true,
config: {
label: 'Status',
statusValue: statusValue,
filterData: filterData,
handleStatusChange: handleStatusChanges,
},
};
// handle start date
const [startDateValue, setStartDateValue] = useState('');
const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newStartDateValue = event.currentTarget.elements['date-input'].value;
setStartDateValue(newStartDateValue);
if (newStartDateValue === '') {
searchParams.delete('start_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['start_date', newStartDateValue]]);
setAppliedParams(params);
}
};
const filterStartDate = {
useFilter: true,
startDate: startDateValue,
setStartDate: setStartDateValue,
handleStartDateChange: handleStartDateChanges,
};
// handle end date
const [endDateValue, setEndDateValue] = useState('');
const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newEndDateValue = event.currentTarget.elements['date-input'].value;
setEndDateValue(newEndDateValue);
if (newEndDateValue === '') {
searchParams.delete('end_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['end_date', newEndDateValue]]);
setAppliedParams(params);
}
};
const filterEndDate = {
useFilter: true,
endDate: endDateValue,
setEndDate: setEndDateValue,
handleEndDateChange: handleEndDateChanges,
};
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'submission_date',
align: 'center',
label: localeData.txtRequestDate,
isSort: true,
},
{
id: 'member_id',
align: 'left',
label: localeData.txtMemberID,
isSort: true,
},
{
id: 'code',
align: 'left',
label: localeData.txtClaimCode,
isSort: true,
},
{
id: 'full_name',
align: 'left',
label: localeData.txtName,
isSort: true,
},
{
id: 'status',
align: 'center',
label: localeData.txtStatus,
isSort: true,
},
{
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(`/get-claim-requests`, {
params: { ...parameters, type: 'claim-request' },
});
setData(
response.data.data.map((obj: any) => ({
...obj,
status:
obj.status === 'requested' ? (
<Label color='primary'>
Request
</Label>
) : obj.status === 'approved' ? (
<Label color='success' >
Approval
</Label>
) : obj.status === 'declined' ? (
<Label color='error'>
Decline
</Label>
) : obj.status === 'reviewed' ? (
<Label color='info'>
Review
</Label>
) : (
<Label color='primary'>
Pending
</Label>
),
submission_date:
<Label>
{obj.submission_date ? fDateSuffix(obj.submission_date) : ''}
</Label>
,
action:
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate ('/detail/'+obj.claim_request_id)}>
<Iconify icon="eva:eye-fill" />
View
</MenuItem>
<MenuItem onClick={() => handleDownloadLog(obj.claim_request_id)}>
<Iconify icon="eva:download-fill" />
Download LOG
</MenuItem>
</>
} />
}))
);
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);
}
const status = [
{"id": "requested", "name": "Request" },
{"id": "reviewed", "name": "Review" },
{"id": "approved", "name": "Approval" },
{"id": "declined", "name": "Decline" },
]
setStatusData(status)
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams]);
const RootNotificationStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: '1rem 0.5rem',
color: 'black',
borderRadius: 0,
backgroundColor: theme.palette.grey[200],
// maxHeight: '240px',
}));
function TabPanel(props:any) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<div>{children}</div>
</Box>
)}
</div>
);
}
return (
<Card>
<RootNotificationStyle sx={{ p: 2, height: 'auto' }}>
<Tabs
value={currentTab}
onChange={handleChangeTab}
aria-label="wrapped label tabs example"
>
<Tab value="request_log" label= "Request LOG" />
<Tab value="final_log" label= "Final LOG" />
</Tabs>
</RootNotificationStyle>
<TabPanel value={currentTab} index={'request_log'}>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
filterStatus={filterStatus}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>
</TabPanel>
<TabPanel value={currentTab} index={'request_log'}>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
filterStatus={filterStatus}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>
</TabPanel>
</Card>
);
}

View File

@@ -1,13 +1,14 @@
import { format, getTime, formatDistanceToNow } from 'date-fns';
import { format, parseISO, getTime, setHours, setMinutes, formatDistanceToNow } from 'date-fns';
// ----------------------------------------------------------------------
export function fDate(date: Date | string | number, dateFormat = 'dd MMMM yyyy' ) {
return format(new Date(date), dateFormat);
export function fDate(date: Date | string | number) {
//console.log(date);
return format(new Date(date), 'dd MMMM yyyy');
}
export function fDateTime(date: Date | string | number) {
return format(new Date(date), 'dd MMM yyyy p');
return format(new Date(date), 'dd MMM yyyy hh:mm');
}
export function fTimestamp(date: Date | string | number) {
@@ -18,13 +19,30 @@ export function fDateTimeSuffix(date: Date | string | number) {
return format(new Date(date), 'dd/MM/yyyy hh:mm p');
}
export function fDateSuffix(date: Date | string | number) {
return format(new Date(date), 'dd MMM yyyy');
}
export function fToNow(date: Date | string | number) {
return formatDistanceToNow(new Date(date), {
addSuffix: true
addSuffix: true,
});
}
export function fPostFormat(date: Date | string | number, dateFormat = 'yyyy-MM-dd HH:mm:ss' ) {
export function fPostFormat(date: Date | string | number, dateFormat = 'yyyy-MM-dd HH:mm:ss') {
return format(new Date(date), dateFormat);
}
// export function fDateString(date) {
// const dateObj = parseISO(date);
// const formattedDate = format(dateObj, 'dd MMMM yyyy');
// return formattedDate;
// }
// export function fFormattedDateString(date : String) {
// console.log(date);
// const datePart = date.split(' ')[0]; // Memisahkan bagian tanggal
// const formattedDate = fDateString(datePart); // Menggunakan fungsi sebelumnya untuk memformat tanggal
// return formattedDate;
// }