client portal update

This commit is contained in:
Muhammad Fajar
2024-01-02 23:29:05 +07:00
parent 4e988200b2
commit 2839d537bd
8 changed files with 155 additions and 142 deletions

View File

@@ -107,19 +107,19 @@ export type TableListProps<DataType> = {
handleStatusChange: (event: SelectChangeEvent) => void; handleStatusChange: (event: SelectChangeEvent) => void;
}; };
}; };
filterStartDate: { filterStartDate?: {
useFilter: boolean; useFilter: boolean;
startDate: string; startDate: string;
setStartDate: Dispatch<SetStateAction<string>>; setStartDate: Dispatch<SetStateAction<string>>;
handleStartDateChange: (event: FormEvent<HTMLFormElement>) => void; handleStartDateChange: (event: FormEvent<HTMLFormElement>) => void;
}; };
filterEndDate: { filterEndDate?: {
useFilter: boolean; useFilter: boolean;
endDate: string; endDate: string;
setEndDate: Dispatch<SetStateAction<string>>; setEndDate: Dispatch<SetStateAction<string>>;
handleEndDateChange: (event: FormEvent<HTMLFormElement>) => void; handleEndDateChange: (event: FormEvent<HTMLFormElement>) => void;
}; };
exportReport: { exportReport?: {
useExport: boolean; useExport: boolean;
startDate: string; startDate: string;
endDate: string; endDate: string;

View File

@@ -1,31 +1,30 @@
import { useState } from 'react'; import { useState } from 'react';
// @mui // @mui
import { alpha } from '@mui/material/styles'; import { alpha } from '@mui/material/styles';
import { Box, Divider, Typography, Stack, MenuItem, Avatar } from '@mui/material'; import { Box, Divider, Typography, MenuItem, Avatar } from '@mui/material';
// components // components
import MenuPopover from '../../../components/MenuPopover'; import MenuPopover from '../../../components/MenuPopover';
import { IconButtonAnimate } from '../../../components/animate'; import { IconButtonAnimate } from '../../../components/animate';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import useAuth from '../../../hooks/useAuth'; import useAuth from '../../../hooks/useAuth';
import useLocalStorage from '../../../hooks/useLocalStorage';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
const MENU_OPTIONS = [ // const MENU_OPTIONS = [
{ // {
label: 'Home', // label: 'Home',
linkTo: '/', // linkTo: '/',
}, // },
{ // {
label: 'Profile', // label: 'Profile',
linkTo: '/', // linkTo: '/',
}, // },
{ // {
label: 'Settings', // label: 'Settings',
linkTo: '/', // linkTo: '/',
}, // },
]; // ];
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -34,6 +33,8 @@ export default function AccountPopover() {
const navigate = useNavigate(); const navigate = useNavigate();
const { logout, user } = useAuth(); const { logout, user } = useAuth();
console.log(user);
const handleOpen = (event: React.MouseEvent<HTMLElement>) => { const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
setOpen(event.currentTarget); setOpen(event.currentTarget);
}; };
@@ -67,10 +68,7 @@ export default function AccountPopover() {
}), }),
}} }}
> >
{user && user.user.avatar_url && (<Avatar <Avatar src={user?.user.avatar_url || ''} alt={user?.user.full_name || ''} />
src={user ? user.user.avatar_url : ''}
alt={user ? user.user.full_name : ''}
/>)}
</IconButtonAnimate> </IconButtonAnimate>
<MenuPopover <MenuPopover
@@ -89,10 +87,10 @@ export default function AccountPopover() {
> >
<Box sx={{ my: 1.5, px: 2.5 }}> <Box sx={{ my: 1.5, px: 2.5 }}>
<Typography variant="subtitle2" noWrap> <Typography variant="subtitle2" noWrap>
{ user ? user.user.full_name ?? 'Hi, ' : 'Hi, '} {`Hi, ${user?.user?.full_name || 'User'}`}
</Typography> </Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }} noWrap> <Typography variant="body2" sx={{ color: 'text.secondary' }} noWrap>
{ user ? user.user.email : 'Please Wait'} {user ? user.user.email : 'Please Wait'}
</Typography> </Typography>
</Box> </Box>

