Merge remote-tracking branch 'origin/master' into mhmfajar

This commit is contained in:
Muhammad Fajar
2022-12-05 16:30:10 +07:00
35 changed files with 2176 additions and 202 deletions

View File

@@ -47,6 +47,7 @@
"@mui/lab": "5.0.0-alpha.80",
"@mui/material": "^5.10.2",
"@mui/system": "^5.10.2",
"@mui/utils": "^5.10.16",
"@mui/x-data-grid": "^5.16.0",
"@mui/x-date-pickers": "5.0.0-beta.2",
"@vitejs/plugin-react": "^1.3.2",

View File

@@ -15,6 +15,7 @@ specifiers:
'@mui/lab': 5.0.0-alpha.80
'@mui/material': ^5.10.2
'@mui/system': ^5.10.2
'@mui/utils': ^5.10.16
'@mui/x-data-grid': ^5.16.0
'@mui/x-date-pickers': 5.0.0-beta.2
'@types/lodash': ^4.14.184
@@ -85,6 +86,7 @@ dependencies:
'@mui/lab': 5.0.0-alpha.80_vli2fzedtzvbon5ke6iprktoka
'@mui/material': 5.10.2_mm2isjkwxtdnw3xlwl5r7b6ile
'@mui/system': 5.10.2_ynexvg6j2j66sbh5giu7mkkh7u
'@mui/utils': 5.10.16_react@17.0.2
'@mui/x-data-grid': 5.16.0_r4jqxufjb3aftjrjm24vhpn4hm
'@mui/x-date-pickers': 5.0.0-beta.2_y3fv7pzpxqpbmxcbzsros3kjnu
'@vitejs/plugin-react': 1.3.2
@@ -1455,14 +1457,20 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
core-js-pure: 3.25.0
regenerator-runtime: 0.13.9
regenerator-runtime: 0.13.11
dev: true
/@babel/runtime/7.18.9:
resolution: {integrity: sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.9
regenerator-runtime: 0.13.11
/@babel/runtime/7.20.6:
resolution: {integrity: sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
/@babel/template/7.18.10:
resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==}
@@ -1554,7 +1562,7 @@ packages:
'@babel/core': 7.18.13
'@babel/helper-module-imports': 7.18.6
'@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.18.13
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
'@emotion/hash': 0.9.0
'@emotion/memoize': 0.8.0
'@emotion/serialize': 1.1.0
@@ -1838,10 +1846,10 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
'@emotion/is-prop-valid': 1.2.0
'@mui/types': 7.1.5_@types+react@17.0.48
'@mui/utils': 5.9.3_react@17.0.2
'@mui/utils': 5.10.16_react@17.0.2
'@popperjs/core': 2.11.6
'@types/react': 17.0.48
clsx: 1.2.1
@@ -1862,10 +1870,10 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
'@emotion/is-prop-valid': 1.2.0
'@mui/types': 7.1.5_@types+react@17.0.48
'@mui/utils': 5.9.3_react@17.0.2
'@mui/utils': 5.10.16_react@17.0.2
'@popperjs/core': 2.11.6
'@types/react': 17.0.48
clsx: 1.2.1
@@ -1924,7 +1932,7 @@ packages:
'@mui/base': 5.0.0-alpha.79_sk3eihvpffgp52mstba5zhq3vu
'@mui/material': 5.10.2_mm2isjkwxtdnw3xlwl5r7b6ile
'@mui/system': 5.10.2_ynexvg6j2j66sbh5giu7mkkh7u
'@mui/utils': 5.9.3_react@17.0.2
'@mui/utils': 5.10.16_react@17.0.2
'@mui/x-date-pickers': 5.0.0-alpha.0_ktjudfp74mtqzmc2e56ri5vvri
'@types/react': 17.0.48
clsx: 1.2.1
@@ -1964,7 +1972,7 @@ packages:
'@mui/core-downloads-tracker': 5.10.2
'@mui/system': 5.10.2_ynexvg6j2j66sbh5giu7mkkh7u
'@mui/types': 7.1.5_@types+react@17.0.48
'@mui/utils': 5.9.3_react@17.0.2
'@mui/utils': 5.10.16_react@17.0.2
'@types/react': 17.0.48
'@types/react-transition-group': 4.4.5
clsx: 1.2.1
@@ -1986,8 +1994,8 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.18.9
'@mui/utils': 5.9.3_react@17.0.2
'@babel/runtime': 7.20.6
'@mui/utils': 5.10.16_react@17.0.2
'@types/react': 17.0.48
prop-types: 15.8.1
react: 17.0.2
@@ -2006,7 +2014,7 @@ packages:
'@emotion/styled':
optional: true
dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
'@emotion/cache': 11.10.3
'@emotion/react': 11.10.0_u65opluo5sjgh3eblfliq6jsnm
'@emotion/styled': 11.10.0_mpqqkyoc5j3vv2sg2f5vubqi7u
@@ -2037,7 +2045,7 @@ packages:
'@mui/private-theming': 5.9.3_skqlhrap4das3cz5b6iqdn2lqi
'@mui/styled-engine': 5.10.2_2av45khroaevmcc27aulpaf2sy
'@mui/types': 7.1.5_@types+react@17.0.48
'@mui/utils': 5.9.3_react@17.0.2
'@mui/utils': 5.10.16_react@17.0.2
'@types/react': 17.0.48
clsx: 1.2.1
csstype: 3.1.0
@@ -2056,13 +2064,13 @@ packages:
'@types/react': 17.0.48
dev: false
/@mui/utils/5.9.3_react@17.0.2:
resolution: {integrity: sha512-l0N5bcrenE9hnwZ/jPecpIRqsDFHkPXoFUcmkgysaJwVZzJ3yQkGXB47eqmXX5yyGrSc6HksbbqXEaUya+siew==}
/@mui/utils/5.10.16_react@17.0.2:
resolution: {integrity: sha512-3MB/SGsgiiu9Z55CFmAfiONUoR7AAue/H4F6w3mc2LnhFQCsoVvXhioDPcsiRpUMIQr34jDPzGXdCuqWooPCXQ==}
engines: {node: '>=12.0.0'}
peerDependencies:
react: ^17.0.0 || ^18.0.0
dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
'@types/prop-types': 15.7.5
'@types/react-is': 17.0.3
prop-types: 15.8.1
@@ -2082,7 +2090,7 @@ packages:
'@babel/runtime': 7.18.9
'@mui/material': 5.10.2_mm2isjkwxtdnw3xlwl5r7b6ile
'@mui/system': 5.10.2_ynexvg6j2j66sbh5giu7mkkh7u
'@mui/utils': 5.9.3_react@17.0.2
'@mui/utils': 5.10.16_react@17.0.2
clsx: 1.2.1
prop-types: 15.8.1
react: 17.0.2
@@ -2117,7 +2125,7 @@ packages:
'@date-io/moment': 2.15.0
'@mui/material': 5.10.2_mm2isjkwxtdnw3xlwl5r7b6ile
'@mui/system': 5.10.2_ynexvg6j2j66sbh5giu7mkkh7u
'@mui/utils': 5.9.3_react@17.0.2
'@mui/utils': 5.10.16_react@17.0.2
clsx: 1.2.1
date-fns: 2.29.2
prop-types: 15.8.1
@@ -2165,7 +2173,7 @@ packages:
'@emotion/styled': 11.10.0_mpqqkyoc5j3vv2sg2f5vubqi7u
'@mui/material': 5.10.2_mm2isjkwxtdnw3xlwl5r7b6ile
'@mui/system': 5.10.2_ynexvg6j2j66sbh5giu7mkkh7u
'@mui/utils': 5.9.3_react@17.0.2
'@mui/utils': 5.10.16_react@17.0.2
'@types/react-transition-group': 4.4.5
clsx: 1.2.1
date-fns: 2.29.2
@@ -2708,7 +2716,7 @@ packages:
resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==}
engines: {node: '>=6.0'}
dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
'@babel/runtime-corejs3': 7.18.9
dev: true
@@ -2798,7 +2806,7 @@ packages:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'}
dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
cosmiconfig: 7.0.1
resolve: 1.22.1
@@ -3192,7 +3200,7 @@ packages:
/dom-helpers/5.2.1:
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
csstype: 3.1.0
dev: false
@@ -5100,7 +5108,7 @@ packages:
react: '>=16.6.0'
react-dom: '>=16.6.0'
dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
dom-helpers: 5.2.1
loose-envify: 1.4.0
prop-types: 15.8.1
@@ -5127,13 +5135,13 @@ packages:
resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
dev: true
/regenerator-runtime/0.13.9:
resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
/regenerator-runtime/0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
/regenerator-transform/0.15.0:
resolution: {integrity: sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==}
dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
dev: true
/regexp.prototype.flags/1.4.3:
@@ -5846,7 +5854,7 @@ packages:
'@apideck/better-ajv-errors': 0.3.6_ajv@8.11.0
'@babel/core': 7.18.13
'@babel/preset-env': 7.18.10_@babel+core@7.18.13
'@babel/runtime': 7.18.9
'@babel/runtime': 7.20.6
'@rollup/plugin-babel': 5.3.1_2uin6pbxavst3oir53roxbd5qi
'@rollup/plugin-node-resolve': 11.2.1_rollup@2.78.1
'@rollup/plugin-replace': 2.4.2_rollup@2.78.1

