diff --git a/.DS_Store b/.DS_Store index f17439c0..07754d16 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/frontend/client-portal/src/components/TablePagination.tsx b/frontend/client-portal/src/components/BaseTablePagination.tsx similarity index 89% rename from frontend/client-portal/src/components/TablePagination.tsx rename to frontend/client-portal/src/components/BaseTablePagination.tsx index 5d1e8392..3af750e6 100755 --- a/frontend/client-portal/src/components/TablePagination.tsx +++ b/frontend/client-portal/src/components/BaseTablePagination.tsx @@ -10,9 +10,10 @@ export default function BaseTablePagination({ onRowsPerPageChange, }: TablePaginationProps) { return ( - + = Map | [K, V][] + +// Public interface +export interface Actions { + set: (key: K, value: V) => void + setAll: (entries: MapOrEntries) => void + remove: (key: K) => void + reset: Map['clear'] +} + +// We hide some setters from the returned map to disable autocompletion +type Return = [Omit, 'set' | 'clear' | 'delete'>, Actions] + +function useMap( + initialState: MapOrEntries = new Map(), +): Return { + const [map, setMap] = useState(new Map(initialState)) + + const actions: Actions = { + set: useCallback((key, value) => { + setMap(prev => { + const copy = new Map(prev) + copy.set(key, value) + return copy + }) + }, []), + + setAll: useCallback(entries => { + setMap(() => new Map(entries)) + }, []), + + remove: useCallback(key => { + setMap(prev => { + const copy = new Map(prev) + copy.delete(key) + return copy + }) + }, []), + + reset: useCallback(() => { + setMap(() => new Map()) + }, []), + } + + return [map, actions] +} + +export default useMap diff --git a/frontend/client-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx b/frontend/client-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx index 91da6090..f4002fa0 100755 --- a/frontend/client-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx +++ b/frontend/client-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx @@ -16,7 +16,6 @@ import Scrollbar from '../../../components/Scrollbar'; import { NavSectionVertical } from '../../../components/nav-section'; // import navConfig from './NavConfig'; -import NavbarDocs from './NavbarDocs'; import NavbarAccount from './NavbarAccount'; import CollapseButton from './CollapseButton'; @@ -76,7 +75,7 @@ export default function NavbarVertical({ isOpenSidebar, onCloseSidebar }: Props) - PRIME CENTER + Client Portal @@ -92,8 +91,6 @@ export default function NavbarVertical({ isOpenSidebar, onCloseSidebar }: Props) - - {!isCollapse && } ); diff --git a/frontend/client-portal/src/pages/AlarmCenter/Index.tsx b/frontend/client-portal/src/pages/AlarmCenter/Index.tsx index 1909d0bd..8cf7ae83 100755 --- a/frontend/client-portal/src/pages/AlarmCenter/Index.tsx +++ b/frontend/client-portal/src/pages/AlarmCenter/Index.tsx @@ -1,7 +1,7 @@ /* ---------------------------------- react --------------------------------- */ import { useState, SyntheticEvent } from 'react'; /* ---------------------------------- @mui ---------------------------------- */ -import { Box, Tabs, Tab, Container, Grid, Card, Typography } from '@mui/material'; +import { Box, Tabs, Tab, Container, Grid, Card } from '@mui/material'; import { styled } from '@mui/material/styles'; /* ------------------------------- components ------------------------------- */ import Page from '../../components/Page'; @@ -43,11 +43,7 @@ function TabPanel(props: TabPanelProps) { aria-labelledby={`simple-tab-${index}`} {...other} > - {value === index && ( - - {children} - - )} + {value === index && {children}} ); } diff --git a/frontend/client-portal/src/pages/AlarmCenter/List.tsx b/frontend/client-portal/src/pages/AlarmCenter/List.tsx index c0df53b1..67b51419 100755 --- a/frontend/client-portal/src/pages/AlarmCenter/List.tsx +++ b/frontend/client-portal/src/pages/AlarmCenter/List.tsx @@ -1,4 +1,4 @@ -// @mui +/* ---------------------------------- @mui ---------------------------------- */ import { Paper, Table, @@ -9,101 +9,322 @@ import { TableRow, TextField, Stack, + Button, + TableSortLabel, + Box, } from '@mui/material'; -// hooks -import React, { ChangeEvent, useEffect, useRef, useState } from 'react'; -import { useSearchParams } from 'react-router-dom'; -// components -import axios from '../../utils/axios'; -import BasePagination from '../../components/BasePagination'; +import { visuallyHidden } from '@mui/utils'; +/* ---------------------------------- axios --------------------------------- */ +import axios from 'axios'; +/* ---------------------------------- react --------------------------------- */ +import { useEffect, useState } from 'react'; +/* -------------------------------- component ------------------------------- */ +import Iconify from '../../components/Iconify'; +import BaseTablePagination from '../../components/BaseTablePagination'; +/* ---------------------------------- hooks --------------------------------- */ +import useMap from '../../hooks/useMap'; +/* ---------------------------------- theme --------------------------------- */ +import palette from '../../theme/palette'; + +/* ---------------------------------- types --------------------------------- */ + +type PaginationTableProps = { + current_page: number; + from: number; + last_page: number; + links: []; + path: string; + per_page: number; + to: number; + total: number; +}; + +type DataTableProps = { + name: string; + member_id: string; + service: string; + start_date: string; + end_date: string; + status: string; +}; + +/* -------------------------------------------------------------------------- */ + +/* -------------------------- enchanced table head -------------------------- */ + +type Order = 'asc' | 'desc'; + +interface HeadCell { + id: string; + label: string; +} + +const headCells: readonly HeadCell[] = [ + { + id: 'name', + label: 'Name', + }, + { + id: 'member_id', + label: 'Member ID', + }, + { + id: 'service', + label: 'Service', + }, + { + id: 'start_date', + label: 'Start Date', + }, + { + id: 'end_date', + label: 'End Date', + }, + { + id: 'status', + label: 'Status', + }, +]; + +interface EnhancedTableProps { + onRequestSort: (event: React.MouseEvent, property: string) => void; + order: Order; + orderBy: string; +} + +function EnhancedTableHead(props: EnhancedTableProps) { + const { order, orderBy, onRequestSort } = props; + const createSortHandler = (property: string) => (event: React.MouseEvent) => { + onRequestSort(event, property); + }; + + return ( + + + No + {headCells.map((headCell) => ( + + + {headCell.label} + {orderBy === headCell.id ? ( + + {order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + + ))} + + + ); +} + +/* -------------------------------------------------------------------------- */ export default function List() { - const [searchParams, setSearchParams] = useSearchParams(); + const [order, setOrder] = useState('asc'); + const [orderBy, setOrderBy] = useState('name'); + const [customSearchParams, setCustomSearchParams] = useMap(); + const [isLoading, setIsLoading] = useState(true); + const [dataTable, setDataTable] = useState([]); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [paginationTable, setPaginationTable] = useState({ + current_page: 0, + from: 0, + last_page: 0, + links: [], + path: '', + per_page: 0, + to: 0, + total: 0, + }); - function SearchInput(props: any) { - // SEARCH - const searchInput = useRef(null); - const [searchText, setSearchText] = useState(''); - - const handleSearchChange = (event: any) => { - const newSearchText = event.target.value ?? ''; - setSearchText(newSearchText); - }; - - const handleSearchSubmit = (event: any) => { - event.preventDefault(); - props.onSearch(searchText); // Trigger to Parent - }; - - useEffect(() => { - // Trigger First Search - setSearchText(searchParams.get('search') ?? ''); - }, []); - - return ( -
- - - ); - } - - const headStyle = { - fontWeight: 'bold', + /* ------------------------------- handle sort ------------------------------ */ + const handleRequestSort = async (event: React.MouseEvent, property: string) => { + const isAsc = orderBy === property && order === 'asc'; + setOrder(isAsc ? 'desc' : 'asc'); + setOrderBy(property); + const params = Object.fromEntries([ + ...customSearchParams.entries(), + ['order', isAsc ? 'desc' : 'asc'], + ['orderBy', property], + ]); + setIsLoading(true); + await new Promise((resolve) => setTimeout(resolve, 500)); + loadDataTable(params); + setIsLoading(false); }; + /* -------------------------------------------------------------------------- */ + + /* ------------------------------ Search field ------------------------------ */ + const [searchText, setSearchText] = useState(''); + + const handleSearch = (event: any) => { + setSearchText(event.target.value); + }; + + const handleSearchSubmit = async (event: any) => { + event.preventDefault(); + const params = Object.fromEntries([...customSearchParams.entries(), ['search', searchText]]); + setIsLoading(true); + await new Promise((resolve) => setTimeout(resolve, 500)); + loadDataTable(params); + setIsLoading(false); + }; + /* -------------------------------------------------------------------------- */ + + /* --------------------------- Load Data Table API -------------------------- */ + const loadDataTable = async (appliedParams: any | null = null) => { + setIsLoading(true); + + const params = appliedParams + ? appliedParams + : Object.fromEntries([ + ...customSearchParams.entries(), + ['order', order], + ['orderBy', orderBy], + ]); + const response = await axios.get('http://localhost:8001/api/alarm-center', { params: params }); + + setDataTable(response.data.data); + setPaginationTable(response.data.meta); + setRowsPerPage(response.data.meta.per_page); + setIsLoading(false); + }; + /* -------------------------------------------------------------------------- */ + + /* ------------------------ button change pagination ------------------------ */ + const onPageChangeHandle = async (event: unknown, newPage: number) => { + const params = Object.fromEntries([...customSearchParams.entries(), ['page', newPage + 1]]); + setPage(newPage); + setIsLoading(true); + await new Promise((resolve) => setTimeout(resolve, 500)); + loadDataTable(params); + setIsLoading(false); + setCustomSearchParams.set('page', newPage + 1); + }; + /* -------------------------------------------------------------------------- */ + + /* ----------------------- row page per limit on click ---------------------- */ + const onRowsPerPageChangeHandle = async (event: React.ChangeEvent) => { + setPage(0); + const params = Object.fromEntries([ + ...customSearchParams.entries(), + ['page', 0], + ['per_page', parseInt(event.target.value, 10)], + ]); + setRowsPerPage(parseInt(event.target.value, 10)); + setIsLoading(true); + await new Promise((resolve) => setTimeout(resolve, 500)); + loadDataTable(params); + setIsLoading(false); + setCustomSearchParams.set('per_page', parseInt(event.target.value, 10)); + }; + /* -------------------------------------------------------------------------- */ + + useEffect(() => { + loadDataTable(); + }, []); return ( {/* Search */} - +
+ + {/* The Main Table */} - - - - No - - - Name - - - Member ID - - - Service - - - Start Date - - - End Date - - - Status - - - + - - - No Data - - + {isLoading ? ( + + + Loading . . . + + + ) : dataTable.length >= 1 ? ( + dataTable.map((row: DataTableProps, index) => ( + + {paginationTable.from + index++} + {row.name} + {row.member_id} + {row.service} + {row.start_date} + {row.end_date} + + {row.status.toLowerCase() === 'done' ? ( + + ) : ( + + )} + + + )) + ) : ( + + + No Data Found + + + )}
{/* Pagination */} - {/* */} +
); } diff --git a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx index d14e79e9..d9da4786 100755 --- a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx @@ -7,6 +7,7 @@ import Page from '../../components/Page'; // theme import CardNotification from '../../sections/dashboard/CardNotification'; import CardBalance from '../../sections/dashboard/CardBalance'; +import TableList from '../../sections/dashboard/TableList'; import List from './List'; // ---------------------------------------------------------------------- @@ -55,7 +56,8 @@ export default function Dashboard() { - + + {/* */} diff --git a/frontend/client-portal/src/pages/Dashboard/List.tsx b/frontend/client-portal/src/pages/Dashboard/List.tsx index c45333b3..dabb387b 100644 --- a/frontend/client-portal/src/pages/Dashboard/List.tsx +++ b/frontend/client-portal/src/pages/Dashboard/List.tsx @@ -47,7 +47,15 @@ import BasePagination from '../../components/BasePagination'; import { Member } from '../../@types/member'; import Iconify from '../../components/Iconify'; -const options = ['All', 'Option 2']; +const options = [ + { label: 'The Shawshank Redemption', value: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, +]; export default function List() { const navigate = useNavigate(); @@ -77,7 +85,8 @@ export default function List() { useEffect(() => { // Trigger First Search setSearchText(searchParams.get('search') ?? ''); - }, [searchParams]); + console.log(value, inputValue); + }, [searchParams, value, inputValue]); return (
diff --git a/frontend/client-portal/src/pages/Dashboard/ListTest.tsx b/frontend/client-portal/src/pages/Dashboard/ListTest.tsx deleted file mode 100644 index 1917f0d1..00000000 --- a/frontend/client-portal/src/pages/Dashboard/ListTest.tsx +++ /dev/null @@ -1,401 +0,0 @@ -// @mui -import { - Box, - Button, - Card, - Collapse, - IconButton, - InputLabel, - MenuItem, - OutlinedInput, - Paper, - Select, - SelectChangeEvent, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - TextField, - Typography, - Badge, - Tab, - Tabs, - CardHeader, - Stack, - Menu, - ButtonGroup, - Pagination, - Grid, -} 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, Component, useEffect, useRef, useState } from 'react'; -import useSettings from '../../hooks/useSettings'; -import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; -// components -import axios from '../../utils/axios'; -import { LaravelPaginatedData } from '../../@types/paginated-data'; -import { Icd } from '../../@types/diagnosis'; -import BasePagination from '../../components/BasePagination'; -import { Member } from '../../@types/member'; - -export default function ListTest() { - const navigate = useNavigate(); - const { themeStretch } = useSettings(); - const { corporate_id } = useParams(); - const [searchParams, setSearchParams] = useSearchParams(); - const [importResult, setImportResult] = useState(null); - - function SearchInput(props: any) { - // SEARCH - const searchInput = useRef(null); - const [searchText, setSearchText] = useState(''); - - const handleSearchChange = (event: any) => { - const newSearchText = event.target.value ?? ''; - setSearchText(newSearchText); - }; - - const handleSearchSubmit = (event: any) => { - event.preventDefault(); - props.onSearch(searchText); // Trigger to Parent - }; - - useEffect(() => { - // Trigger First Search - setSearchText(searchParams.get('search') ?? ''); - }, [searchParams]); - - return ( - - - - ); - } - - function ImportForm(props: any) { - // IMPORT - // Create Button Menu - const [anchorEl, setAnchorEl] = React.useState(null); - const createMenu = Boolean(anchorEl); - const importForm = useRef(null); - const [currentImportFileName, setCurrentImportFileName] = useState(null); - - const handleClick = (event: React.MouseEvent) => { - 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(`master/formularium/import`, 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) => { - alert( - 'Looks like something went wrong. Please check your data and try again. ' + - response.message - ); - }); - } else { - alert('No File Selected'); - } - }; - - return ( -
- - {!currentImportFileName && ( - - - {/*

kjasndkjandskjasndkjansdkjansd

*/} - - - { - navigate('/master/formularium/create'); - }} - > - Create - - Import - Download Template - -
- )} - - {currentImportFileName && ( - - - - - - - - - )} - {importResult && ( - - - Last Import Result Report :{' '} - - {importResult.result_file?.name ?? '-'} - - - - )} -
- ); - } - - // Called on every row to map the data to the columns - function createData(member: Member): Member { - return { - ...member, - }; - } - - // Generate the every row of the table - function Row(props: { row: ReturnType }) { - const { row } = props; - const [open, setOpen] = React.useState(true); - - return ( - - *': { borderBottom: 'unset' } }}> - - setOpen(!open)}> - {open ? : } - - - {row.member_id} - {row.payor_id} - {row.name} - {row.nik} - {row.nric} - - - - - {/* */} - - {/* COLLAPSIBLE ROW */} - - - - - - - - - - - - - ); - } - - // Dummy Default Data - const [dataTableIsLoading, setDataTableLoading] = useState(true); - const [dataTableLastRequest, setDataTableLastRequest] = useState(0); - const [dataTableResponseState, setDataTableResponseState] = useState('idle'); - const [dataTableData, setDataTableData] = useState({ - 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, - }); - const [dataTablePage, setDataTablePage] = useState(5); - - const loadDataTableData = async (appliedFilter: any | null = null) => { - setDataTableLoading(true); - const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); - const response = await axios.get('/members', { params: filter }); - - setDataTableData(response.data.members); - setDataTableLoading(false); - }; - - const headStyle = { - fontWeight: 'bold', - }; - - const applyFilter = async (searchFilter: string) => { - await loadDataTableData({ search: searchFilter }); - setSearchParams({ search: searchFilter }); - }; - - const handlePageChange = (event: ChangeEvent, value: number) => { - const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); - loadDataTableData(filter); - setSearchParams(filter); - }; - - useEffect(() => { - loadDataTableData(); - }, []); - - return ( - - - - - {/* The Main Table */} - - - - - - Detail - - - MemberID - - - PayorID - - - Name - - - NIK - - - PlanID - - - Status - - {/* Action */} - - - {dataTableIsLoading ? ( - - - - Loading - - - - ) : dataTableData.data.length == 0 ? ( - - - - No Data - - - - ) : ( - - {dataTableData.data.map((row) => ( - - ))} - - )} -
-
- - -
-
- ); -} diff --git a/frontend/client-portal/src/sections/dashboard/TableList.tsx b/frontend/client-portal/src/sections/dashboard/TableList.tsx new file mode 100755 index 00000000..4b06149f --- /dev/null +++ b/frontend/client-portal/src/sections/dashboard/TableList.tsx @@ -0,0 +1,379 @@ +/* ---------------------------------- @mui ---------------------------------- */ +import { + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Button, + TableSortLabel, + Box, + IconButton, + Card, + Grid, + Autocomplete, +} from '@mui/material'; +import { visuallyHidden } from '@mui/utils'; +/* ---------------------------------- axios --------------------------------- */ +import axios from 'axios'; +/* ---------------------------------- react --------------------------------- */ +import { useEffect, useState } from 'react'; +/* -------------------------------- component ------------------------------- */ +import Iconify from '../../components/Iconify'; +import BaseTablePagination from '../../components/BaseTablePagination'; +/* ---------------------------------- hooks --------------------------------- */ +import useMap from '../../hooks/useMap'; +/* ---------------------------------- theme --------------------------------- */ +import palette from '../../theme/palette'; +import { useSearchParams } from 'react-router-dom'; + +/* ---------------------------------- types --------------------------------- */ + +type PaginationTableProps = { + current_page: number; + from: number; + last_page: number; + links: []; + path: string; + per_page: number; + to: number; + total: number; +}; + +type DataTableProps = { + name: string; + member_id: string; + service: string; + start_date: string; + end_date: string; + status: string; +}; + +type Order = 'asc' | 'desc'; + +interface HeadCell { + id: string; + align: string; + label: string; + isSort: boolean; +} + +interface EnhancedTableProps { + onRequestSort: (event: React.MouseEvent, property: string) => void; + order: Order; + orderBy: string; +} + +/* -------------------------------------------------------------------------- */ + +/* -------------------------- enchanced table head -------------------------- */ + +const headCells: readonly HeadCell[] = [ + { + id: 'member_id', + align: 'left', + label: 'Member ID', + isSort: false, + }, + { + id: 'name', + align: 'center', + label: 'Name', + isSort: false, + }, + { + id: 'division', + align: 'center', + label: 'Divisi', + isSort: false, + }, + { + id: 'limit', + align: 'center', + label: 'Limit', + isSort: false, + }, + { + id: 'status', + align: 'center', + label: 'Status', + isSort: false, + }, +]; + +function EnhancedTableHead({ order, orderBy, onRequestSort }: EnhancedTableProps) { + const createSortHandler = (property: string) => (event: React.MouseEvent) => { + onRequestSort(event, property); + }; + + return ( + + + {headCells.map((headCell) => ( + + {headCell.isSort ? ( + + {headCell.label} + {orderBy === headCell.id ? ( + + {order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + ) : ( + headCell.label + )} + + ))} + {''} + + + ); +} + +/* -------------------------------------------------------------------------- */ + +export default function TableList() { + const [order, setOrder] = useState('asc'); + const [orderBy, setOrderBy] = useState('name'); + const [customSearchParams, setCustomSearchParams] = useSearchParams(); + const [isLoading, setIsLoading] = useState(true); + const [dataTable, setDataTable] = useState([]); + const [dataDivision, setDataDivision] = useState([]); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [appliedParams, setAppliedParams] = useState({}); + const [paginationTable, setPaginationTable] = useState({ + current_page: 0, + from: 0, + last_page: 0, + links: [], + path: '', + per_page: 0, + to: 0, + total: 0, + }); + + /* ------------------------------- handle sort ------------------------------ */ + const handleRequestSort = async (event: React.MouseEvent, property: string) => { + const isAsc = orderBy === property && order === 'asc'; + setOrder(isAsc ? 'desc' : 'asc'); + setOrderBy(property); + const params = Object.fromEntries([ + ...customSearchParams.entries(), + ['order', isAsc ? 'desc' : 'asc'], + ['orderBy', property], + ]); + setAppliedParams(params); + }; + /* -------------------------------------------------------------------------- */ + + /* ------------------------------ Search field ------------------------------ */ + const [searchText, setSearchText] = useState(''); + + const handleSearch = (event: React.ChangeEvent) => { + setSearchText(event.target.value); + }; + + const handleSearchSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + setIsLoading(true); + const params = Object.fromEntries([ + ...customSearchParams.entries(), + ['page', 0], + ['search', searchText], + ]); + await new Promise((resolve) => setTimeout(resolve, 500)); + setAppliedParams(params); + setIsLoading(false); + }; + /* -------------------------------------------------------------------------- */ + + /* ---------------------------- table pagination ---------------------------- */ + + /* ------------------------ button change pagination ------------------------ */ + const onPageChangeHandle = async ( + event: React.MouseEvent | null, + newPage: number + ) => { + setIsLoading(true); + const params = Object.fromEntries([ + ...customSearchParams.entries(), + ['page', newPage + 1], + ['order', order], + ['orderBy', orderBy], + ]); + setPage(newPage); + await new Promise((resolve) => setTimeout(resolve, 500)); + setAppliedParams(params); + setIsLoading(false); + // setCustomSearchParams.set('page', newPage + 1); + }; + /* -------------------------------------------------------------------------- */ + + /* --------------------------- row page per limit --------------------------- */ + const onRowsPerPageChangeHandle = async (event: React.ChangeEvent) => { + setIsLoading(true); + setPage(0); + const params = Object.fromEntries([ + ...customSearchParams.entries(), + ['page', 0], + ['order', order], + ['orderBy', orderBy], + ['per_page', parseInt(event.target.value, 10)], + ]); + setRowsPerPage(parseInt(event.target.value, 10)); + await new Promise((resolve) => setTimeout(resolve, 500)); + setAppliedParams(params); + setIsLoading(false); + // setCustomSearchParams.set('per_page', parseInt(event.target.value, 10)); + }; + /* -------------------------------------------------------------------------- */ + + /* -------------------------------------------------------------------------- */ + + useEffect(() => { + (async () => { + setIsLoading(true); + + const params = + Object.keys(appliedParams).length !== 0 + ? appliedParams + : Object.fromEntries([ + ...customSearchParams.entries(), + // ['order', order], + // ['orderBy', orderBy], + ]); + + const response = await axios.get('http://localhost:8001/api/dashboard', { + params: params, + }); + + const division = await axios.get('http://localhost:8001/api/division'); + setDataDivision(division.data); + + setDataTable(response.data.data); + setPaginationTable(response.data.meta); + setRowsPerPage(response.data.meta.per_page); + setIsLoading(false); + })(); + }, [appliedParams, customSearchParams, order, orderBy]); + + return ( + + + {/* Field 1 */} + +
+ + +
+ {/* End Field 1 */} + {/* Field 2 */} + + + + + + {isLoading ? ( + + + Loading . . . + + + ) : dataTable.length >= 1 ? ( + dataTable.map((row: DataTableProps, index) => ( + + {row.member_id} + {row.name} + {row.service} + {row.start_date} + + {row.status.toLowerCase() === 'active' ? ( + + ) : ( + + )} + + + + + + + + )) + ) : ( + + + No Data Found + + + )} + +
+
+ + {/* Pagination */} + +
+ {/* End Field 2 */} +
+
+ ); +} diff --git a/frontend/client-portal/src/theme/overrides/Table.ts b/frontend/client-portal/src/theme/overrides/Table.ts index 0b8a6f9d..7e02c3ea 100755 --- a/frontend/client-portal/src/theme/overrides/Table.ts +++ b/frontend/client-portal/src/theme/overrides/Table.ts @@ -4,9 +4,19 @@ import { Theme } from '@mui/material/styles'; export default function Table(theme: Theme) { return { + MuiTableHead: { + styleOverrides: { + root: { + '.MuiTableRow-root': { + borderBottom: 'none', + }, + }, + }, + }, MuiTableRow: { styleOverrides: { root: { + borderBottom: '1px solid rgba(241, 243, 244, 1)', '&.Mui-selected': { backgroundColor: theme.palette.action.selected, '&:hover': { diff --git a/frontend/dashboard/src/pages/Corporates/Index.tsx b/frontend/dashboard/src/pages/Corporates/Index.tsx index 3dcdb66e..5c0ea756 100755 --- a/frontend/dashboard/src/pages/Corporates/Index.tsx +++ b/frontend/dashboard/src/pages/Corporates/Index.tsx @@ -1,5 +1,30 @@ // @mui -import { Box, Button, Card, Collapse, Container, FormControl, Grid, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Stack } from '@mui/material'; +import { + Box, + Button, + Card, + Collapse, + Container, + FormControl, + Grid, + IconButton, + InputLabel, + MenuItem, + OutlinedInput, + Paper, + Select, + SelectChangeEvent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, + Badge, + Stack, +} from '@mui/material'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; import PublishIcon from '@mui/icons-material/Publish'; @@ -10,7 +35,7 @@ import useSettings from '../../hooks/useSettings'; import Page from '../../components/Page'; import axios from '../../utils/axios'; import useAuth from '../../hooks/useAuth'; -import { Link , NavLink as RouterLink, useSearchParams } from 'react-router-dom'; +import { Link, NavLink as RouterLink, useSearchParams } from 'react-router-dom'; import React, { ChangeEvent, useEffect, useRef, useState } from 'react'; import { Theme, useTheme } from '@mui/material/styles'; import { Corporate } from '../../@types/corporates'; @@ -25,10 +50,10 @@ export default function Corporates() { const [searchParams, setSearchParams] = useSearchParams(); // Called on every row to map the data to the columns - function createData( corporate: Corporate ): Corporate { + function createData(corporate: Corporate): Corporate { return { ...corporate, - } + }; } // Dummy Default Data @@ -36,19 +61,19 @@ export default function Corporates() { const [dataTableData, setDataTableData] = React.useState({ current_page: 1, data: [], - path: "", - first_page_url: "", + path: '', + first_page_url: '', last_page: 1, - last_page_url: "", - next_page_url: "", - prev_page_url: "", + last_page_url: '', + next_page_url: '', + prev_page_url: '', per_page: 10, from: 0, to: 0, - total: 0 + total: 0, }); - const loadDataTableData = async (appliedFilter : any | null = null) => { + const loadDataTableData = async (appliedFilter: any | null = null) => { setDataTableLoading(true); const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); const response = await axios.get('/corporates', { params: filter }); @@ -56,31 +81,28 @@ export default function Corporates() { setDataTableLoading(false); setDataTableData(response.data); - } + }; - const applyFilter = async (searchFilter: any) => { - await loadDataTableData({ "search" : searchFilter }); - setSearchParams({ "search" : searchFilter }); - } + await loadDataTableData({ search: searchFilter }); + setSearchParams({ search: searchFilter }); + }; - const handlePageChange = (event : ChangeEvent, value: number) => { - const filter = Object.fromEntries([...searchParams.entries(), ["page", value]]); + const handlePageChange = (event: ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); loadDataTableData(filter); setSearchParams(filter); - } - + }; useEffect(() => { loadDataTableData(); - }, []) - + }, []); const headStyle = { fontWeight: 'bold', }; - // FILTER SELECT + // FILTER SELECT const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; const MenuProps = { @@ -92,13 +114,7 @@ export default function Corporates() { }, }; - const names = [ - 'PLAN001', - 'PLAN002', - 'PLAN003', - 'PLAN004', - 'PLAN005', - ]; + const names = ['PLAN001', 'PLAN002', 'PLAN003', 'PLAN004', 'PLAN005']; function getStyles(name: string, personName: string[], theme: Theme) { return { fontWeight: @@ -107,7 +123,7 @@ export default function Corporates() { : theme.typography.fontWeightMedium, }; } - + const theme = useTheme(); const [planIdFilter, setPlanIdFilter] = React.useState([]); @@ -117,7 +133,7 @@ export default function Corporates() { } = event; setPlanIdFilter( // On autofill we get a stringified value. - typeof value === 'string' ? value.split(',') : value, + typeof value === 'string' ? value.split(',') : value ); }; @@ -128,37 +144,45 @@ export default function Corporates() { } = event; setStatusFilter( // On autofill we get a stringified value. - typeof value === 'string' ? value.split(',') : value, + typeof value === 'string' ? value.split(',') : value ); }; // END FILTER SELECT - + // Component Search Input function SearchInput(props: any) { - // SEARCH + // SEARCH const searchInput = useRef(null); - const [searchText, setSearchText] = useState(""); + const [searchText, setSearchText] = useState(''); const handleSearchChange = (event: any) => { - const newSearchText = event.target.value ?? '' + const newSearchText = event.target.value ?? ''; setSearchText(newSearchText); - } + }; const handleSubmit = (event: any) => { event.preventDefault(); props.onSearch(searchText); // Trigger to Parent - } + }; - useEffect(() => { + useEffect(() => { // console.log('Search Input: useEffect') setSearchText(searchParams.get('search') ?? ''); - }, [searchParams]) + }, [searchParams]); return (
- - + + {/* @@ -214,17 +238,21 @@ export default function Corporates() { */} {/* */} - {/* + {/* */} - - + {/* */} - +
); } @@ -236,50 +264,80 @@ export default function Corporates() { const [open, setOpen] = React.useState(false); const handleActivate = (model: any, status: string) => { - axios.put(`/corporates/${row.id}/activation`, { - // service_code: service.service_code, - active: status == 'active' - }) - .then((res) => { - setDataTableData({ - ...dataTableData, - data: dataTableData.data.map((model) => { - let updatedModel = model - if (row.id == model.id) { - updatedModel.active = res.data.corporate.active - } - return updatedModel - }) + axios + .put(`/corporates/${row.id}/activation`, { + // service_code: service.service_code, + active: status == 'active', }) - }) - .catch((error) => { - // console.log('asdasd', error.response.data.message) - enqueueSnackbar(error.response.data.message ?? error.message ?? 'Failed Processing Request', { variant: 'error' }); - }) - } + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.map((model) => { + let updatedModel = model; + if (row.id == model.id) { + updatedModel.active = res.data.corporate.active; + } + return updatedModel; + }), + }); + }) + .catch((error) => { + // console.log('asdasd', error.response.data.message) + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; return ( *': { borderBottom: 'unset' } }}> - setOpen(!open)} - > + setOpen(!open)}> {open ? : } {row.code} {row.name} - {( row.active == 1 && )} - {( row.active != 1 && )} + {row.active == 1 && ( + + )} + {row.active != 1 && ( + + )} - - + + + + + + @@ -335,7 +393,10 @@ export default function Corporates() { Minimal Deposit - : {row.current_policy ? fCurrency(row.current_policy?.minimal_deposit_net) : '-'} + :{' '} + {row.current_policy + ? fCurrency(row.current_policy?.minimal_deposit_net) + : '-'} @@ -365,7 +426,6 @@ export default function Corporates() { - Sub Corporate @@ -380,7 +440,6 @@ export default function Corporates() { -
@@ -403,47 +462,55 @@ export default function Corporates() { ]} /> - - + + - {/* The Main Table */} - - - - - - Code - Name - Status - Action - - - {dataTableIsLoading ? - ( + {/* The Main Table */} + +
+ + + + + Code + + + Name + + + Status + + + Action + + + + {dataTableIsLoading ? ( - Loading + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + ) : ( - dataTableData.data.length == 0 ? - ( - - - No Data - - - ) : ( - - {dataTableData.data.map(row => ( - - ))} - - ) - )} -
-
- + + {dataTableData.data.map((row) => ( + + ))} + + )} + + +
diff --git a/storage/.DS_Store b/storage/.DS_Store new file mode 100644 index 00000000..5168bb0f Binary files /dev/null and b/storage/.DS_Store differ