Merge branch 'feature/client-portal-dashboard' into staging

This commit is contained in:
Fajar
2023-03-24 15:25:05 +07:00
6 changed files with 225 additions and 138 deletions

View File

@@ -21,12 +21,14 @@ class CorporateMemberService
$corporateEmployee->where('corporate_id', $corporateId); $corporateEmployee->where('corporate_id', $corporateId);
}) })
->when($request->input('search'), function (Builder $query, $search) { ->when($request->input('search'), function (Builder $query, $search) {
$query->where('member_id', 'like', "%" . $search . "%") $query->where(function (Builder $query) use ($search) {
->orWhere('name', 'like', "%" . $search . "%"); $query->orWhere('members.member_id', 'like', "%" . $search . "%")
->orWhere('members.name', 'like', "%" . $search . "%");
});
}) })
->when($request->input('division'), function (Builder $division, $division_id) { ->when($request->input('division'), function (Builder $division, $value) {
$division->whereHas('division', function ($corporateEmployee) use ($division_id) { $division->whereHas('division', function (Builder $corporateEmployee) use ($value) {
$corporateEmployee->where('division_id', $division_id); $corporateEmployee->where('division_id', $value);
}); });
}) })
->when($request->has('orderBy'), function (Builder $query) use ($request) { ->when($request->has('orderBy'), function (Builder $query) use ($request) {

View File

@@ -1,4 +1,5 @@
import { Dispatch, SetStateAction } from 'react'; import { SelectChangeEvent } from '@mui/material';
import { Dispatch, FormEvent, SetStateAction } from 'react';
/* ------------------------------- pagination ------------------------------- */ /* ------------------------------- pagination ------------------------------- */
export type PaginationTableProps = { export type PaginationTableProps = {
@@ -34,6 +35,13 @@ export type HeadCell<DataType> = {
}; };
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* ----------------------------- division filter ---------------------------- */
export type DivisionData = {
id: number;
name: string;
};
/* -------------------------------------------------------------------------- */
/* ----------------------------------- row ---------------------------------- */ /* ----------------------------------- row ---------------------------------- */
export type TableListProps<DataType> = { export type TableListProps<DataType> = {
headCells?: HeadCell<DataType>[]; headCells?: HeadCell<DataType>[];
@@ -62,5 +70,19 @@ export type TableListProps<DataType> = {
appliedParams: {}; appliedParams: {};
setAppliedParams: Dispatch<SetStateAction<{}>>; setAppliedParams: Dispatch<SetStateAction<{}>>;
}; };
searchs: {
searchText: string;
setSearchText: Dispatch<SetStateAction<string>>;
handleSearchSubmit: (event: FormEvent<HTMLFormElement>) => void;
};
filters?: {
useFilter: boolean;
config: {
label: string;
divisionValue: string;
divisionData: DivisionData[];
handleDivisionChange: (event: SelectChangeEvent) => void;
};
};
}; };
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */

View File

@@ -28,7 +28,7 @@ import { visuallyHidden } from '@mui/utils';
/* ---------------------------------- axios --------------------------------- */ /* ---------------------------------- axios --------------------------------- */
import axios from '../utils/axios'; import axios from '../utils/axios';
/* ---------------------------------- react --------------------------------- */ /* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react'; import { Fragment, useContext, useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
/* -------------------------------- component ------------------------------- */ /* -------------------------------- component ------------------------------- */
import BaseTablePagination from './BaseTablePagination'; import BaseTablePagination from './BaseTablePagination';
@@ -61,6 +61,8 @@ export default function Table<T>({
orders, orders,
loadings, loadings,
params, params,
filters,
searchs,
}: TableListProps<T>) { }: TableListProps<T>) {
/* ------------------------------- handle sort ------------------------------ */ /* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => { const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
@@ -120,46 +122,6 @@ export default function Table<T>({
}; };
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* ----------------------------- division field ----------------------------- */
// const [divisionValue, setDivisionValue] = useState('all');
// const [divisionData, setDivisionData] = useState([]);
// const handleDivisionChange = (event: SelectChangeEvent) => {
// setDivisionValue(event.target.value as string);
// if (event.target.value === 'all') {
// searchParams.delete('division');
// const params = Object.fromEntries([...searchParams.entries()]);
// setAppliedParams(params);
// } else {
// const params = Object.fromEntries([
// ...searchParams.entries(),
// ['division', event.target.value as string],
// ]);
// setAppliedParams(params);
// }
// };
/* -------------------------------------------------------------------------- */
/* ------------------------------ Search field ------------------------------ */
// const [searchText, setSearchText] = useState('');
// const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
// event.preventDefault();
// setIsLoading(true);
// 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 ------------------------ */ /* ------------------------ button change pagination ------------------------ */
const onPageChangeHandle = async ( const onPageChangeHandle = async (
event: React.MouseEvent<HTMLButtonElement> | null, event: React.MouseEvent<HTMLButtonElement> | null,
@@ -194,41 +156,58 @@ export default function Table<T>({
<Card> <Card>
<Grid container> <Grid container>
{/* Field 1 */} {/* Field 1 */}
{/* <Grid item xs={12} paddingX="24px" paddingY="20px"> <Grid item xs={12} paddingX="24px" paddingY="20px">
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={12} lg={3} xl={2}> {filters && filters.useFilter ? (
<FormControl fullWidth> <Fragment>
<InputLabel id="simple-division-select-lable">Division</InputLabel> <Grid item xs={12} lg={3} xl={2}>
<Select <FormControl fullWidth>
labelId="simple-division-select-lable" <InputLabel id="simple-division-select-lable">Division</InputLabel>
id="division-select-lable" <Select
value={divisionValue} labelId="simple-division-select-lable"
label="Division" id="division-select-lable"
onChange={handleDivisionChange} value={filters.config.divisionValue}
> label="Division"
<MenuItem value="all">All</MenuItem> onChange={filters.config.handleDivisionChange}
{divisionData.map((row: DivisionDataProps, index) => ( >
<MenuItem key={index} value={row.id}> <MenuItem value="all">All</MenuItem>
{row.name} {filters.config.divisionData.map((row: DivisionDataProps, index) => (
</MenuItem> <MenuItem key={index} value={row.id}>
))} {row.name}
</Select> </MenuItem>
</FormControl> ))}
</Grid> </Select>
<Grid item xs={12} lg={9} xl={10}> </FormControl>
<form onSubmit={handleSearchSubmit}> </Grid>
<TextField <Grid item xs={12} lg={9} xl={10}>
id="search-input" <form onSubmit={searchs.handleSearchSubmit}>
label="Search" <TextField
variant="outlined" id="search-input"
onChange={(event) => setSearchText(event.target.value)} label="Search"
value={searchText} variant="outlined"
fullWidth onChange={(event) => searchs.setSearchText(event.target.value)}
/> value={searchs.searchText}
</form> fullWidth
</Grid> />
</form>
</Grid>
</Fragment>
) : (
<Grid item xs={12}>
<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>
)}
</Grid> </Grid>
</Grid> */} </Grid>
{/* End Field 1 */} {/* End Field 1 */}
{/* Field 2 */} {/* Field 2 */}
<Grid item xs={12}> <Grid item xs={12}>

View File

@@ -8,6 +8,7 @@ import {
IconButton, IconButton,
LinearProgress, LinearProgress,
linearProgressClasses, linearProgressClasses,
SelectChangeEvent,
} from '@mui/material'; } from '@mui/material';
// hooks // hooks
import useSettings from '../../hooks/useSettings'; import useSettings from '../../hooks/useSettings';
@@ -22,11 +23,10 @@ import { Stack } from '@mui/system';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { PolicyProps } from '../../@types/policy'; import { PolicyProps } from '../../@types/policy';
import Table from '../../components/Table'; import Table from '../../components/Table';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table'; import { DivisionData, HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import palette from '../../theme/palette'; import palette from '../../theme/palette';
import { MoreVert as MoreVertIcon } from '@mui/icons-material'; import { MoreVert as MoreVertIcon } from '@mui/icons-material';
import TableList from '../../sections/dashboard/TableList';
import { fSplit } from '../../utils/formatNumber'; import { fSplit } from '../../utils/formatNumber';
const itemList = [ const itemList = [
@@ -130,6 +130,60 @@ export default function Index() {
}; };
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* ------------------------------ 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 = {
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle filter ----------------------------- */
const [divisionValue, setDivisionValue] = useState('all');
const [divisionData, setDivisionData] = useState([]);
const handleDivisionChange = (event: SelectChangeEvent) => {
setDivisionValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('division');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['division', event.target.value as string],
]);
setAppliedParams(params);
}
};
const filters = {
useFilter: true,
config: {
label: 'Division',
divisionValue: divisionValue,
divisionData: divisionData,
handleDivisionChange: handleDivisionChange,
},
};
/* -------------------------------------------------------------------------- */
/* -------------------------------- headCell -------------------------------- */ /* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [ const headCells: HeadCell<never>[] = [
{ {
@@ -181,6 +235,7 @@ export default function Index() {
useEffect(() => { useEffect(() => {
(async () => { (async () => {
setIsLoading(true); setIsLoading(true);
let mounted = true;
await new Promise((resolve) => setTimeout(resolve, 250)); await new Promise((resolve) => setTimeout(resolve, 250));
@@ -196,17 +251,30 @@ export default function Index() {
params: { ...parameters }, params: { ...parameters },
}); });
setPolicyData(corporatePolicyLimit.data.data);
setSearchParams(parameters); setSearchParams(parameters);
setMemberData(corporateMembers.data.data);
setPaginationTable(corporateMembers.data);
setRowsPerPage(corporateMembers.data.per_page);
setIsLoading(false); if (mounted) {
setPolicyData(corporatePolicyLimit.data.data);
setDivisionData(corporateDivision.data);
setMemberData(corporateMembers.data.data);
setPaginationTable(corporateMembers.data);
setRowsPerPage(corporateMembers.data.per_page);
setIsLoading(false);
}
return () => {
mounted = false;
};
})(); })();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]); }, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
/* ------------------------------- card policy ------------------------------ */
const newPolicyData = {
limit: policyData,
};
/* -------------------------------------------------------------------------- */
const newMemberData: any = memberData.map((obj: any) => { const newMemberData: any = memberData.map((obj: any) => {
return { return {
...obj, ...obj,
@@ -270,7 +338,7 @@ export default function Index() {
<CardNotification data={itemList} /> <CardNotification data={itemList} />
</Grid> </Grid>
<Grid item xs={12} lg={6} md={12}> <Grid item xs={12} lg={6} md={12}>
<CardPolicy data={policyData} /> <CardPolicy data={newPolicyData} />
</Grid> </Grid>
<Grid item xs={12} lg={12} md={12}> <Grid item xs={12} lg={12} md={12}>
<Table <Table
@@ -280,8 +348,9 @@ export default function Index() {
paginations={paginations} paginations={paginations}
loadings={loadings} loadings={loadings}
params={params} params={params}
searchs={searchs}
filters={filters}
/> />
{/* <TableList /> */}
</Grid> </Grid>
</Grid> </Grid>
</Container> </Container>

View File

@@ -22,15 +22,26 @@ import DialogClaimSubmitMember from './DialogClaimSubmitMember';
type CardPolicyProps = { type CardPolicyProps = {
data: { data: {
myLimit: { limit: {
balance: number; myLimit: {
total: number; balance: number;
percentage: number; total: number;
}; percentage: number;
lockLimit: { };
balance: number; lockLimit: {
percentage: number; balance: number;
percentage: number;
};
}; };
// topUpLimit: {
// companyName: string;
// policyNumber: number;
// totalMembers: number;
// totalCases: number;
// totalPersen: number;
// myLimit: number;
// totalLimit: number;
// };
}; };
}; };
@@ -65,7 +76,7 @@ export default function CardPolicy(props: CardPolicyProps) {
const [dialogTitle, setDialogTitle] = useState(''); const [dialogTitle, setDialogTitle] = useState('');
const [isDialog, setIsDialog] = useState(''); const [isDialog, setIsDialog] = useState('');
const { myLimit, lockLimit } = props.data; const { limit } = props.data;
const clickHandler = (isDialog: string) => { const clickHandler = (isDialog: string) => {
switch (isDialog) { switch (isDialog) {
@@ -94,23 +105,23 @@ export default function CardPolicy(props: CardPolicyProps) {
Total Limit Total Limit
</Typography> </Typography>
<Typography sx={{ typography: 'body2' }}> <Typography sx={{ typography: 'body2' }}>
{fCurrency(myLimit ? myLimit.balance : 0)} {fCurrency(limit.myLimit ? limit.myLimit.balance : 0)}
</Typography> </Typography>
<Typography sx={{ typography: 'caption', color: '#919EAB' }}> <Typography sx={{ typography: 'caption', color: '#919EAB' }}>
/ {fSplit(myLimit ? myLimit.total : 0)} / {fSplit(limit.myLimit ? limit.myLimit.total : 0)}
</Typography> </Typography>
</div> </div>
<Stack direction="row" alignItems="center" justifyContent="center"> <Stack direction="row" alignItems="center" justifyContent="center">
<Typography variant="h5" sx={{ ml: 0.5 }}> <Typography variant="h5" sx={{ ml: 0.5 }}>
{myLimit ? myLimit.percentage : 0}% {limit.myLimit ? limit.myLimit.percentage : 0}%
</Typography> </Typography>
</Stack> </Stack>
</Stack> </Stack>
<BorderLinearProgress <BorderLinearProgress
variant="determinate" variant="determinate"
value={myLimit ? myLimit.percentage : 0} value={limit.myLimit ? limit.myLimit.percentage : 0}
sx={{ mb: 1 }} sx={{ mb: 1 }}
/> />
@@ -123,11 +134,12 @@ export default function CardPolicy(props: CardPolicyProps) {
sx={{ color: '#424242', marginRight: '6px' }} sx={{ color: '#424242', marginRight: '6px' }}
/> />
<Typography variant="caption" component="span"> <Typography variant="caption" component="span">
Lock Fund ( {lockLimit ? lockLimit.percentage : 0}% ) Lock Fund ( {limit.lockLimit ? limit.lockLimit.percentage : 0}% )
</Typography> </Typography>
</Typography> </Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}> <Typography sx={{ typography: 'caption', color: '#637381' }}>
{fSplit(lockLimit ? lockLimit.balance : 0)} / {fSplit(myLimit ? myLimit.total : 0)} {fSplit(limit.lockLimit ? limit.lockLimit.balance : 0)} /{' '}
{fSplit(limit.myLimit ? limit.myLimit.total : 0)}
</Typography> </Typography>
</Stack> </Stack>

View File

@@ -21,14 +21,7 @@ import * as Yup from 'yup';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup'; import { yupResolver } from '@hookform/resolvers/yup';
// ---------------------------------------------------------------------- /* ---------------------------------- types --------------------------------- */
type DataContent = {
info: string;
date: string;
time: string;
};
type MuiDialogProps = { type MuiDialogProps = {
title?: { title?: {
name?: string; name?: string;
@@ -37,24 +30,31 @@ type MuiDialogProps = {
openDialog: boolean; openDialog: boolean;
setOpenDialog: Function; setOpenDialog: Function;
content?: ReactElement; content?: ReactElement;
data?: DataContent[]; data?: {
companyName: string;
policyNumber: number;
totalMembers: number;
totalCases: number;
totalPersen: number;
myLimit: number;
totalLimit: number;
};
}; };
type FormValuesProps = { type FormValuesProps = {
topup: string; topup: string;
}; };
/* -------------------------------------------------------------------------- */
// ---------------------------------------------------------------------- // const testData = {
// companyName: 'PT. Aman Mineral',
const testData = { // policyNumber: 12345678,
companyName: 'PT. Aman Mineral', // totalMembers: 50,
policyNumber: 12345678, // totalCases: 100,
totalMembers: 50, // totalPersen: 75,
totalCases: 100, // myLimit: 375000000,
totalPersen: 75, // totalLimit: 500000000,
myLimit: 375000000, // };
totalLimit: 500000000,
};
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10, height: 10,
@@ -70,7 +70,12 @@ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => { export default function DialogTopUpLimit({
title,
openDialog,
setOpenDialog,
data,
}: MuiDialogProps) {
const [isDisabledCheckbox, setIsDisabledCheckbox] = useState(false); const [isDisabledCheckbox, setIsDisabledCheckbox] = useState(false);
const [isDisabledButton, setIsDisabledButton] = useState(true); const [isDisabledButton, setIsDisabledButton] = useState(true);
@@ -126,42 +131,42 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP
<Typography variant="caption" color="#637381"> <Typography variant="caption" color="#637381">
Company Name Company Name
</Typography> </Typography>
<Typography variant="body2">{testData.companyName}</Typography> <Typography variant="body2">{data ? data.companyName : ''}</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption" color="#637381"> <Typography variant="caption" color="#637381">
Policy Number Policy Number
</Typography> </Typography>
<Typography variant="body2">{testData.policyNumber}</Typography> <Typography variant="body2">{data ? data.policyNumber : 0}</Typography>
</Stack> </Stack>
<Stack direction="row" spacing={22}> <Stack direction="row" spacing={22}>
<Stack> <Stack>
<Typography variant="caption" color="#637381"> <Typography variant="caption" color="#637381">
Total Member Total Member
</Typography> </Typography>
<Typography variant="body2">{testData.totalMembers} Person</Typography> <Typography variant="body2">{data ? data.totalMembers : 0} Person</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption" color="#637381"> <Typography variant="caption" color="#637381">
Total Cases Total Cases
</Typography> </Typography>
<Typography variant="body2">{testData.totalCases} Cases</Typography> <Typography variant="body2">{data ? data.totalCases : 0} Cases</Typography>
</Stack> </Stack>
</Stack> </Stack>
<Stack spacing={1} sx={{ backgroundColor: '#F4F6F8', borderRadius: 1.5, padding: 2 }}> <Stack spacing={1} sx={{ backgroundColor: '#F4F6F8', borderRadius: 1.5, padding: 2 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center"> <Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack> <Stack>
<Typography variant="body2">Company Pooled Fund</Typography> <Typography variant="body2">Company Pooled Fund</Typography>
<Typography variant="body2">{fCurrency(testData.myLimit)}</Typography> <Typography variant="body2">{fCurrency(data ? data.myLimit : 0)}</Typography>
<Typography variant="caption" color="#919EAB"> <Typography variant="caption" color="#919EAB">
/ {testData.totalLimit} / {data ? data.totalLimit : 0}
</Typography> </Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="h5">{testData.totalPersen}%</Typography> <Typography variant="h5">{data ? data.totalPersen : 0}%</Typography>
</Stack> </Stack>
</Stack> </Stack>
<BorderLinearProgress variant="determinate" value={testData.totalPersen} /> <BorderLinearProgress variant="determinate" value={data ? data.totalPersen : 0} />
</Stack> </Stack>
<Stack spacing={2}> <Stack spacing={2}>
<Typography variant="subtitle1" marginTop={3}> <Typography variant="subtitle1" marginTop={3}>
@@ -178,7 +183,7 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP
<FormControlLabel <FormControlLabel
sx={{ typography: 'caption' }} sx={{ typography: 'caption' }}
control={<Checkbox />} control={<Checkbox />}
label={'Max ' + fCurrency(testData.totalLimit - testData.myLimit)} label={'Max ' + fCurrency((data ? data.totalLimit : 0) - (data ? data.myLimit : 0))}
onChange={handleSubmit(onCheckHandler)} onChange={handleSubmit(onCheckHandler)}
/> />
@@ -207,6 +212,4 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP
maxWidth="xs" maxWidth="xs"
/> />
); );
}; }
export default DialogTopUpLimit;