View File

@@ -12,3 +12,18 @@ export type LaravelPaginatedData = {
to?: number;
total: number;
}
export const LaravelPaginatedDataDefault = {
current_page: 1,
data: [],
path: "",
first_page_url: "",
last_page: 1,
last_page_url: "",
next_page_url: "",
prev_page_url: "",
per_page: 10,
from: 0,
to: 0,
total: 0
}

View File

@@ -0,0 +1,28 @@
import { Card, Paper, TableContainer } from "@mui/material";
import { LaravelPaginatedData } from "../@types/paginated-data";
import BasePagination from "./BasePagination";
type LaravelTableProps = {
isLoading: boolean;
lastRequest: number;
data: LaravelPaginatedData;
handlePageChange: void;
TableContent: any;
};
function LaravelTable(props: LaravelTableProps) {
return (
<Card>
<TableContainer component={Paper}>
{ props.TableContent }
</TableContainer>
{ !props.isLoading ?
(<BasePagination paginationData={props.data} onPageChange={props.handlePageChange}/>) :
(<div></div>)
}
</Card>
)
}
export default LaravelTable

View File

@@ -0,0 +1,56 @@
import { Dialog, DialogTitle, DialogContent, Stack, Typography, IconButton } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { ReactElement } from 'react';
import Iconify from './Iconify';
// ----------------------------------------------------------------------
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
maxWidth?: string;
};
// ----------------------------------------------------------------------
const MuiDialog = ({ title, openDialog, setOpenDialog, content, maxWidth }: MuiDialogProps) => {
const handleClose = () => {
setOpenDialog(false);
};
let maxWidthDialog = 'md';
if (maxWidth) {
maxWidthDialog = maxWidth;
}
return (
<Dialog open={openDialog} onClose={handleClose} fullWidth={true} maxWidth={maxWidthDialog}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
{title?.icon ? (
<Stack direction="row">
<Iconify icon={title?.icon} width={25} height={25} sx={{ marginRight: '10px' }} />
<Typography variant="h6">{title?.name}</Typography>
</Stack>
) : (
<Typography variant="h6">{title?.name ? title?.name : ''}</Typography>
)}
<IconButton sx={{ color: '#FFF' }} onClick={handleClose}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent sx={{ backgroundColor: '#F9FAFB' }}>
{content ? content : 'Testing Content Dialog'}
</DialogContent>
</Dialog>
);
};
export default MuiDialog;

