From d69b610c5fcd65ed1bf6e9801329cd58950fa269 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Mon, 9 Oct 2023 13:18:38 +0700 Subject: [PATCH] pengajuan claim --- .../client-portal/src/@types/claim-submit.ts | 8 + .../src/components/CardClaimSubmit.tsx | 274 ++++++++++++++ .../pages/ClaimSubmit/DialogDetailClaim.tsx | 133 +++++-- .../src/pages/ClaimSubmit/Index.tsx | 83 +++- .../src/pages/ClaimSubmit/List.tsx | 155 +------- .../src/pages/Corporate/Form.tsx | 14 +- frontend/client-portal/src/routes/index.tsx | 22 ++ .../claim-submit/CardNotification.tsx | 148 ++++++++ .../src/sections/claim-submit/CardPolicy.tsx | 210 +++++++++++ .../claim-submit/DialogClaimSubmitMember.tsx | 266 +++++++++++++ .../claim-submit/DialogDetailClaim.tsx | 175 +++++++++ .../claim-submit/DialogNotification.tsx | 93 +++++ .../claim-submit/DialogRequestLog.tsx | 355 ++++++++++++++++++ .../claim-submit/DialogTopUpLimit.tsx | 265 +++++++++++++ 14 files changed, 2017 insertions(+), 184 deletions(-) create mode 100644 frontend/client-portal/src/@types/claim-submit.ts create mode 100644 frontend/client-portal/src/components/CardClaimSubmit.tsx create mode 100644 frontend/client-portal/src/sections/claim-submit/CardNotification.tsx create mode 100644 frontend/client-portal/src/sections/claim-submit/CardPolicy.tsx create mode 100644 frontend/client-portal/src/sections/claim-submit/DialogClaimSubmitMember.tsx create mode 100644 frontend/client-portal/src/sections/claim-submit/DialogDetailClaim.tsx create mode 100644 frontend/client-portal/src/sections/claim-submit/DialogNotification.tsx create mode 100644 frontend/client-portal/src/sections/claim-submit/DialogRequestLog.tsx create mode 100644 frontend/client-portal/src/sections/claim-submit/DialogTopUpLimit.tsx diff --git a/frontend/client-portal/src/@types/claim-submit.ts b/frontend/client-portal/src/@types/claim-submit.ts new file mode 100644 index 00000000..ab368d7c --- /dev/null +++ b/frontend/client-portal/src/@types/claim-submit.ts @@ -0,0 +1,8 @@ +export type CardSubmit = { + rows?: Array; + id: number, + name: string, + member_id: string, + usesage_limit: number, + limit: number +} \ No newline at end of file diff --git a/frontend/client-portal/src/components/CardClaimSubmit.tsx b/frontend/client-portal/src/components/CardClaimSubmit.tsx new file mode 100644 index 00000000..a4f20ed1 --- /dev/null +++ b/frontend/client-portal/src/components/CardClaimSubmit.tsx @@ -0,0 +1,274 @@ +/* ---------------------------------- @mui ---------------------------------- */ +import { styled } from '@mui/material/styles'; +import { + Paper, + Table as TableContent, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Button, + TableSortLabel, + Box, + Card, + Grid, + FormControl, + InputLabel, + Select, + MenuItem, + SelectChangeEvent, + Stack, + Typography, + LinearProgress, + linearProgressClasses, + InputAdornment, +} from '@mui/material'; +import { visuallyHidden } from '@mui/utils'; +/* ---------------------------------- axios --------------------------------- */ +import axios from '../utils/axios'; +/* ---------------------------------- react --------------------------------- */ +import { Fragment, useContext, useEffect, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; +/* -------------------------------- component ------------------------------- */ +import BaseTablePagination from './BaseTablePagination'; +/* ---------------------------------- theme --------------------------------- */ +import palette from '../theme/palette'; +/* ---------------------------------- utils --------------------------------- */ +import { UserCurrentCorporateContext } from '../contexts/UserCurrentCorporate'; +import { fSplit } from '../utils/formatNumber'; +/* ---------------------------------- types --------------------------------- */ +import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '../@types/table'; +/* ----------------------------------- icon --------------------------------- */ +import SearchIcon from '@mui/icons-material/Search'; +import { FormControlLabel } from '@mui/material'; +import { Checkbox } from '@mui/material'; +import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded'; +import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded'; +import { LoadingButton } from '@mui/lab'; + +/* --------------------------------- styled --------------------------------- */ +const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ + height: 10, + borderRadius: 6, + [`&.${linearProgressClasses.colorPrimary}`]: { + backgroundColor: '#D1F1F1', + }, + [`& .${linearProgressClasses.bar}`]: { + borderRadius: 6, + backgroundColor: '#54D62C', + }, +})); +/* -------------------------------------------------------------------------- */ + +export default function Table({ + rows, + loadings, + params, + searchs, +}: TableListProps) { + + + /* ------------------------------ handle checkbox ----------------------------*/ + const handleCheckboxChange = async (event: React.ChangeEvent) => { + // Anda bisa menambahkan logika di sini + if (event.target.checked) { + // Checkbox dicentang + console.log('Checkbox dicentang'); + // Tambahkan kode lain yang ingin Anda jalankan saat checkbox dicentang + } else { + // Checkbox tidak dicentang + console.log('Checkbox tidak dicentang'); + // Tambahkan kode lain yang ingin Anda jalankan saat checkbox tidak dicentang + } + }; + + + return ( + // + + {/* Field 1 */} + + {searchs && searchs.useSearchs ? ( + + +
+ searchs.setSearchText(event.target.value)} + value={searchs.searchText} + fullWidth + placeholder='Search Name or Member ID... ' + InputProps={{ + startAdornment: ( + + + + ), + }} + + /> + +
+
+ ) : null } +
+ {/* End Field 1 */} + + {/* Field 2 */} + + + + + + +
+ } + label="" + labelPlacement="end" + sx={{marginLeft: '20px'}} + /> + +
+
+ user-profile +
+ + Alexandra Rhea Putranto + KM002-01 + + + + + + + LIMIT + + + + + + {fSplit(8000000)} + + + / {fSplit(10000000000)} + + + + + + + + + + + + + {/* + + */} + +
+
+
+
+ + + + + +
+ } + label="" + labelPlacement="end" + sx={{marginLeft: '20px'}} + /> + +
+
+ user-profile +
+ + Alexandra Rhea Putranto + KM002-01 + + + + + + + LIMIT + + + + + + {fSplit(8000000)} + + + / {fSplit(10000000000)} + + + + + + + + + + + + + {/* + + */} + +
+
+
+
+ {/* End Field 2 */} + + Claim Submit Selected + +
+ //
+ ); +} diff --git a/frontend/client-portal/src/pages/ClaimSubmit/DialogDetailClaim.tsx b/frontend/client-portal/src/pages/ClaimSubmit/DialogDetailClaim.tsx index 01df6b98..ace7af39 100644 --- a/frontend/client-portal/src/pages/ClaimSubmit/DialogDetailClaim.tsx +++ b/frontend/client-portal/src/pages/ClaimSubmit/DialogDetailClaim.tsx @@ -1,7 +1,10 @@ // @mui +import { styled } from '@mui/material/styles'; import { Button, Box, + LinearProgress, + linearProgressClasses, Stepper, Step, StepLabel, @@ -9,19 +12,36 @@ import { Typography, Divider, Stack, + Grid, + Avatar, } from '@mui/material'; import { Add } from '@mui/icons-material'; // components import MuiDialog from '../../components/MuiDialog'; +/*------------------------------------ icon ----------------------------------- */ +import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; // theme import palette from '../../theme/palette'; // React -import { ReactElement } from 'react'; +import { ReactElement, useRef, useState } from 'react'; +import { useSearchParams, useNavigate, Link } from 'react-router-dom'; +import { fPostFormat } from '../../utils/formatTime'; +import { fCurrency } from '../../utils/formatNumber'; -type DataContent = { - info: string; - date: string; - time: string; +// -------------------------------- type -------------------------------------- +type DataContentType = { + id: number; + fullName: string; + memberId: string; + limit: { + current: number; + total: number; + percentage: number; + }; + avatar?: { + url?: string; + title?: string; + }; }; type MuiDialogProps = { @@ -32,15 +52,33 @@ type MuiDialogProps = { openDialog: boolean; setOpenDialog: Function; content?: ReactElement; - data?: DataContent[]; + data: DataContentType; }; +/* --------------------------------- styles --------------------------------- */ +const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ + height: 10, + borderRadius: 6, + [`&.${linearProgressClasses.colorPrimary}`]: { + backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800], + }, + [`& .${linearProgressClasses.bar}`]: { + borderRadius: 6, + background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)', + }, +})); +/* -------------------------------------------------------------------------- */ + + const steps = ['Review', 'Approval', 'Disbursement']; const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => { function clickHandler(arg0: string) { throw new Error('Function not implemented.'); } + + const navigate = useNavigate(); + const [serviceCode, setServiceCode] = useState('IP'); // const getContent = () => ( @@ -48,29 +86,66 @@ const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialog return ( <> - - - Claim Request - - - Submission date - 15 / 05 / 2022 - - - - - {steps.map((label) => ( - - {label} - - ))} - - + + {/* Field 1 */} + + + navigate(`/corporate`)} sx={{ cursor: "pointer" }} /> + Claim Submission + Submission Date + + {fPostFormat(new Date(), 'dd MMM yyyy')} + + + + + + + + + + + + + + + {'Alexandra Rhea Putranto'} + {'KM002-01'} + + + + Total Limit + + + + {fCurrency(8000000)} / {fCurrency(100000)} + + + + + + + 17 Mei 2022 diff --git a/frontend/client-portal/src/pages/ClaimSubmit/Index.tsx b/frontend/client-portal/src/pages/ClaimSubmit/Index.tsx index b54211ec..a6226041 100644 --- a/frontend/client-portal/src/pages/ClaimSubmit/Index.tsx +++ b/frontend/client-portal/src/pages/ClaimSubmit/Index.tsx @@ -1,7 +1,7 @@ /* ---------------------------------- react --------------------------------- */ import { useContext, useEffect, useState } from 'react'; /* ----------------------------------- mui ---------------------------------- */ -import { Container, Grid } from '@mui/material'; +import { Container, Grid, Typography } from '@mui/material'; /* ------------------------------- components ------------------------------- */ import Page from '../../components/Page'; import TableList from '../../components/Table'; @@ -23,6 +23,9 @@ import Documents from '../Claims/components/Documents'; // theme import palette from '../../theme/palette'; import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs'; +import { Stack } from '@mui/system'; +import { fDateSuffix } from '../../utils/formatTime'; +import DialogClaimSubmitMember from '../../sections/claim-submit/DialogClaimSubmitMember'; interface ClaimStatusType { name: string; @@ -30,6 +33,50 @@ interface ClaimStatusType { color: string; } +/* ------------------------------ default data ------------------------------ */ +type DataMember = { + id: number; + fullName: string; + memberId: string; + limit: { + current: number; + total: number; + percentage: number; + }; + avatar?: { + url?: string; + title?: string; + }; +}; + +type CardPolicyProps = { + limit: { + myLimit: { + balance: number; + total: number; + percentage: number; + }; + lockLimit: { + balance: number; + percentage: number; + }; + }; + topUpLimit: { + companyName: string; + policyNumber: number; + totalMembers: number; + totalCases: number; + totalPersen: number; + myLimit: { + balance: number; + total: number; + percentage: number; + }; + maxTopUp: number; + }; + members?: DataMember[]; +}; + export default function Drugs() { const { themeStretch } = useSettings(); const { corporateValue } = useContext(UserCurrentCorporateContext); @@ -48,9 +95,12 @@ export default function Drugs() { setIsLoading: setIsLoading, }; + + /* ------------------------------ handle params ----------------------------- */ const [searchParams, setSearchParams] = useSearchParams(); const [appliedParams, setAppliedParams] = useState({}); + const [policyData, setPolicyData] = useState(); const params = { searchParams: searchParams, @@ -70,6 +120,11 @@ export default function Drugs() { orderBy: orderBy, setOrderBy: setOrderBy, }; + + + /* ---------------------------- Get Current Date ---------------------------- */ + const current = new Date(); + const date = fDateSuffix(current); /* -------------------------------------------------------------------------- */ /* ---------------------------- handle pagination --------------------------- */ @@ -177,7 +232,7 @@ export default function Drugs() { setSearchParams(parameters); - setListAllMemberByClaimStatus(claim.data.data.allMembersByClaimStatus.data); + // setListAllMemberByClaimStatus(claim.data.data.allMembersByClaimStatus.data); setPaginationTable(claim.data.data.allMembersByClaimStatus); setIsLoading(false); @@ -198,17 +253,21 @@ export default function Drugs() { /> + + Select Employee + + + + Submission Date + {date} + + - - - {/* */} + diff --git a/frontend/client-portal/src/pages/ClaimSubmit/List.tsx b/frontend/client-portal/src/pages/ClaimSubmit/List.tsx index 9b2e2c31..5685cea5 100644 --- a/frontend/client-portal/src/pages/ClaimSubmit/List.tsx +++ b/frontend/client-portal/src/pages/ClaimSubmit/List.tsx @@ -8,7 +8,7 @@ import { useContext, useEffect, useState } from 'react'; /* -------------------------------- component ------------------------------- */ import Iconify from '../../components/Iconify'; -import TableComponent from '../../components/Table'; +import CardClaimSubmit from '../../components/CardClaimSubmit'; /* ---------------------------------- theme --------------------------------- */ import palette from '../../theme/palette'; import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; @@ -106,147 +106,30 @@ export default function List() { handleSearchSubmit: handleSearchSubmit, }; + + /* ------------------------------------------------------------------------- */ + /*-------------------------------- handlle checkbox ------------------------ */ + const handleCheckboxChange = async (event: React.FormEvent) => { + // Anda bisa menambahkan logika di sini + if (event.target.checked) { + // Checkbox dicentang + console.log('Checkbox dicentang'); + // Tambahkan kode lain yang ingin Anda jalankan saat checkbox dicentang + } else { + // Checkbox tidak dicentang + console.log('Checkbox tidak dicentang'); + // Tambahkan kode lain yang ingin Anda jalankan saat checkbox tidak dicentang + } + }; + + /* -------------------------------- headCell -------------------------------- */ - const headCells: HeadCell[] = [ - { - id: 'memberId', - align: 'left', - label: 'Member ID', - isSort: true, - }, - { - id: 'codeRequest', - align: 'left', - label: 'Code Request', - isSort: true, - }, - { - id: 'submissionDate', - align: 'left', - label: 'Request Date', - isSort: true, - }, - { - id: 'fullName', - align: 'left', - label: 'Name', - isSort: true, - }, - { - id: 'division', - align: 'left', - label: 'Divisi', - isSort: false, - }, - { - id: 'status', - align: 'center', - label: 'Status', - isSort: false, - }, - { - id: 'action', - align: 'right', - label: '', - isSort: false, - }, - ]; - - useEffect(() => { - (async () => { - setIsLoading(true); - - await new Promise((resolve) => setTimeout(resolve, 250)); - - const parameters = - Object.keys(appliedParams).length !== 0 - ? appliedParams - : Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]); - - const response = await axios.get(`${corporateValue}/members`, { - params: { ...parameters, type: 'claim-report' }, - }); - - setData( - response.data.data.map((obj: any) => ({ - ...obj, - status: - obj.status === 'requested' ? ( - - ) : obj.status === 'approved' ? ( - - ) : ( - - ), - submissionDate: - obj.submissionDate ? fDate(obj.submissionDate) : '' - })) - ); - - setPaginationTable(response.data); - setRowsPerPage(response.data.per_page); - - if (searchParams.get('page')) { - //@ts-ignore - const currentPage = parseInt(searchParams.get('page')) - 1; - - paginationTable.current_page = currentPage; - setPage(currentPage); - } - - setIsLoading(false); - })(); - }, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]); return ( - {/* Corporate Detail */} - + @@ -431,7 +431,7 @@ export default function CorporateForm({currentCorporate }: Props) { - {/* + @@ -439,7 +439,7 @@ export default function CorporateForm({currentCorporate }: Props) { Company Logo - + {/* @@ -452,10 +452,10 @@ export default function CorporateForm({currentCorporate }: Props) { Company Automatic Linking - + */} - + {/* Linking Rules @@ -464,9 +464,9 @@ export default function CorporateForm({currentCorporate }: Props) { - + */} - */} + diff --git a/frontend/client-portal/src/routes/index.tsx b/frontend/client-portal/src/routes/index.tsx index 1a10761c..58cca7df 100644 --- a/frontend/client-portal/src/routes/index.tsx +++ b/frontend/client-portal/src/routes/index.tsx @@ -113,6 +113,26 @@ export default function Router() { }, ], }, + { + path: '/claim-request/:id', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + { + path: 'dialog-detail', + element: + }, + ], + }, { path: '/claim-report', element: ( @@ -212,6 +232,8 @@ const DialogDetailClaim = Loadable(lazy(()=> import('../pages/ClaimReport/Dialog // Claim submit const ClaimSubmit = Loadable(lazy(() => import('../pages/ClaimSubmit/Index'))); +// Claim Request +const ClaimRequest = Loadable(lazy(() => import('../pages/ClaimSubmit/DialogDetailClaim'))); // Corporate const Corporate = Loadable(lazy(() => import('../pages/Corporate/Index'))); diff --git a/frontend/client-portal/src/sections/claim-submit/CardNotification.tsx b/frontend/client-portal/src/sections/claim-submit/CardNotification.tsx new file mode 100644 index 00000000..7727e61d --- /dev/null +++ b/frontend/client-portal/src/sections/claim-submit/CardNotification.tsx @@ -0,0 +1,148 @@ +// @mui +import { styled } from '@mui/material/styles'; +import { Button, Card, Typography, Link, Divider, Stack } from '@mui/material'; +import { ChevronRight } from '@mui/icons-material'; +// components +import Iconify from '../../components/Iconify'; +// Section +import DialogNotification from './DialogNotification'; +import DialogDetailClaim from './DialogDetailClaim'; +// React +import { useState } from 'react'; + +// ---------------------------------------------------------------------- + +type DataContent = { + info: string; + date: string; + time: string; +}; + +type NotificationProps = { + data?: DataContent[]; +}; + +// ---------------------------------------------------------------------- + +const RootNotificationStyle = styled(Card)(({ theme }) => ({ + boxShadow: 'none', + padding: '1.5rem', + color: 'black', + backgroundColor: theme.palette.grey[200], + height: '100%', + maxHeight: '240px', +})); + +const ItemNotificationStyle = styled(Card)(({ theme }) => ({ + boxShadow: 'none', + padding: theme.spacing(1), + borderRadius: 0.5, + color: 'black', + marginTop: 2, + overflowY: 'auto', + maxHeight: '154px', + gap: '0.5rem', +})); + +// ---------------------------------------------------------------------- + +export default function CardNotification({ data }: NotificationProps) { + const [openDialog, setOpenDialog] = useState(false); + const [dialogTitle, setDialogTitle] = useState(''); + const [isDialog, setIsDialog] = useState(''); + + const clickHandler = (isDialog: string) => { + switch (isDialog) { + case 'allNotification': + setDialogTitle('Notification'); + setIsDialog(isDialog); + setOpenDialog(true); + break; + case 'infoDetail': + setDialogTitle('Claim Details'); + setIsDialog(isDialog); + setOpenDialog(true); + break; + + default: + break; + } + }; + + return ( + + + + + + Notification + + + + + + + + {data + ? data.map(({ info, date, time }, index) => ( +
+ {index >= 1 ? : ''} + + + {info} + clickHandler('infoDetail')} + > + Info Detail + + + + {date} + {time} + + +
+ )) + : ''} +
+ + {isDialog === 'allNotification' && ( + + )} + + {isDialog === 'infoDetail' && ( + + )} +
+ ); +} diff --git a/frontend/client-portal/src/sections/claim-submit/CardPolicy.tsx b/frontend/client-portal/src/sections/claim-submit/CardPolicy.tsx new file mode 100644 index 00000000..74a7ea1b --- /dev/null +++ b/frontend/client-portal/src/sections/claim-submit/CardPolicy.tsx @@ -0,0 +1,210 @@ +// @mui +import { styled } from '@mui/material/styles'; +import { + Button, + Card, + Typography, + LinearProgress, + linearProgressClasses, + Stack, +} from '@mui/material'; +// components +import Iconify from '../../components/Iconify'; +// React +import { useState } from 'react'; +// utils +import { fCurrency, fSplit } from '../../utils/formatNumber'; +/* -------------------------------- sections -------------------------------- */ +import DialogTopUpLimit from './DialogTopUpLimit'; +import DialogClaimSubmitMember from './DialogClaimSubmitMember'; + +/* ---------------------------------- types --------------------------------- */ + +type DataMember = { + id: number; + fullName: string; + memberId: string; + limit: { + current: number; + total: number; + percentage: number; + }; + avatar?: { + url?: string; + title?: string; + }; +}; + +type CardPolicyProps = { + data: { + limit: { + myLimit: { + balance: number; + total: number; + percentage: number; + }; + lockLimit: { + balance: number; + percentage: number; + }; + }; + topUpLimit: { + companyName: string; + policyNumber: number; + totalMembers: number; + totalCases: number; + totalPersen: number; + myLimit: { + balance: number; + total: number; + percentage: number; + }; + maxTopUp: number; + }; + members: { + memberId: string; + memberFullName: string; + }; + }; +}; + +/* -------------------------------------------------------------------------- */ + +/* --------------------------------- styled --------------------------------- */ + +const RootBalanceStyle = styled(Card)(({ theme }) => ({ + boxShadow: 'none', + padding: theme.spacing(3), + color: 'black', + backgroundColor: theme.palette.grey[200], + maxHeight: '240px', +})); + +const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ + height: 10, + borderRadius: 6, + [`&.${linearProgressClasses.colorPrimary}`]: { + backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800], + }, + [`& .${linearProgressClasses.bar}`]: { + borderRadius: 6, + backgroundColor: theme.palette.primary.main, + }, +})); + +/* -------------------------------------------------------------------------- */ + +export default function CardPolicy(props: CardPolicyProps) { + const [openDialog, setOpenDialog] = useState(false); + const [dialogTitle, setDialogTitle] = useState(''); + const [isDialog, setIsDialog] = useState(''); + + const { limit, topUpLimit, members } = props.data || {}; + if (!limit || !topUpLimit) { + return null; + } + + const clickHandler = (isDialog: string) => { + switch (isDialog) { + case 'submitClaim': + setDialogTitle('Add Claim'); + setIsDialog(isDialog); + setOpenDialog(true); + break; + case 'topUpLimit': + setDialogTitle('Top Up Limit'); + setIsDialog(isDialog); + setOpenDialog(true); + break; + + default: + break; + } + }; + + return ( + + <> + +
+ + Total Limit + + + {fCurrency(limit.myLimit ? limit.myLimit.balance : 0)} + + + / {fSplit(limit.myLimit ? limit.myLimit.total : 0)} + +
+ + + + {limit.myLimit ? limit.myLimit.percentage : 0}% + + +
+ + + + + + + + Lock Fund ( {limit.lockLimit ? limit.lockLimit.percentage : 0}% ) + + + + {fSplit(limit.lockLimit ? limit.lockLimit.balance : 0)} /{' '} + {fSplit(limit.myLimit ? limit.myLimit.total : 0)} + + + + + + + + + + {isDialog === 'submitClaim' && ( + + )} + + {isDialog === 'topUpLimit' && ( + + )} +
+ ); +} diff --git a/frontend/client-portal/src/sections/claim-submit/DialogClaimSubmitMember.tsx b/frontend/client-portal/src/sections/claim-submit/DialogClaimSubmitMember.tsx new file mode 100644 index 00000000..668310ec --- /dev/null +++ b/frontend/client-portal/src/sections/claim-submit/DialogClaimSubmitMember.tsx @@ -0,0 +1,266 @@ +// @mui +import { styled } from '@mui/material/styles'; +import { + Typography, + LinearProgress, + linearProgressClasses, + Stack, + TextField, + InputAdornment, + Card, + Grid, + IconButton, + FormControlLabel, + Checkbox, +} from '@mui/material'; +import { Search as SearchIcon } from '@mui/icons-material'; +// components +import MuiDialog from '../../components/MuiDialog'; +import Iconify from '../../components/Iconify'; +import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded'; +// React +import { ReactElement, useContext, useEffect, useState } from 'react'; +import DialogRequestLog from './DialogRequestLog'; +import axios from '../../utils/axios'; +import { useSearchParams, useNavigate, Link } from 'react-router-dom'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; +import { fSplit } from '../../utils/formatNumber'; +import { LoadingButton } from '@mui/lab'; + +// ---------------------------------------------------------------------- + +type DataContentType = { + id: number; + fullName: string; + memberId: string; + limit: { + current: number; + total: number; + percentage: number; + }; + avatar?: { + url?: string; + title?: string; + }; +}; + +type MuiDialogProps = { + title?: { + name?: string; + icon?: string; + }; + openDialog: boolean; + setOpenDialog: Function; + content?: ReactElement; + // data?: DataContent[]; +}; + +// ---------------------------------------------------------------------- + +const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ + height: 10, + borderRadius: 6, + [`&.${linearProgressClasses.colorPrimary}`]: { + backgroundColor: '#D1F1F1', + }, + [`& .${linearProgressClasses.bar}`]: { + borderRadius: 6, + backgroundColor: '#54D62C', + }, +})); + +// ---------------------------------------------------------------------- + +export default function DialogClaimSubmitMember({ + title, + openDialog, + setOpenDialog, +}: MuiDialogProps) { + const { corporateValue } = useContext(UserCurrentCorporateContext); + + /* ---------------------------------- data ---------------------------------- */ + const [data, setData] = useState([]); + const [dataMemberClaim, setDataMemberClaim] = useState({ + id: 0, + fullName: '', + memberId: '', + limit: { + current: 0, + total: 0, + percentage: 0, + }, + }); + const navigate = useNavigate(); + /* -------------------------------------------------------------------------- */ + + /* --------------------------------- Search --------------------------------- */ + const [searchText, setSearchText] = useState(''); + const [appliedParams, setAppliedParams] = useState({}); + + const handleSearchSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + if (searchText === '') { + setAppliedParams({}); + } else { + setAppliedParams({ search: searchText }); + } + await new Promise((resolve) => setTimeout(resolve, 500)); + }; + /* -------------------------------------------------------------------------- */ + + + /* ------------------------------ Icon On Click ----------------------------- */ + const clickHandler = ({ id, fullName, memberId, limit, avatar }: DataContentType) => { + setDataMemberClaim({ + id: id, + fullName: fullName, + memberId: memberId, + limit: { + current: limit.current, + total: limit.total, + percentage: limit.percentage, + }, + avatar: { + url: avatar && avatar.url, + title: avatar && avatar.title, + }, + }); + }; + /* -------------------------------------------------------------------------- */ + + useEffect(() => { + (async () => { + if (openDialog === true) { + const response = await axios.get(`${corporateValue}/members`, { + params: { ...appliedParams, type: 'claim-submit' }, + }); + + setData(response.data.data); + } + })(); + }, [corporateValue, openDialog, appliedParams]); + + const getContent = () => ( + + + {data.map((row: DataContentType, key) => ( + + clickHandler({ + id: row.id, + fullName: row.fullName, + memberId: row.memberId, + limit: { + current: row.limit.current, + total: row.limit.total, + percentage: row.limit.percentage, + }, + }) + } + > + + +
+ } + label="" + labelPlacement="end" + sx={{marginLeft: '20px'}} + /> + +
+
+ user-profile +
+ + {row.fullName} + + {row.memberId} + + + + + + + + LIMIT + + + + + + {fSplit(row.limit && row.limit.current)} + + + / {fSplit(row.limit && row.limit.total)} + + + + + + + + + + navigate(`/claim-request/${row.id}`)} > + + + +
+
+ ))} +
+
+ + ); + + return ( + + +
+ setSearchText(event?.target.value)} + value={searchText} + InputProps={{ + startAdornment: ( + + + + ), + }} + placeholder="Search Name or Member ID... " + sx={{ marginTop: 2 }} + /> + + {getContent()} +
+ + + Claim Submit Selected + + +
+ ) +} diff --git a/frontend/client-portal/src/sections/claim-submit/DialogDetailClaim.tsx b/frontend/client-portal/src/sections/claim-submit/DialogDetailClaim.tsx new file mode 100644 index 00000000..3aba83d1 --- /dev/null +++ b/frontend/client-portal/src/sections/claim-submit/DialogDetailClaim.tsx @@ -0,0 +1,175 @@ +// @mui +import { + Button, + Box, + Stepper, + Step, + StepLabel, + Card, + Typography, + Divider, + Stack, +} from '@mui/material'; +import { Add } from '@mui/icons-material'; +// components +import MuiDialog from '../../components/MuiDialog'; +// theme +import palette from '../../theme/palette'; +// React +import { ReactElement } from 'react'; + +type DataContent = { + info: string; + date: string; + time: string; +}; + +type MuiDialogProps = { + title?: { + name?: string; + icon?: string; + }; + openDialog: boolean; + setOpenDialog: Function; + content?: ReactElement; + data?: DataContent[]; +}; + +const steps = ['Review', 'Approval', 'Disbursement']; + +const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => { + const getContent = () => ( + <> + + + Claim Request + + + Submission date + 15 / 05 / 2022 + + + + + {steps.map((label) => ( + + {label} + + ))} + + + + + 17 Mei 2022 + + + + + + {/* Item 1 */} + + + 09:10 WIB + + Approval + + + + + + Details : mohon melengkapi kekurangan dokumen + + + Lab pemeriksaan darah + + + + + {/* Item 2 */} + + + 09:00 WIB + + Approval + + + + + + Details : Penilaian Dokter + + + + {/* Item 3 */} + + + 08:00 WIB + + Review + + + + + + Details : Klaim Diajukan + + + + + + + ); + + return ( + + ); +}; + +export default DialogDetailClaim; diff --git a/frontend/client-portal/src/sections/claim-submit/DialogNotification.tsx b/frontend/client-portal/src/sections/claim-submit/DialogNotification.tsx new file mode 100644 index 00000000..c93a27b2 --- /dev/null +++ b/frontend/client-portal/src/sections/claim-submit/DialogNotification.tsx @@ -0,0 +1,93 @@ +// react +import { ReactElement, useState } from 'react'; +// mui +import { Card, Divider, Link, Stack, Typography } from '@mui/material'; +import { styled } from '@mui/material/styles'; +// Component +import MuiDialog from '../../components/MuiDialog'; +// Sections +import DialogDetailClaim from './DialogDetailClaim'; + +type DataContent = { + info: string; + date: string; + time: string; +}; + +type MuiDialogProps = { + title?: { + name?: string; + icon?: string; + }; + openDialog: boolean; + setOpenDialog: Function; + content?: ReactElement; + data?: DataContent[]; +}; + +const ItemNotificationStyle = styled(Card)(({ theme }) => ({ + boxShadow: 'none', + padding: theme.spacing(1), + borderRadius: 0.5, + color: 'black', +})); + +const DialogNotification = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => { + const [openDialogClaim, setOpenDialogClaim] = useState(false); + const [dialogTitleClaim, setDialogTitleClaim] = useState(''); + + const clickHandler = () => { + setDialogTitleClaim('Claim Details'); + setOpenDialogClaim(true); + }; + + const getContent = () => ( + + + {data + ? data.map(({ info, date, time }: DataContent, key) => ( +
+ {key >= 1 ? : ''} + + + {info} + + Info Detail + + + + {date} + {time} + + +
+ )) + : ''} +
+
+ ); + + return ( + <> + + + + + ); +}; + +export default DialogNotification; diff --git a/frontend/client-portal/src/sections/claim-submit/DialogRequestLog.tsx b/frontend/client-portal/src/sections/claim-submit/DialogRequestLog.tsx new file mode 100644 index 00000000..adfac5cb --- /dev/null +++ b/frontend/client-portal/src/sections/claim-submit/DialogRequestLog.tsx @@ -0,0 +1,355 @@ +// @mui +import { styled } from '@mui/material/styles'; +import { + Typography, + LinearProgress, + linearProgressClasses, + Stack, + Card, + Button, + Divider, + Avatar, +} from '@mui/material'; +// components +import MuiDialog from '../../components/MuiDialog'; +import Iconify from '../../components/Iconify'; +// React +import { ReactElement, useRef, useState } from 'react'; +// form +import { LoadingButton } from '@mui/lab'; +import axios from '../../utils/axios'; +import { enqueueSnackbar } from 'notistack'; +import { fPostFormat } from '../../utils/formatTime'; +import { fCurrency } from '../../utils/formatNumber'; +import { makeFormData } from '../../utils/jsonToFormData'; + +/* ---------------------------------- types --------------------------------- */ +type DataContentType = { + id: number; + fullName: string; + memberId: string; + limit: { + current: number; + total: number; + percentage: number; + }; + avatar?: { + url?: string; + title?: string; + }; +}; + +type MuiDialogProps = { + title?: { + name?: string; + icon?: string; + }; + openDialog: boolean; + setOpenDialog: Function; + content?: ReactElement; + data: DataContentType; +}; +/* -------------------------------------------------------------------------- */ + +/* --------------------------------- styles --------------------------------- */ +const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ + height: 10, + borderRadius: 6, + [`&.${linearProgressClasses.colorPrimary}`]: { + backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800], + }, + [`& .${linearProgressClasses.bar}`]: { + borderRadius: 6, + background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)', + }, +})); +/* -------------------------------------------------------------------------- */ + +const DialogRequestLog = ({ openDialog, setOpenDialog, data }: MuiDialogProps) => { + const [serviceCode, setServiceCode] = useState('IP'); + + const fileDiagnosaInput = useRef(null); + const [fileDiagnosas, setFileDiagnosas] = useState([]); + + const handleDiagnosaInputChange = (event) => { + if (event.target.files[0]) { + setFileDiagnosas([...fileDiagnosas, ...event.target.files]); + } else { + console.log('NO FILE'); + } + }; + const removeDiagnosaFiles = (filesState, index) => { + setFileDiagnosas(filesState.filter((file, fileIndex) => fileIndex != index)); + }; + + const fileKondisiInput = useRef(null); + const [fileKondisis, setFileKondisis] = useState([]); + + const handleKondisiInputChange = (event) => { + if (event.target.files[0]) { + setFileKondisis([...fileKondisis, ...event.target.files]); + } else { + console.log('NO FILE'); + } + }; + const removeKondisiFiles = (filesState, index) => { + setFileKondisis(filesState.filter((file, fileIndex) => fileIndex != index)); + }; + + const fileHasilPenunjangInput = useRef(null); + const [fileHasilPenunjangs, setFileHasilPenunjangs] = useState([]); + + const handleResultInputChange = (event) => { + if (event.target.files[0]) { + setFileHasilPenunjangs([...fileHasilPenunjangs, ...event.target.files]); + } else { + console.log('NO FILE'); + } + }; + const removeFiles = (filesState, index) => { + setFileHasilPenunjangs(filesState.filter((file, fileIndex) => fileIndex != index)); + }; + + const [submitLoading, setSubmitLoading] = useState(false); + function submitRequest() { + setSubmitLoading(true); + const formData = makeFormData({ + member_id: data.id, + result_files: fileHasilPenunjangs, + diagnosa_files: fileDiagnosas, + kondisi_files: fileKondisis, + service_code: serviceCode, + }); + + axios + .post('/claim-requests', formData) + .then((response) => { + enqueueSnackbar(response.data.message ?? 'Berhasil membuat data', { variant: 'success' }); + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' }); + }) + .then(() => { + setSubmitLoading(false); + }); + } + + const getContent = () => ( + + + + Submission Date :
{fPostFormat(new Date(), 'dd/MM/yyyy')} +
+
+ + + + + + + + + + + + + {data.fullName} + {data.memberId} + + + + + + + Total Limit + + + + {fCurrency(data.limit.current)} / {fCurrency(data.limit.total)} + + + + } + spacing={4} + sx={{ marginY: 2 }} + > + + + Dokumen Kondisi + + } + spacing={1} + sx={{ marginY: 2 }} + > + {fileKondisis && + fileKondisis.map((file, index) => ( + + {file.name} + { + removeKondisiFiles(fileKondisis, index); + }} + /> + + ))} + + + { + fileKondisiInput?.current?.click(); + }} + > + + Add Result + + + + + + Dokumen Diagnosa + + {/* Hasil Lab, */} + } + spacing={1} + sx={{ marginY: 2 }} + > + {fileDiagnosas && + fileDiagnosas.map((file, index) => ( + + {file.name} + { + removeDiagnosaFiles(fileDiagnosas, index); + }} + /> + + ))} + + + { + fileDiagnosaInput?.current?.click(); + }} + > + + Add Result + + + + + + Dokumen Hasil Penunjang + + } + spacing={1} + sx={{ marginY: 2 }} + > + {fileHasilPenunjangs && + fileHasilPenunjangs.map((file, index) => ( + + {file.name} + { + removeFiles(fileHasilPenunjangs, index); + }} + /> + + ))} + + + { + fileHasilPenunjangInput?.current?.click(); + }} + > + + Add File + + + + + { + submitRequest(); + }} + loading={submitLoading} + > + LOG Request + +
+ ); + + return ( + // <> + // + // + getContent() + ); +}; + +export default DialogRequestLog; diff --git a/frontend/client-portal/src/sections/claim-submit/DialogTopUpLimit.tsx b/frontend/client-portal/src/sections/claim-submit/DialogTopUpLimit.tsx new file mode 100644 index 00000000..4e22a137 --- /dev/null +++ b/frontend/client-portal/src/sections/claim-submit/DialogTopUpLimit.tsx @@ -0,0 +1,265 @@ +// @mui +import { styled } from '@mui/material/styles'; +import { + Typography, + LinearProgress, + linearProgressClasses, + Stack, + FormControlLabel, +} from '@mui/material'; +import { LoadingButton } from '@mui/lab'; +import Checkbox from '@mui/material/Checkbox'; +// components +import MuiDialog from '../../components/MuiDialog'; +import { FormProvider, RHFTextField } from '../../components/hook-form'; +// React +import { useContext, ReactElement, useEffect, useState } from 'react'; +import { fCurrency } from '../../utils/formatNumber'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; + +// yup +import * as Yup from 'yup'; +// form +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import axios from '../../utils/axios'; +import { enqueueSnackbar } from 'notistack'; + +/* ---------------------------------- types --------------------------------- */ +type MuiDialogProps = { + title?: { + name?: string; + icon?: string; + }; + openDialog: boolean; + setOpenDialog: Function; + content?: ReactElement; + data?: DataProps; +}; + +type DataProps = { + companyName: string; + policyNumber: number; + totalMembers: number; + totalCases: number; + totalPersen: number; + myLimit: { + balance: number; + total: number; + percentage: number; + }; + maxTopUp: number; +}; + +type FormValuesProps = { + topup: string; +}; +/* -------------------------------------------------------------------------- */ + +const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ + height: 10, + borderRadius: 6, + [`&.${linearProgressClasses.colorPrimary}`]: { + backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800], + }, + [`& .${linearProgressClasses.bar}`]: { + borderRadius: 6, + background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)', + }, +})); + +// ---------------------------------------------------------------------- + +export default function DialogTopUpLimit({ + title, + openDialog, + setOpenDialog, + data, +}: MuiDialogProps) { + const [isDisabledInput, setIsDisabledInput] = useState(false); + const [isDisabledButton, setIsDisabledButton] = useState(true); + const [isCheckboxChecked, setIsCheckboxChecked] = useState(false); + const [ message, setMessage ] = useState (''); + const { corporateValue } = useContext(UserCurrentCorporateContext); + + const TopUpSchema = Yup.object().shape({ + topup: Yup.number().max( + data?.maxTopUp, + `Maximum top-up amount is ${fCurrency(data?.maxTopUp)}` + ), + }); + + const defaultValues = { + topup: 0, + }; + + const methods = useForm({ + resolver: yupResolver(TopUpSchema), + // @ts-ignore + defaultValues, + }); + + const { + setValue, + reset, + handleSubmit, + formState: { errors, isSubmitting }, + } = methods; + + useEffect(() => { + if (openDialog === false) { + setIsDisabledInput(false); + setIsDisabledButton(true); + setIsCheckboxChecked(false); + + reset(); + } + }, [openDialog, reset]); + + const onSubmit = async (data: FormValuesProps) => { + + + await new Promise((resolve) => setTimeout(resolve, 500)); + + setIsDisabledInput(false); + setIsDisabledButton(true); + setIsCheckboxChecked(false); + + try { + // Send the HTTP POST request to the backend + await axios.post(corporateValue + '/topup', { + topup: data.topup, + }); + + // Show a success notification + enqueueSnackbar('The request has been sent', { variant: 'success' }); + setOpenDialog(false); + + reset(); + } catch (error) { + // Show an error notification + enqueueSnackbar('An error occurred', { variant: 'error' }); + setOpenDialog(false); + } + + + }; + + const onCheckHandler = (value: string) => { + setIsDisabledInput(!isDisabledInput); + value === '0' || value === '' ? setIsDisabledButton(true) : setIsDisabledButton(false); + setIsCheckboxChecked(!isCheckboxChecked); + // @ts-ignore + setValue('topup', data.maxTopUp.toString()); + }; + + const onTopupHandler = (value: string) => { + //console.log(!!errors); + + let newValue; + + if (value.startsWith('0')) { + newValue = '0'; + } else { + newValue = value; + } + + newValue === '0' || newValue === '' ? setIsDisabledButton(true) : setIsDisabledButton(false); + setValue('topup', newValue); + }; + + const getContent = () => ( + + + + Company Name + + {data ? data.companyName : ''} + + + + Policy Number + + {data ? data.policyNumber : 0} + + + + + Total Member + + {data ? data.totalMembers : 0} Person + + + + Total Cases + + {data ? data.totalCases : 0} Cases + + + + + + Company Pooled Fund + {fCurrency(data ? data.myLimit.balance : 0)} + + / {data ? data.myLimit.total : 0} + + + + {data ? data.myLimit.percentage : 0}% + + + + + + + Top Up Limit + + + onTopupHandler(e.target.value)} + error={!!errors.topup} + helperText={errors.topup?.message} + /> + onCheckHandler(e.target.value)} + /> + } + label={'Max ' + fCurrency(data ? data.maxTopUp : 0)} + /> + + + Ajukan Permintaan + + + + + ); + + return ( + + ); +}