View File

@@ -13,11 +13,7 @@ import Logo from '../../../components/Logo';
import Iconify from '../../../components/Iconify'; import Iconify from '../../../components/Iconify';
import { IconButtonAnimate } from '../../../components/animate'; import { IconButtonAnimate } from '../../../components/animate';
// //
import Searchbar from './Searchbar';
import AccountPopover from './AccountPopover'; import AccountPopover from './AccountPopover';
import LanguagePopover from './LanguagePopover';
import ContactsPopover from './ContactsPopover';
import NotificationsPopover from './NotificationsPopover';
import CorporatePopover from './CorporatePopover'; import CorporatePopover from './CorporatePopover';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -46,11 +42,11 @@ const RootStyle = styled(AppBar, {
width: `calc(100% - ${NAVBAR.DASHBOARD_COLLAPSE_WIDTH}px)`, width: `calc(100% - ${NAVBAR.DASHBOARD_COLLAPSE_WIDTH}px)`,
}), }),
...(isOffset && { ...(isOffset && {
height: HEADER.DASHBOARD_DESKTOP_OFFSET_HEIGHT, height: HEADER.DASHBOARD_DESKTOP_HEIGHT,
}), }),
...(verticalLayout && { ...(verticalLayout && {
width: '100%', width: '100%',
height: HEADER.DASHBOARD_DESKTOP_OFFSET_HEIGHT, height: HEADER.DASHBOARD_DESKTOP_HEIGHT,
backgroundColor: theme.palette.background.default, backgroundColor: theme.palette.background.default,
}), }),
}, },
@@ -89,15 +85,15 @@ export default function DashboardHeader({
</IconButtonAnimate> </IconButtonAnimate>
)} )}
<Searchbar /> {/* <Searchbar /> */}
<Box sx={{ flexGrow: 1 }} /> <Box sx={{ flexGrow: 1 }} />
<Stack direction="row" alignItems="center" spacing={{ xs: 0.5, sm: 1.5 }}> <Stack direction="row" alignItems="center" spacing={{ xs: 0.5, sm: 1.5 }}>
<CorporatePopover /> <CorporatePopover />
<Divider orientation="vertical" flexItem /> <Divider orientation="vertical" flexItem />
<LanguagePopover /> {/* <LanguagePopover /> */}
<NotificationsPopover /> {/* <NotificationsPopover /> */}
<ContactsPopover /> {/* <ContactsPopover /> */}
<AccountPopover /> <AccountPopover />
</Stack> </Stack>
</Toolbar> </Toolbar>

View File