View File

@@ -0,0 +1,275 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Typography,
LinearProgress,
linearProgressClasses,
Stack,
FormControlLabel,
TableRow,
TableCell,
Table,
TableBody,
TextField,
Button,
} from '@mui/material';
import { LoadingButton } from '@mui/lab';
import Checkbox from '@mui/material/Checkbox';
// components
import { FormProvider, RHFTextField } from '../../components/hook-form';
// React
import React, { ReactElement, useEffect, useState } from 'react';
import { fCurrency } from '../../utils/formatNumber';
// yup
import * as Yup from 'yup';
// form
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import MuiDialog from '../MuiDialog';
import { Member } from '../../@types/member';
import { LaravelPaginatedDataDefault } from '../../@types/paginated-data';
import DataTable from '../LaravelTable';
import axios from '../../utils/axios';
// ----------------------------------------------------------------------
type DataContent = {
info: string;
date: string;
time: string;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
onSelect?: Function;
};
type FormValuesProps = {
topup: string;
};
// ----------------------------------------------------------------------
const testData = {
companyName: 'PT. Aman Mineral',
policyNumber: 12345678,
totalMembers: 50,
totalCases: 100,
totalPersen: 75,
myLimit: '375.000.000',
totalLimit: 500000000,
};
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
},
}));
// ----------------------------------------------------------------------
export default function MemberSelectDialog({
openDialog,
setOpenDialog,
onSelect
}: MuiDialogProps) {
const [dataTableLoading, setDataTableLoading] = useState(false)
const [dataTableData, setDataTableData] = useState(LaravelPaginatedDataDefault)
const [searchFilter, setSearchFilter] = useState({
search: ''
})
const handleSearchChange = (event: any) => {
setSearchFilter({...searchFilter, ...{search: event.target.value}})
}
const loadDataTableData = async (appliedFilter : any | null = null) => {
setDataTableLoading(true);
const filter = appliedFilter ? appliedFilter : {};
const response = await axios.get('/members', { params: filter });
// console.log(response.data);
setDataTableLoading(false);
setDataTableData(response.data);
}
const applyFilter = async (searchFilter: {search: string}) => {
await loadDataTableData(searchFilter);
}
const handleFilterSubmit = (event: any) => {
event.preventDefault();
applyFilter(searchFilter)
}
useEffect(() => {
console.log('effect openDialog');
if (openDialog === false) {
}
}, [openDialog]);
const handlePageChange = () => {
console.log('handle change')
}
// Called on every row to map the data to the columns
function createData(member: Member): Member {
return {
...member,
};
}
const Row = (props: { row: ReturnType<typeof createData>, onSelect }) => (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
{/* <TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell> */}
{/* { columns.map((column, index) =>
<TableCell key={ index } align={ column.align } minwidth={ column.minWidth }>{ row[column.id] ?? '-' }</TableCell>
) } */}
{/* TODO FIX DISPLAY DATA FROM RELATION */}
{/* { id: 'member_id', label: 'MemberID', minWidth: 100, align: "left" },
{ id: 'principal_id', label: 'Mapping ID', minWidth: 100, align: "left" },
{ id: 'nik', label: 'NIK', minWidth: 100, align: "left" },
{ id: 'current_policy.policy_number', label: 'Policy Number', minWidth: 100, align: "left" },
{ id: 'effective_date', label: 'Effective Date', minWidth: 100, align: "left" },
{ id: 'name', label: 'Name', minWidth: 100, align: "left" },
{ id: 'nric', label: 'NRIC', minWidth: 100, align: "left" },
{ id: 'email', label: 'E-mail', minWidth: 100, align: "left" },
{ id: 'plan_id', label: 'PlanID', minWidth: 100, align: "left" },
{ id: 'termination_date', label: 'Termination Date', minWidth: 100, align: 'right' },
{ id: 'activation_date', label: 'Activation Date', minWidth: 100, align: "right" },
*/}
<TableCell align="left">{props.row.member_id}</TableCell>
<TableCell align="left">{props.row.principal_id}</TableCell>
<TableCell align="left">{props.row.employeds?.[0]?.nik}</TableCell>
<TableCell align="left">{props.row.current_policy?.policy_id}</TableCell>
<TableCell align="left">{props.row.current_policy?.start}</TableCell>
<TableCell align="left">{props.row.name}</TableCell>
<TableCell align="left">{props.row.nric}</TableCell>
<TableCell align="left">{props.row.email}</TableCell>
<TableCell align="left">{props.row.current_plan?.code}</TableCell>
<TableCell align="left">{props.row.current_policy?.start}</TableCell>
<TableCell align="left">{props.row.current_policy?.end}</TableCell>
<TableCell align="right" style={{ position: 'sticky', right: 0}}><Button variant="outlined" color="success" style={{backgroundColor: "#ffffff"}} size="small" onClick={() => {onSelect(props.row); setOpenDialog(false); }}>Select</Button></TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
{/* <TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={10}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ borderBottom: 1 }}>
<Typography variant="body2" gutterBottom component="div">
No Extra Data
</Typography>
</Box>
{false && <Box sx={{ margin: 1 }} />}
</Collapse>
</TableCell>
</TableRow> */}
</React.Fragment>
);
const headStyle = {
fontWeight: 'bold',
};
const TableContent = () => (
<Table aria-label="collapsible table">
{/* ------------------ TABLE HEADER ------------------ */}
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left" />
<TableCell style={headStyle} align="left">
Type
</TableCell>
<TableCell style={headStyle} align="left">
Code
</TableCell>
<TableCell style={headStyle} align="left">
Name
</TableCell>
<TableCell style={headStyle} align="left">
Version
</TableCell>
<TableCell style={headStyle} align="right">
Status
</TableCell>
<TableCell style={headStyle} align="right">
Action
</TableCell>
</TableRow>
</TableBody>
{/* ------------------ END TABLE HEADER ------------------ */}
{/* ------------------ TABLE ROW ------------------ */}
{dataTableLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
Loading
</TableCell>
</TableRow>
</TableBody>
) : dataTableData.data.length === 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
No Data
</TableCell>
</TableRow>
</TableBody>
) : (
<TableBody>
{dataTableData.data.map((row) => (
<Row key={row.id} row={row} onSelect/>
))}
</TableBody>
)}
{/* ------------------ END TABLE ROW ------------------ */}
</Table>
);
const getContent = () => (
<Stack spacing={1} marginTop={2}>
<form onSubmit={handleFilterSubmit}>
<TextField label="Search" variant="outlined" fullWidth onChange={handleSearchChange} value={searchFilter.search} autoFocus/>
</form>
<DataTable
isLoading={dataTableLoading}
lastRequest={0}
data={dataTableData}
handlePageChange={handlePageChange}
TableContent={<TableContent />}
/>
</Stack>
);
return (
<MuiDialog
title={{name: "Select Member"}}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
maxWidth="xl"
/>
);
}

