From 127cdf87080a5f0ab1c7f0b9a3fef27e5dce5c01 Mon Sep 17 00:00:00 2001 From: Fajar Date: Fri, 24 Mar 2023 15:23:41 +0700 Subject: [PATCH] fixing table filter and search on dashboard --- app/Services/CorporateMemberService.php | 12 +- frontend/client-portal/src/@types/table.ts | 24 +++- .../client-portal/src/components/Table.tsx | 127 ++++++++---------- .../src/pages/Dashboard/Index.tsx | 89 ++++++++++-- .../src/sections/dashboard/CardPolicy.tsx | 42 +++--- .../sections/dashboard/DialogTopUpLimit.tsx | 69 +++++----- 6 files changed, 225 insertions(+), 138 deletions(-) diff --git a/app/Services/CorporateMemberService.php b/app/Services/CorporateMemberService.php index 3db82921..c17143f7 100644 --- a/app/Services/CorporateMemberService.php +++ b/app/Services/CorporateMemberService.php @@ -21,12 +21,14 @@ class CorporateMemberService $corporateEmployee->where('corporate_id', $corporateId); }) ->when($request->input('search'), function (Builder $query, $search) { - $query->where('member_id', 'like', "%" . $search . "%") - ->orWhere('name', 'like', "%" . $search . "%"); + $query->where(function (Builder $query) use ($search) { + $query->orWhere('members.member_id', 'like', "%" . $search . "%") + ->orWhere('members.name', 'like', "%" . $search . "%"); + }); }) - ->when($request->input('division'), function (Builder $division, $division_id) { - $division->whereHas('division', function ($corporateEmployee) use ($division_id) { - $corporateEmployee->where('division_id', $division_id); + ->when($request->input('division'), function (Builder $division, $value) { + $division->whereHas('division', function (Builder $corporateEmployee) use ($value) { + $corporateEmployee->where('division_id', $value); }); }) ->when($request->has('orderBy'), function (Builder $query) use ($request) { diff --git a/frontend/client-portal/src/@types/table.ts b/frontend/client-portal/src/@types/table.ts index e54f0cec..4ba7bafc 100644 --- a/frontend/client-portal/src/@types/table.ts +++ b/frontend/client-portal/src/@types/table.ts @@ -1,4 +1,5 @@ -import { Dispatch, SetStateAction } from 'react'; +import { SelectChangeEvent } from '@mui/material'; +import { Dispatch, FormEvent, SetStateAction } from 'react'; /* ------------------------------- pagination ------------------------------- */ export type PaginationTableProps = { @@ -34,6 +35,13 @@ export type HeadCell = { }; /* -------------------------------------------------------------------------- */ +/* ----------------------------- division filter ---------------------------- */ +export type DivisionData = { + id: number; + name: string; +}; +/* -------------------------------------------------------------------------- */ + /* ----------------------------------- row ---------------------------------- */ export type TableListProps = { headCells?: HeadCell[]; @@ -62,5 +70,19 @@ export type TableListProps = { appliedParams: {}; setAppliedParams: Dispatch>; }; + searchs: { + searchText: string; + setSearchText: Dispatch>; + handleSearchSubmit: (event: FormEvent) => void; + }; + filters?: { + useFilter: boolean; + config: { + label: string; + divisionValue: string; + divisionData: DivisionData[]; + handleDivisionChange: (event: SelectChangeEvent) => void; + }; + }; }; /* -------------------------------------------------------------------------- */ diff --git a/frontend/client-portal/src/components/Table.tsx b/frontend/client-portal/src/components/Table.tsx index 9cb58a4e..2f7e86f9 100644 --- a/frontend/client-portal/src/components/Table.tsx +++ b/frontend/client-portal/src/components/Table.tsx @@ -28,7 +28,7 @@ import { visuallyHidden } from '@mui/utils'; /* ---------------------------------- axios --------------------------------- */ import axios from '../utils/axios'; /* ---------------------------------- react --------------------------------- */ -import { useContext, useEffect, useState } from 'react'; +import { Fragment, useContext, useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; /* -------------------------------- component ------------------------------- */ import BaseTablePagination from './BaseTablePagination'; @@ -61,6 +61,8 @@ export default function Table({ orders, loadings, params, + filters, + searchs, }: TableListProps) { /* ------------------------------- handle sort ------------------------------ */ const handleRequestSort = async (event: React.MouseEvent, property: string) => { @@ -120,46 +122,6 @@ export default function Table({ }; /* -------------------------------------------------------------------------- */ - /* ----------------------------- 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) => { - // 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 ------------------------ */ const onPageChangeHandle = async ( event: React.MouseEvent | null, @@ -194,41 +156,58 @@ export default function Table({ {/* Field 1 */} - {/* + - - - Division - - - - -
- setSearchText(event.target.value)} - value={searchText} - fullWidth - /> - -
+ {filters && filters.useFilter ? ( + + + + Division + + + + +
+ searchs.setSearchText(event.target.value)} + value={searchs.searchText} + fullWidth + /> + +
+
+ ) : ( + +
+ searchs.setSearchText(event.target.value)} + value={searchs.searchText} + fullWidth + /> + +
+ )}
-
*/} +
{/* End Field 1 */} {/* Field 2 */} diff --git a/frontend/client-portal/src/pages/Dashboard/Index.tsx b/frontend/client-portal/src/pages/Dashboard/Index.tsx index 1946dc42..b8f4b7e8 100755 --- a/frontend/client-portal/src/pages/Dashboard/Index.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Index.tsx @@ -8,6 +8,7 @@ import { IconButton, LinearProgress, linearProgressClasses, + SelectChangeEvent, } from '@mui/material'; // hooks import useSettings from '../../hooks/useSettings'; @@ -22,11 +23,10 @@ import { Stack } from '@mui/system'; import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; import { PolicyProps } from '../../@types/policy'; 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 palette from '../../theme/palette'; import { MoreVert as MoreVertIcon } from '@mui/icons-material'; -import TableList from '../../sections/dashboard/TableList'; import { fSplit } from '../../utils/formatNumber'; const itemList = [ @@ -130,6 +130,60 @@ export default function Index() { }; /* -------------------------------------------------------------------------- */ + /* ------------------------------ handle search ----------------------------- */ + const [searchText, setSearchText] = useState(''); + + const handleSearchSubmit = async (event: React.FormEvent) => { + 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 -------------------------------- */ const headCells: HeadCell[] = [ { @@ -181,6 +235,7 @@ export default function Index() { useEffect(() => { (async () => { setIsLoading(true); + let mounted = true; await new Promise((resolve) => setTimeout(resolve, 250)); @@ -196,17 +251,30 @@ export default function Index() { params: { ...parameters }, }); - setPolicyData(corporatePolicyLimit.data.data); - 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]); + /* ------------------------------- card policy ------------------------------ */ + const newPolicyData = { + limit: policyData, + }; + /* -------------------------------------------------------------------------- */ + const newMemberData: any = memberData.map((obj: any) => { return { ...obj, @@ -270,7 +338,7 @@ export default function Index() { - + - {/* */} diff --git a/frontend/client-portal/src/sections/dashboard/CardPolicy.tsx b/frontend/client-portal/src/sections/dashboard/CardPolicy.tsx index d7f8d4e1..5aec52a3 100644 --- a/frontend/client-portal/src/sections/dashboard/CardPolicy.tsx +++ b/frontend/client-portal/src/sections/dashboard/CardPolicy.tsx @@ -22,15 +22,26 @@ import DialogClaimSubmitMember from './DialogClaimSubmitMember'; type CardPolicyProps = { data: { - myLimit: { - balance: number; - total: number; - percentage: number; - }; - lockLimit: { - balance: number; - percentage: number; + 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: number; + // totalLimit: number; + // }; }; }; @@ -65,7 +76,7 @@ export default function CardPolicy(props: CardPolicyProps) { const [dialogTitle, setDialogTitle] = useState(''); const [isDialog, setIsDialog] = useState(''); - const { myLimit, lockLimit } = props.data; + const { limit } = props.data; const clickHandler = (isDialog: string) => { switch (isDialog) { @@ -94,23 +105,23 @@ export default function CardPolicy(props: CardPolicyProps) { Total Limit - {fCurrency(myLimit ? myLimit.balance : 0)} + {fCurrency(limit.myLimit ? limit.myLimit.balance : 0)} - / {fSplit(myLimit ? myLimit.total : 0)} + / {fSplit(limit.myLimit ? limit.myLimit.total : 0)} - {myLimit ? myLimit.percentage : 0}% + {limit.myLimit ? limit.myLimit.percentage : 0}% @@ -123,11 +134,12 @@ export default function CardPolicy(props: CardPolicyProps) { sx={{ color: '#424242', marginRight: '6px' }} /> - Lock Fund ( {lockLimit ? lockLimit.percentage : 0}% ) + Lock Fund ( {limit.lockLimit ? limit.lockLimit.percentage : 0}% ) - {fSplit(lockLimit ? lockLimit.balance : 0)} / {fSplit(myLimit ? myLimit.total : 0)} + {fSplit(limit.lockLimit ? limit.lockLimit.balance : 0)} /{' '} + {fSplit(limit.myLimit ? limit.myLimit.total : 0)} diff --git a/frontend/client-portal/src/sections/dashboard/DialogTopUpLimit.tsx b/frontend/client-portal/src/sections/dashboard/DialogTopUpLimit.tsx index fbff72bc..1025d291 100644 --- a/frontend/client-portal/src/sections/dashboard/DialogTopUpLimit.tsx +++ b/frontend/client-portal/src/sections/dashboard/DialogTopUpLimit.tsx @@ -21,14 +21,7 @@ import * as Yup from 'yup'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; -// ---------------------------------------------------------------------- - -type DataContent = { - info: string; - date: string; - time: string; -}; - +/* ---------------------------------- types --------------------------------- */ type MuiDialogProps = { title?: { name?: string; @@ -37,24 +30,31 @@ type MuiDialogProps = { openDialog: boolean; setOpenDialog: Function; content?: ReactElement; - data?: DataContent[]; + data?: { + companyName: string; + policyNumber: number; + totalMembers: number; + totalCases: number; + totalPersen: number; + myLimit: number; + totalLimit: number; + }; }; type FormValuesProps = { topup: string; }; +/* -------------------------------------------------------------------------- */ -// ---------------------------------------------------------------------- - -const testData = { - companyName: 'PT. Aman Mineral', - policyNumber: 12345678, - totalMembers: 50, - totalCases: 100, - totalPersen: 75, - myLimit: 375000000, - totalLimit: 500000000, -}; +// const testData = { +// companyName: 'PT. Aman Mineral', +// policyNumber: 12345678, +// totalMembers: 50, +// totalCases: 100, +// totalPersen: 75, +// myLimit: 375000000, +// totalLimit: 500000000, +// }; const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ 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 [isDisabledButton, setIsDisabledButton] = useState(true); @@ -126,42 +131,42 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP Company Name - {testData.companyName} + {data ? data.companyName : ''} Policy Number - {testData.policyNumber} + {data ? data.policyNumber : 0} Total Member - {testData.totalMembers} Person + {data ? data.totalMembers : 0} Person Total Cases - {testData.totalCases} Cases + {data ? data.totalCases : 0} Cases Company Pooled Fund - {fCurrency(testData.myLimit)} + {fCurrency(data ? data.myLimit : 0)} - / {testData.totalLimit} + / {data ? data.totalLimit : 0} - {testData.totalPersen}% + {data ? data.totalPersen : 0}% - + @@ -178,7 +183,7 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP } - label={'Max ' + fCurrency(testData.totalLimit - testData.myLimit)} + label={'Max ' + fCurrency((data ? data.totalLimit : 0) - (data ? data.myLimit : 0))} onChange={handleSubmit(onCheckHandler)} /> @@ -207,6 +212,4 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP maxWidth="xs" /> ); -}; - -export default DialogTopUpLimit; +}