@@ -23,10 +23,8 @@ type Props = {
}; };
export default function NavbarAccount({ isCollapse }: Props) { export default function NavbarAccount({ isCollapse }: Props) {
const { user } = useAuth(); const { user } = useAuth();
// console.log('current user is ', user)
return ( return (
<Link underline="none" color="inherit"> <Link underline="none" color="inherit">
<RootStyle <RootStyle
@@ -36,10 +34,9 @@ export default function NavbarAccount({ isCollapse }: Props) {
}), }),
}} }}
> >
{user && user.user.avatar_url && (<Avatar {user && user.user.avatar_url && (
src={user ? user.user.avatar_url : ''} <Avatar src={user?.user.avatar_url || ''} alt={user?.user.full_name || ''} />
alt={user ? user.user.full_name : ''} )}
/>)}
<Box <Box
sx={{ sx={{
@@ -55,10 +52,10 @@ export default function NavbarAccount({ isCollapse }: Props) {
}} }}
> >
<Typography variant="subtitle2" noWrap> <Typography variant="subtitle2" noWrap>
{ user ? user.user.full_name ?? 'Hi, ' : 'Hi, '} {`Hi, ${user?.user?.full_name || 'User'}`}
</Typography> </Typography>
<Typography variant="body2" noWrap sx={{ color: 'text.secondary', fontSize: '11px' }}> <Typography variant="body2" noWrap sx={{ color: 'text.secondary', fontSize: '11px' }}>
{ user ? user.user.email : 'Please Wait'} {user ? user.user.email : 'Please Wait'}
</Typography> </Typography>
</Box> </Box>
</RootStyle> </RootStyle>

View File

@@ -24,13 +24,7 @@ import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useLocation, useSearchParams } from 'react-router-dom'; import { useLocation, useSearchParams } from 'react-router-dom';
import palette from '../../theme/palette'; import palette from '../../theme/palette';
import { fSplit } from '../../utils/formatNumber'; import { fSplit } from '../../utils/formatNumber';
import { closeSnackbar, enqueueSnackbar } from 'notistack';
// const itemList = [
// { info: 'Mohon lengkapi dokumen Alison Born', date: 'Selasa, 13 Februari 23', time: '09:43 WIB' },
// { info: 'Mohon lengkapi dokumen Alison Born', date: 'Selasa, 13 Februari 23', time: '09:43 WIB' },
// { info: 'Mohon lengkapi dokumen Alison Born', date: 'Selasa, 13 Februari 23', time: '09:43 WIB' },
// { info: 'Mohon lengkapi dokumen Alison Born', date: 'Selasa, 13 Februari 23', time: '09:43 WIB' },
// ];
/* ------------------------------ default data ------------------------------ */ /* ------------------------------ default data ------------------------------ */
type DataMember = { type DataMember = {
@@ -175,7 +169,7 @@ export default function Index() {
}; };
const searchs = { const searchs = {
// useSearchs: true, useSearchs: false,
searchText: searchText, searchText: searchText,
setSearchText: setSearchText, setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit, handleSearchSubmit: handleSearchSubmit,
@@ -257,6 +251,11 @@ export default function Index() {
useEffect(() => { useEffect(() => {
(async () => { (async () => {
setIsLoading(true); setIsLoading(true);
const loadNotif = enqueueSnackbar('Data sedang di load, harap tunggu!', {
variant: 'info',
autoHideDuration: null,
anchorOrigin: { horizontal: 'right', vertical: 'bottom' },
});
const parameters = const parameters =
Object.keys(appliedParams).length !== 0 Object.keys(appliedParams).length !== 0
@@ -343,7 +342,12 @@ export default function Index() {
setPage(currentPage); setPage(currentPage);
} }
closeSnackbar(loadNotif);
setIsLoading(false); setIsLoading(false);
enqueueSnackbar('Data sudah di load!', {
variant: 'info',
anchorOrigin: { horizontal: 'right', vertical: 'bottom' },
});
})(); })();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]); }, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);

View File

@@ -20,20 +20,20 @@ import DialogClaimSubmitMember from './DialogClaimSubmitMember';
/* ---------------------------------- types --------------------------------- */ /* ---------------------------------- types --------------------------------- */
type DataMember = { // type DataMember = {
id: number; // id: number;
fullName: string; // fullName: string;
memberId: string; // memberId: string;
limit: { // limit: {
current: number; // current: number;
total: number; // total: number;
percentage: number; // percentage: number;
}; // };
avatar?: { // avatar?: {
url?: string; // url?: string;
title?: string; // title?: string;
}; // };
}; // };
type CardPolicyProps = { type CardPolicyProps = {
data: { data: {
@@ -99,7 +99,7 @@ export default function CardPolicy(props: CardPolicyProps) {
const [dialogTitle, setDialogTitle] = useState(''); const [dialogTitle, setDialogTitle] = useState('');
const [isDialog, setIsDialog] = useState(''); const [isDialog, setIsDialog] = useState('');
const { limit, topUpLimit, members } = props.data || {}; const { limit, topUpLimit } = props.data || {};
if (!limit || !topUpLimit) { if (!limit || !topUpLimit) {
return null; return null;
} }
@@ -178,14 +178,14 @@ export default function CardPolicy(props: CardPolicyProps) {
> >
Submit Claim Submit Claim
</Button> </Button>
<Button {/* <Button
variant="contained" variant="contained"
startIcon={<Iconify icon="heroicons-solid:cash" />} startIcon={<Iconify icon="heroicons-solid:cash" />}
fullWidth={true} fullWidth={true}
onClick={() => clickHandler('topUpLimit')} onClick={() => clickHandler('topUpLimit')}
> >
Top Up Top Up
</Button> </Button> */}
</Stack> </Stack>
</> </>

View File

@@ -7,11 +7,12 @@ import {
LinearProgress, LinearProgress,
linearProgressClasses, linearProgressClasses,
Stack, Stack,
Skeleton,
} from '@mui/material'; } from '@mui/material';
// components // components
import Iconify from '../../components/Iconify'; import Iconify from '../../components/Iconify';
// React // React
import { useState } from 'react'; import { Fragment, useState } from 'react';
// utils // utils
import { fCurrency, fSplit } from '../../utils/formatNumber'; import { fCurrency, fSplit } from '../../utils/formatNumber';
/* -------------------------------- sections -------------------------------- */ /* -------------------------------- sections -------------------------------- */
@@ -20,23 +21,23 @@ import DialogClaimSubmitMember from './DialogClaimSubmitMember';
/* ---------------------------------- types --------------------------------- */ /* ---------------------------------- types --------------------------------- */
type DataMember = { // type DataMember = {
id: number; // id: number;
fullName: string; // fullName: string;
memberId: string; // memberId: string;
limit: { // limit: {
current: number; // current: number;
total: number; // total: number;
percentage: number; // percentage: number;
}; // };
avatar?: { // avatar?: {
url?: string; // url?: string;
title?: string; // title?: string;
}; // };
}; // };
type CardPolicyProps = { type CardPolicyProps = {
data: { data?: {
limit: { limit: {
myLimit: { myLimit: {
balance: number; balance: number;
@@ -61,10 +62,6 @@ type CardPolicyProps = {
}; };
maxTopUp: number; maxTopUp: number;
}; };
members: {
memberId: string;
memberFullName: string;
};
}; };
}; };
@@ -99,10 +96,7 @@ export default function CardPolicy(props: CardPolicyProps) {
const [dialogTitle, setDialogTitle] = useState(''); const [dialogTitle, setDialogTitle] = useState('');
const [isDialog, setIsDialog] = useState(''); const [isDialog, setIsDialog] = useState('');
const { limit, topUpLimit, members } = props.data || {}; const { limit, topUpLimit } = props.data || {};
if (!limit || !topUpLimit) {
return null;
}
const clickHandler = (isDialog: string) => { const clickHandler = (isDialog: string) => {
switch (isDialog) { switch (isDialog) {
@@ -124,70 +118,108 @@ export default function CardPolicy(props: CardPolicyProps) {
return ( return (
<RootBalanceStyle> <RootBalanceStyle>
<> <Fragment>
<Stack direction="row" justifyContent="space-between" sx={{ mb: 1 }}> <Stack direction="row" justifyContent="space-between" sx={{ mb: 1 }} spacing={2}>
<div> <Stack>
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}> <Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
Total Limit {limit ? (
'Total Limit'
) : (
<Skeleton sx={{ bgcolor: 'grey.300' }} animation="wave" width={75} />
)}
</Typography> </Typography>
<Typography sx={{ typography: 'body2' }}> <Typography sx={{ typography: 'body2' }}>
{fCurrency(limit.myLimit ? limit.myLimit.balance : 0)} {limit ? (
fCurrency(limit.myLimit.balance ?? 0)
) : (
<Skeleton sx={{ bgcolor: 'grey.300' }} animation="wave" width={150} />
)}
</Typography> </Typography>
<Typography sx={{ typography: 'caption', color: '#919EAB' }}> <Typography sx={{ typography: 'caption', color: '#919EAB' }}>
/ {fSplit(limit.myLimit ? limit.myLimit.total : 0)} {limit ? (
`/ ${fSplit(limit.myLimit.total ?? 0)}`
) : (
<Skeleton sx={{ bgcolor: 'grey.300' }} animation="wave" />
)}
</Typography> </Typography>
</div> </Stack>
<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 }}>
{limit.myLimit ? limit.myLimit.percentage : 0}% {limit ? (
`${limit.myLimit.percentage ?? 0}%`
) : (
<Skeleton sx={{ bgcolor: 'grey.300' }} animation="wave" width={70} />
)}
</Typography> </Typography>
</Stack> </Stack>
</Stack> </Stack>
<BorderLinearProgress {limit ? (
variant="determinate" <BorderLinearProgress
value={limit.myLimit ? limit.myLimit.percentage : 0} variant="determinate"
sx={{ mb: 1 }} value={limit.myLimit.percentage ?? 0}
/> sx={{ mb: 1 }}
/>
) : (
<Skeleton sx={{ bgcolor: 'grey.300' }} animation="wave" />
)}
<Stack sx={{ backgroundColor: '#B2E8E8', paddingY: 1, paddingX: 1.5, mb: 2 }}> <Stack sx={{ backgroundColor: '#B2E8E8', paddingY: 1, paddingX: 1.5, mb: 2 }}>
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}> <Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify {limit ? (
icon="bxs:lock-alt" <Fragment>
width={12} <Iconify
height={13} icon="bxs:lock-alt"
sx={{ color: '#424242', marginRight: '6px' }} width={12}
/> height={13}
<Typography variant="caption" component="span"> sx={{ color: '#424242', marginRight: '6px' }}
Lock Fund ( {limit.lockLimit ? limit.lockLimit.percentage : 0}% ) />
</Typography> <Typography variant="caption" component="span">
Lock Fund ( {limit.lockLimit.percentage ?? 0}% )
</Typography>
</Fragment>
) : (
<Skeleton sx={{ bgcolor: 'grey.300' }} animation="wave" width={130} />
)}
</Typography> </Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}> <Typography sx={{ typography: 'caption', color: '#637381' }}>
{fSplit(limit.lockLimit ? limit.lockLimit.balance : 0)} /{' '} {limit ? (
{fSplit(limit.myLimit ? limit.myLimit.total : 0)} `${fSplit(limit.lockLimit.balance ?? 0)} / ${fSplit(limit.myLimit.total ?? 0)}`
) : (
<Skeleton sx={{ bgcolor: 'grey.300' }} animation="wave" width={120} />
)}
</Typography> </Typography>
</Stack> </Stack>
<Stack direction="row" spacing={2}> <Stack direction="row" spacing={2}>
<Button {limit ? (
variant="outlined" <Button
startIcon={<Iconify icon="bi:clipboard-check-fill" />} variant="outlined"
fullWidth={true} startIcon={<Iconify icon="bi:clipboard-check-fill" />}
onClick={() => clickHandler('submitClaim')} fullWidth={true}
> onClick={() => clickHandler('submitClaim')}
Submit Claim >
</Button> Submit Claim
<Button </Button>
) : (
<Skeleton
sx={{ bgcolor: 'grey.300' }}
animation="wave"
height={'36px'}
width={'100%'}
/>
)}
{/* <Button
variant="contained" variant="contained"
startIcon={<Iconify icon="heroicons-solid:cash" />} startIcon={<Iconify icon="heroicons-solid:cash" />}
fullWidth={true} fullWidth={true}
onClick={() => clickHandler('topUpLimit')} onClick={() => clickHandler('topUpLimit')}
> >
Top Up Top Up
</Button> </Button> */}
</Stack> </Stack>
</> </Fragment>
{isDialog === 'submitClaim' && ( {isDialog === 'submitClaim' && (
<DialogClaimSubmitMember <DialogClaimSubmitMember

View File

@@ -1,27 +1,13 @@
import axios from 'axios'; import axios from 'axios';
// config
import { HOST_API } from '../config'; import { HOST_API } from '../config';
import { getSession } from './token';
// ----------------------------------------------------------------------
const token = getSession();
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: HOST_API, baseURL: HOST_API,
// headers: {
// 'X-Requested-With': 'XMLHttpRequest',
// },
// withCredentials: true,
// headers: {
// Authorization: `Bearer ${token}`
// }
}); });
axiosInstance.interceptors.response.use( axiosInstance.interceptors.response.use(
(response) => response, (response) => response,
(error) => Promise.reject((error) || 'Something went wrong') (error) => Promise.reject(error || 'Something went wrong')
); );
export default axiosInstance; export default axiosInstance;