View File

@@ -62,9 +62,10 @@ const navConfig = [
},
{
title: 'CASE MANAGEMENT',
children: [
{ title: 'Request', path: '/case-request' },
],
path: '/claims',
// children: [
// { title: 'Request', path: '/case-request' },
// ],
},
{
title: 'CUSTOMER SERVICES',

View File

@@ -0,0 +1,413 @@
import * as Yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { Autocomplete, Button, Card, Collapse, Divider, Grid, Stack, Table, TableBody, TableCell, TableRow, TextField, Typography } from '@mui/material';
import { Controller, useForm } from 'react-hook-form';
import { useParams, useNavigate } from 'react-router-dom';
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
import { FormProvider, RHFCheckbox, RHFSelect, RHFTextField } from '../../components/hook-form';
import Page from '../../components/Page';
import useSettings from '../../hooks/useSettings';
import { useEffect, useMemo, useRef, useState } from 'react';
import MemberSelectDialog from '../../components/dialogs/MemberSelectDialog';
import { styled } from '@mui/system';
import axios from '../../utils/axios';
import { enqueueSnackbar } from 'notistack';
import { LoadingButton } from '@mui/lab';
import { fCurrency } from '../../utils/formatNumber';
import Iconify from '../../components/Iconify';
export default function ClaimsCreate() {
const navigate = useNavigate();
const [member, setMember] = useState();
const selectedMemberDisplay = useRef<HTMLInputElement>(null);
const NewClaimSchema = Yup.object().shape({
member_id: Yup.string().required('Please select Member'),
total_claim: Yup.number().typeError('Claim should be a number').min(1, 'Claim cannot 0').required('Total Claim is required'),
diagnosis: Yup.object().required('Choose Diagnosis'),
benefit: Yup.object().required('Please Select Benefit')
});
const defaultValues = useMemo(
() => ({
member_id: null,
benefit_id: null,
diagnosis_id: null,
total_claim: 0,
benefit: null
}),
[]
);
const methods = useForm({
resolver: yupResolver(NewClaimSchema),
defaultValues,
});
const {
reset,
watch,
control,
setValue,
getValues,
setError,
handleSubmit,
formState: { isSubmitting },
} = methods;
const onSubmit = async (data: any) => {
axios.post('claims', getValues())
.then(function(res) {
console.log('SUCCESS', res)
enqueueSnackbar('Success Creating Claim', { variant: 'success' })
navigate('/claims');
})
.catch(function (err) {
console.log('ERROR CUK', err)
enqueueSnackbar('Failed Creating Claim : '+ err.response.data.message, { variant: 'error' })
})
};
const [memberBenefits, setMemberBenefits] = useState([]);
const getMemberBenefits = (member) => {
axios.get(`members/${member.id}/benefits`)
.then( (res) => {
setMemberBenefits(res.data);
})
.catch( (err) => {
enqueueSnackbar('Failed getting member benefits', { variant: 'error' })
})
}
const [isMemberDialogOpen, setIsMemberDialogOpen] = useState(false);
const memberSelected = (selectedMember: any) => {
// Reset Selected Benefit
setMemberBenefits([]);
setValue('benefit_id', null);
setValue('benefit', null);
setMember(selectedMember);
setValue('member_id', selectedMember.id)
getMemberBenefits(selectedMember)
};
const [diagnosis, setDiagnosis] = useState([]);
const searchDiagnosis = (search) => {
axios.get('master/diagnosis/search', {params: {search}})
.then(function(res) {
setDiagnosis(res.data);
})
}
useEffect(() => { // Trigger First Search
axios.get('master/diagnosis/search')
.then(function(res) {
setDiagnosis(res.data);
})
}, [])
const [isEligible, setIsEligible] = useState<boolean|null>(null)
const [isCheckingLimit, setIsCheckingLimit] = useState(false)
const checkLimit = (event) => {
event.preventDefault();
console.log(getValues('diagnosis_id'))
if (!member || !getValues('diagnosis_id')) {
enqueueSnackbar('Please Check the Data', { variant: 'error' })
return false;
}
setIsCheckingLimit(true)
axios.post('check-limit', {
'member_id' : member.id,
'diagnosis' : getValues('diagnosis_id'),
'total_claim' : getValues('total_claim')
})
.then((res) => {
setIsEligible(true)
})
.catch((err) => {
enqueueSnackbar('Failed Checking Limit : ' + err.message ?? '', { variant: 'error' })
})
.then(() => {
setIsCheckingLimit(false)
})
}
const headStyle = {
fontWeight: 'bold'
};
return (
<Page title="Create Claim" sx={{ mx: 2 }}>
<HeaderBreadcrumbs
heading={'Create Claim'}
links={[
{ name: 'Dashboard', href: '/dashboard' },
{
name: 'Claims',
href: '/claims',
},
{
name: 'Create',
href: '/claims/create',
},
]}
/>
<Grid container spacing={2}>
<Grid item xs={12}>
<Card sx={{ p: 2 }}>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={3}>
<Typography variant="h6">Member</Typography>
<Stack spacing={2} direction="row">
<Grid item xs={12}>
<RHFTextField
name="member_id"
label="Member"
variant="outlined"
fullWidth
value={member?.name || ''}
ref={selectedMemberDisplay}
InputProps={{
readOnly: true,
}}
onClick={() => {setIsMemberDialogOpen(true)}}
/>
</Grid>
{/* <Grid item xs={2}>
<Button variant="outlined" fullWidth sx={{ p: 1.8 }} onClick={() => {
setIsMemberDialogOpen(true)
}}>
{member ? 'Change' : 'Search'}
</Button>
</Grid> */}
</Stack>
{ member && (
<Stack>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Table border="light-700">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left">Name</TableCell>
<TableCell align="left">{member.full_name}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">DOB</TableCell>
<TableCell align="left">{member.birth_date} ({member.age + ' years'})</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">Marital Status</TableCell>
<TableCell align="left">{member.marital_status}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">Record Type</TableCell>
<TableCell align="left">{member.record_type}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">Principal ID</TableCell>
<TableCell align="left">{member.principal_id} ({member.relation_with_principal})</TableCell>
</TableRow>
</TableBody>
</Table>
</Grid>
<Grid item xs={12} md={6}>
<Table border="light-700">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left">Plan</TableCell>
<TableCell align="left">{member.current_plan.code}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">Active</TableCell>
<TableCell align="left">{member.current_plan.start} - {member.current_plan.end} (Active)</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">Corporate Limit</TableCell>
<TableCell align="left">{fCurrency(0)} / {fCurrency(member.current_plan.limit_rules)}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">Plan Usage</TableCell>
<TableCell align="left">{fCurrency(0)} / {fCurrency(member.current_plan.limit_rules)}</TableCell>
</TableRow>
</TableBody>
</Table>
</Grid>
</Grid>
</Stack>
)}
<Controller
name="benefit"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
options={memberBenefits}
getOptionLabel={(option) =>
option ? `#${option.id} (${option.code}) ${option.description}` : ''
}
value={value || ''}
onChange={(event: any, newValue: any) => {
setValue('benefit_id', newValue?.id)
onChange(newValue);
}}
renderInput={(params) => (
<TextField
name="benefit"
{...params}
label="Benefit"
variant="outlined"
fullWidth
// onKeyPress={(event) => {
// if (event.key === 'Enter')
// searchDiagnosis(event.target.value)
// }}
/>
)}
/>
)}
/>
<Controller
name="diagnosis"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
options={diagnosis}
getOptionLabel={(option) =>
option ? `(${option.code}) ${option.name}` : ''
}
value={value || ''}
onChange={(event: any, newValue: any) => {
setValue('diagnosis_id', newValue?.id)
// setValue('diagnosis', newValue)
onChange(newValue);
}}
renderInput={(params) => (
<TextField
name="diagnosis"
{...params}
label="Diagnosis"
variant="outlined"
fullWidth
onKeyPress={(event) => {
if (event.key === 'Enter')
searchDiagnosis(event.target.value)
}}
/>
)}
/>
)}
/>
{ isCheckingLimit && (
<Stack sx={{ backgroundColor: 'gray', paddingY: 1, paddingX: 1.5, mb: 2, borderRadius: '3-xl' }}>
{/* Checking */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:info-circle"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Please Wait, Checking Eligibilty
</Typography>
</Typography>
</Stack>
)}
{ false && isCheckingLimit == false && isEligible == null && (
<Stack sx={{ backgroundColor: 'gray', paddingY: 1, paddingX: 1.5, mb: 2, borderRadius: '3-xl' }}>
{/* No Data Selected */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:info-circle"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Please Select Diagnosis !
</Typography>
</Typography>
</Stack>
)}
{ (!isCheckingLimit && isEligible !== null) && isEligible && (
<Stack sx={{ backgroundColor: '#B2E8E8', paddingY: 1, paddingX: 1.5, mb: 2, borderRadius: '3-xl' }}>
{/* Eligible */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:lock-alt"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Diagnosis is Eligible
</Typography>
</Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
125.000.000 / 125.000.000
</Typography>
</Stack>
)}
{ (!isCheckingLimit && isEligible !== null) && !isEligible && (
<Stack sx={{ backgroundColor: '#B2E8E8', paddingY: 1, paddingX: 1.5, mb: 2, borderRadius: '3-xl' }}>
{/* Not Eligible */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:lock-alt"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Not Eligible
</Typography>
</Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
125.000.000 / 125.000.000
</Typography>
</Stack>
)}
<RHFTextField type="number" name="total_claim" label="Total Claim" />
{ isEligible === true ? (
<LoadingButton onClick={handleSubmit(onSubmit)} variant="contained" color="success" style={{color: '#ffffff'}} size="large" fullWidth={true} loading={isCheckingLimit}>
Create Claim
</LoadingButton>
) : (
<LoadingButton onClick={checkLimit} variant="outlined" size="large" fullWidth={true} loading={isCheckingLimit}>
Check Limit
</LoadingButton>
)}
</Stack>
</FormProvider>
</Card>
</Grid>
</Grid>
<MemberSelectDialog
openDialog={isMemberDialogOpen}
setOpenDialog={setIsMemberDialogOpen}
onSelect={memberSelected}
></MemberSelectDialog>
</Page>
);
}

View File

@@ -0,0 +1,30 @@
import { Card, Stack } from "@mui/material";
import HeaderBreadcrumbs from "../../components/HeaderBreadcrumbs";
import Page from "../../components/Page";
import List from "./List";
export default function Claims() {
const pageTitle = 'Claim';
return (
<Page title={ pageTitle } sx={{ mx: 2}}>
<HeaderBreadcrumbs
heading={ pageTitle }
links={[
{ name: 'Dashboard', href: '/dashboard' },
{
name: 'Claim',
href: '/claims',
},
]}
/>
<Stack>
<List />
</Stack>
</Page>
);
}

View File

@@ -0,0 +1,266 @@
// @mui
import { Box, Button, Card, Collapse, IconButton, MenuItem, Table, TableBody, TableCell, TableRow, TextField, Typography, Stack, Menu, ButtonGroup, Link } from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
// hooks
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
// components
import axios from '../../utils/axios';
import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../@types/paginated-data';
import DataTable from '../../components/LaravelTable';
import { fCurrency } from '../../utils/formatNumber';
export default function List() {
const [searchParams, setSearchParams] = useSearchParams();
const [importResult, setImportResult] = useState(null);
function SearchInput(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
const [searchText, setSearchText] = useState("");
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? ''
setSearchText(newSearchText);
}
const handleSearchSubmit = (event: any) => {
event.preventDefault();
props.onSearch({search: searchText }); // Trigger to Parent
}
useEffect(() => { // Trigger First Search
setSearchText(searchParams.get('search') ?? '');
}, [])
return (
<form onSubmit={handleSearchSubmit} style={{ width: '100%' }}>
<TextField id="search-input" ref={searchInput} label="Search" variant="outlined" fullWidth onChange={handleSearchChange} value={searchText}/>
</form>
);
}
function ImportForm(props: any) {
// IMPORT
// Create Button Menu
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const createMenu = Boolean(anchorEl);
const importForm = useRef<HTMLInputElement>(null)
const [currentImportFileName, setCurrentImportFileName] = useState(null)
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleImportButton = () => {
if (importForm?.current) {
handleClose();
importForm.current ? importForm.current.click() : console.log('No File selected');
} else {
alert('No file selected')
}
}
const handleCancelImportButton = () => {
importForm.current.value = "";
importForm.current.dispatchEvent(new Event("change", { bubbles: true }));
}
const handleImportChange = (event: any) => {
if (event.target.files[0]) {
setCurrentImportFileName(event.target.files[0].name)
} else {
setCurrentImportFileName(null);
}
}
const handleUpload = () => {
if (importForm.current?.files.length) {
const formData = new FormData();
formData.append("file", importForm.current?.files[0])
axios.post(`corporates/${corporate_id}/import-plan-benefit`, formData )
.then(response => {
handleCancelImportButton();
loadDataTableData();
setImportResult(response.data)
// alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows');
})
.catch(response => {
enqueueSnackbar('Looks like something went wrong. Please check your data and try again. ' + response.message, { variant: 'error' })
})
} else {
enqueueSnackbar('No File Selected', { variant: 'warning' })
}
}
return (
<div>
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter}/>
<Link href="/claims/create" style={{ textDecoration: "none" }}>
<Button
variant='outlined'
startIcon={<AddIcon />} sx={{ p: 1.8 }}
>
Create
</Button>
</Link>
</Stack>
</div>
);
}
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = useState(true);
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>(LaravelPaginatedDataDefault);
const loadDataTableData = async (appliedFilter : any | null = null) => {
setDataTableLoading(true);
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const response = await axios.get('/claims', { params: filter });
// console.log(response.data);
setDataTableLoading(false);
setDataTableData(response.data);
}
const applyFilter = async (searchFilter: {search: string}) => {
await loadDataTableData(searchFilter);
setSearchParams(searchFilter);
}
const handlePageChange = (event : ChangeEvent, value: number) : void => {
const filter = Object.fromEntries([...searchParams.entries(), ["page", value]]);
loadDataTableData(filter);
setSearchParams(filter);
}
useEffect(() => {
loadDataTableData();
}, [])
const headStyle = {
fontWeight: 'bold',
};
// Called on every row to map the data to the columns
function createData( data: any ): any {
return {
...data,
}
}
{/* ------------------ TABLE ROW ------------------ */}
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.member?.full_name}</TableCell>
<TableCell align="left">{row.plan?.code}</TableCell>
<TableCell align="left">{row.benefit?.code}</TableCell>
<TableCell align="left">({row.diagnosis?.code}) {row.diagnosis?.name}</TableCell>
<TableCell align="left">{fCurrency(row.total_claim)}</TableCell>
{/* <TableCell align="right"><Button variant="outlined" color="error" size="small">Disable</Button></TableCell> */}
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={99}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ borderBottom: 1 }}>
<Typography variant="body2" gutterBottom component="div">
Description : {row.description}
</Typography>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
{/* ------------------ END TABLE ROW ------------------ */}
function TableContent() {
return (
<Table aria-label="collapsible table">
{/* ------------------ TABLE HEADER ------------------ */}
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left" />
<TableCell style={headStyle} align="left">Code</TableCell>
<TableCell style={headStyle} align="left">Member Name</TableCell>
<TableCell style={headStyle} align="left">Plan</TableCell>
<TableCell style={headStyle} align="left">Benefit</TableCell>
<TableCell style={headStyle} align="left">Diagnosis</TableCell>
<TableCell style={headStyle} align="left">Total Claim</TableCell>
{/* <TableCell style={headStyle} align="right">Action</TableCell> */}
</TableRow>
</TableBody>
{/* ------------------ END TABLE HEADER ------------------ */}
{/* ------------------ TABLE ROW ------------------ */}
{dataTableIsLoading ?
(
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">Loading</TableCell>
</TableRow>
</TableBody>
) : (
dataTableData.data.length === 0 ?
(
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">No Data</TableCell>
</TableRow>
</TableBody>
) : (
<TableBody>
{dataTableData.data.map(row => (
<Row key={row.id} row={row} />
))}
</TableBody>
)
)}
{/* ------------------ END TABLE ROW ------------------ */}
</Table>
)
}
return (
<Card>
<ImportForm />
<DataTable
isLoading={dataTableIsLoading}
lastRequest={0}
data={dataTableData}
handlePageChange={handlePageChange}
TableContent={<TableContent />}
/>
</Card>
);
}

View File

@@ -398,6 +398,16 @@ export default function Corporates() {
? fCurrency(row.current_policy?.minimal_deposit_net)
: '-'}
</Grid>
<Grid item xs={6}>
Corporate Limit
</Grid>
<Grid item xs={6}>
:{' '}
{row.current_policy
? fCurrency(row.current_policy?.limit_balance)
: '-'}
</Grid>
</Grid>
</Grid>
</Grid>

View File

@@ -268,7 +268,7 @@ export default function CorporatePlanList() {
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">{row.nric}</TableCell>
<TableCell align="left">{row.email}</TableCell>
<TableCell align="left">{row.plan_id}</TableCell>
<TableCell align="left">{row.current_plan?.code}</TableCell>
<TableCell align="left">{row.current_policy?.start}</TableCell>
<TableCell align="left">{row.current_policy?.end}</TableCell>
{( row.active ? (<TableCell align="right"><Button variant="outlined" color="success" size="small">Active</Button></TableCell>)

View File

@@ -205,6 +205,20 @@ export default function Router() {
path: 'master/formularium/create',
element: <MasterFormulariumCreate />,
},
{
path: 'claims',
element: <Claims />,
},
{
path: 'claims/create',
element: <ClaimsCreate />
},
{
path: 'claims/:id',
element: <ClaimsCreate />
}
]
},
// {
@@ -287,3 +301,6 @@ const CorporateServicesCreate = Loadable(lazy(() => import('../pages/Corporates/
const CorporateHospitals = Loadable(lazy(() => import('../pages/Corporates/Hospital/Index')));
const CorporateClaimHistories = Loadable(lazy(() => import('../pages/Corporates/ClaimHistory/Index')));
const Claims = Loadable(lazy(() => import('../pages/Claims/Index')));
const ClaimsCreate = Loadable(lazy(() => import('../pages/Claims/Create')));