Merge branch 'mhmfajar-dev' into mhmfajar
This commit is contained in:
@@ -16,9 +16,16 @@ import { FormProvider, RHFTextField } from '../../../components/hook-form';
|
||||
|
||||
type FormValuesProps = {
|
||||
email: string;
|
||||
afterSubmit?: string;
|
||||
};
|
||||
|
||||
export default function LoginForm() {
|
||||
interface Props {
|
||||
formPhone: boolean;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function LoginForm({ formPhone }: Props) {
|
||||
const { login } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -28,8 +35,13 @@ export default function LoginForm() {
|
||||
email: Yup.string().email('Email must be a valid email address').required('Email is required'),
|
||||
});
|
||||
|
||||
const defaultValues = {
|
||||
email: '',
|
||||
};
|
||||
|
||||
const methods = useForm<FormValuesProps>({
|
||||
resolver: yupResolver(LoginSchema),
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -43,10 +55,8 @@ export default function LoginForm() {
|
||||
try {
|
||||
await login(data.email);
|
||||
|
||||
navigate('/dashboard');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
navigate('/auth/verify-code', { state: { phoneOrEmail: data.email, formPhone } });
|
||||
} catch (error: any) {
|
||||
reset();
|
||||
|
||||
if (isMountedRef.current) {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import * as Yup from 'yup';
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import axios from '../../../utils/axios';
|
||||
// form
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
@@ -10,6 +8,7 @@ import { Stack, Alert, InputAdornment } from '@mui/material';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
// components
|
||||
import { FormProvider, RHFTextField } from '../../../components/hook-form';
|
||||
import useAuth from '../../../hooks/useAuth';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -18,7 +17,12 @@ type FormValuesProps = {
|
||||
afterSubmit?: string;
|
||||
};
|
||||
|
||||
export default function LoginPhoneForm() {
|
||||
interface Props {
|
||||
formPhone: boolean;
|
||||
}
|
||||
|
||||
export default function LoginPhoneForm({ formPhone }: Props) {
|
||||
const { login } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const LoginSchema = Yup.object().shape({
|
||||
@@ -43,8 +47,8 @@ export default function LoginPhoneForm() {
|
||||
|
||||
const onSubmit = async (data: FormValuesProps) => {
|
||||
try {
|
||||
await axios.post('/otp-request', { phone_or_email: 0 + data.phone });
|
||||
navigate('/auth/otp-validation', { state: { phone_or_email: 0 + data.phone } });
|
||||
await login(0 + data.phone);
|
||||
navigate('/auth/verify-code', { state: { phoneOrEmail: 0 + data.phone, formPhone } });
|
||||
} catch (error: any) {
|
||||
reset();
|
||||
setError('afterSubmit', { ...error, message: error.response.data.message });
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default as LoginEmailForm } from './LoginEmailForm';
|
||||
export { default as LoginPhoneForm } from './LoginPhoneForm';
|
||||
export { default as LoginPhoneForm } from './LoginPhoneForm';
|
||||
@@ -7,6 +7,7 @@ import { useForm, Controller } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
// @mui
|
||||
import { OutlinedInput, Stack } from '@mui/material';
|
||||
import useAuth from '../../../hooks/useAuth';
|
||||
// routes
|
||||
// import { PATH_DASHBOARD } from '../../../routes/paths';
|
||||
|
||||
@@ -19,13 +20,17 @@ type FormValuesProps = {
|
||||
code4: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
phoneOrEmail: string;
|
||||
};
|
||||
|
||||
type ValueNames = 'code1' | 'code2' | 'code3' | 'code4';
|
||||
|
||||
export default function VerifyCodeForm() {
|
||||
export default function VerifyCodeForm({ phoneOrEmail }: Props) {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { validateOtp } = useAuth();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const { phone_or_email } = location.state;
|
||||
|
||||
const VerifyCodeSchema = Yup.object().shape({
|
||||
@@ -64,12 +69,10 @@ export default function VerifyCodeForm() {
|
||||
|
||||
const onSubmit = async (data: FormValuesProps) => {
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
console.log('code:', Object.values(data).join(''));
|
||||
|
||||
enqueueSnackbar('Verify success!');
|
||||
|
||||
// navigate('/dashboard', { replace: true });
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
await validateOtp(phoneOrEmail, Object.values(data).join(''));
|
||||
navigate('/dashboard');
|
||||
enqueueSnackbar('Verify success!', { variant: 'success' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -111,7 +114,7 @@ export default function VerifyCodeForm() {
|
||||
|
||||
return (
|
||||
<form onChange={handleSubmit(onSubmit)}>
|
||||
<Stack direction="row" spacing={2} justifyContent="center">
|
||||
<Stack direction="row" spacing={2} justifyContent="space-evenly">
|
||||
{Object.keys(values).map((name, index) => (
|
||||
<Controller
|
||||
key={name}
|
||||
@@ -122,7 +125,7 @@ export default function VerifyCodeForm() {
|
||||
{...field}
|
||||
id="field-code"
|
||||
autoFocus={index === 0}
|
||||
placeholder="-"
|
||||
placeholder=""
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleChangeWithNextField(event, field.onChange)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// @mui
|
||||
import { Grid, Card, Typography, Stack } from '@mui/material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
// theme
|
||||
import palette from '../../theme/palette';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface ClaimStatusType {
|
||||
name: string;
|
||||
value: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
interface PropsCardClaimStatus {
|
||||
data?: ClaimStatusType[];
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const RootStyle = styled(Card)(({ theme }) => ({
|
||||
boxShadow: 'none',
|
||||
padding: theme.spacing(2),
|
||||
color: 'black',
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
}));
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const defaultData = [
|
||||
{ name: 'Requested', value: 0, color: palette.dark.primary.dark },
|
||||
{ name: 'Approval', value: 0, color: palette.dark.warning.dark },
|
||||
{ name: 'Disbrusment', value: 0, color: palette.dark.success.dark },
|
||||
{ name: 'Rejected', value: 0, color: palette.dark.error.dark },
|
||||
];
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function CardClaimStatus({ data }: PropsCardClaimStatus) {
|
||||
return (
|
||||
<RootStyle>
|
||||
<Stack sx={{ mb: 1 }}>
|
||||
<Typography variant="body2">Claim Status</Typography>
|
||||
</Stack>
|
||||
<Grid container spacing={2}>
|
||||
{data
|
||||
? data.map(({ name, value, color }: ClaimStatusType, key) => (
|
||||
<Grid item key={key} xs={6} sm={3}>
|
||||
<Card
|
||||
sx={{
|
||||
paddingX: 1,
|
||||
borderRadius: 0.75,
|
||||
borderColor: color,
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1px',
|
||||
padding: 2,
|
||||
flex: 1,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography component="p" variant="body2">
|
||||
{name}
|
||||
</Typography>
|
||||
<Typography component="p" variant="h5" sx={{ marginTop: 2 }}>
|
||||
{value}
|
||||
</Typography>
|
||||
<Typography component="p" variant="body2" sx={{ marginTop: 2 }}>
|
||||
Cases
|
||||
</Typography>
|
||||
</Card>
|
||||
</Grid>
|
||||
))
|
||||
: defaultData.map(({ name, value, color }: ClaimStatusType, key) => (
|
||||
<Grid item key={key} xs={6} sm={3}>
|
||||
<Card
|
||||
sx={{
|
||||
paddingX: 1,
|
||||
borderRadius: 0.75,
|
||||
borderColor: color,
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1px',
|
||||
padding: 2,
|
||||
flex: 1,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography component="p" variant="body2">
|
||||
{name}
|
||||
</Typography>
|
||||
<Typography component="p" variant="h5" sx={{ marginTop: 2 }}>
|
||||
{value}
|
||||
</Typography>
|
||||
<Typography component="p" variant="body2" sx={{ marginTop: 2 }}>
|
||||
Cases
|
||||
</Typography>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</RootStyle>
|
||||
);
|
||||
}
|
||||
161
frontend/client-portal/src/sections/dashboard/CardBalance.tsx
Normal file
161
frontend/client-portal/src/sections/dashboard/CardBalance.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
// @mui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Typography,
|
||||
LinearProgress,
|
||||
linearProgressClasses,
|
||||
Stack,
|
||||
} from '@mui/material';
|
||||
// components
|
||||
import Iconify from '../../components/Iconify';
|
||||
// React
|
||||
import { useState } from 'react';
|
||||
// utils
|
||||
import { fCurrency } from '../../utils/formatNumber';
|
||||
// <sections></sections>
|
||||
import DialogTopUpLimit from './DialogTopUpLimit';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type DataContent = {
|
||||
info: string;
|
||||
date: string;
|
||||
time: string;
|
||||
};
|
||||
|
||||
type NotificationProps = {
|
||||
data?: DataContent[];
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const RootBalanceStyle = styled(Card)(({ theme }) => ({
|
||||
boxShadow: 'none',
|
||||
padding: theme.spacing(3),
|
||||
color: 'black',
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
maxHeight: '240px',
|
||||
}));
|
||||
|
||||
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
|
||||
height: 10,
|
||||
borderRadius: 6,
|
||||
[`&.${linearProgressClasses.colorPrimary}`]: {
|
||||
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
|
||||
},
|
||||
[`& .${linearProgressClasses.bar}`]: {
|
||||
borderRadius: 6,
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
}));
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const INITIAL = '500.000.000';
|
||||
const TOTAL = 375000000;
|
||||
const PERCENT = 75;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function CardBalance({ data }: NotificationProps) {
|
||||
const [openDialog, setOpenDialog] = useState(false);
|
||||
const [dialogTitle, setDialogTitle] = useState('');
|
||||
const [isDialog, setIsDialog] = useState('');
|
||||
|
||||
const clickHandler = (isDialog: string) => {
|
||||
switch (isDialog) {
|
||||
case 'submitClaim':
|
||||
setDialogTitle('Notification');
|
||||
setIsDialog(isDialog);
|
||||
setOpenDialog(true);
|
||||
break;
|
||||
case 'topUpLimit':
|
||||
setDialogTitle('Top Up Limit');
|
||||
setIsDialog(isDialog);
|
||||
setOpenDialog(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<RootBalanceStyle>
|
||||
<>
|
||||
<Stack direction="row" justifyContent="space-between" sx={{ mb: 1 }}>
|
||||
<div>
|
||||
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
|
||||
Total Limit
|
||||
</Typography>
|
||||
<Typography sx={{ typography: 'body2' }}>{fCurrency(TOTAL)}</Typography>
|
||||
<Typography sx={{ typography: 'caption', color: '#919EAB' }}>/ {INITIAL}</Typography>
|
||||
</div>
|
||||
|
||||
<Stack direction="row" alignItems="center" justifyContent="center">
|
||||
<Typography variant="h5" sx={{ ml: 0.5 }}>
|
||||
{PERCENT}%
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<BorderLinearProgress variant="determinate" value={PERCENT} sx={{ mb: 1 }} />
|
||||
|
||||
<Stack sx={{ backgroundColor: '#B2E8E8', paddingY: 1, paddingX: 1.5, mb: 2 }}>
|
||||
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
|
||||
<Iconify
|
||||
icon="bxs:lock-alt"
|
||||
width={12}
|
||||
height={13}
|
||||
sx={{ color: '#424242', marginRight: '6px' }}
|
||||
/>
|
||||
<Typography variant="caption" component="span">
|
||||
Lock Fund ( 25% )
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Typography sx={{ typography: 'caption', color: '#637381' }}>
|
||||
125.000.000 / 125.000.000
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" spacing={2}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Iconify icon="bi:clipboard-check-fill" />}
|
||||
fullWidth={true}
|
||||
onClick={() => clickHandler('submitClaim')}
|
||||
>
|
||||
Submit Claim
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<Iconify icon="heroicons-solid:cash" />}
|
||||
fullWidth={true}
|
||||
onClick={() => clickHandler('topUpLimit')}
|
||||
>
|
||||
Top Up
|
||||
</Button>
|
||||
</Stack>
|
||||
</>
|
||||
|
||||
{/* {isDialog === 'submitClaim' && (
|
||||
<DialogNotification
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
title={dialogTitle}
|
||||
data={data}
|
||||
/>
|
||||
)} */}
|
||||
|
||||
{isDialog === 'topUpLimit' && (
|
||||
<DialogTopUpLimit
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
title={{ name: dialogTitle, icon: 'heroicons-solid:cash' }}
|
||||
/>
|
||||
)}
|
||||
</RootBalanceStyle>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// @mui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Button, Card, Typography, Link, Divider, Stack } from '@mui/material';
|
||||
import { ChevronRight } from '@mui/icons-material';
|
||||
// components
|
||||
import Iconify from '../../components/Iconify';
|
||||
// Section
|
||||
import DialogNotification from './DialogNotification';
|
||||
import DialogDetailClaim from './DialogDetailClaim';
|
||||
// React
|
||||
import { useState } from 'react';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type DataContent = {
|
||||
info: string;
|
||||
date: string;
|
||||
time: string;
|
||||
};
|
||||
|
||||
type NotificationProps = {
|
||||
data?: DataContent[];
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const RootNotificationStyle = styled(Card)(({ theme }) => ({
|
||||
boxShadow: 'none',
|
||||
padding: '1rem 0.5rem',
|
||||
color: 'black',
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
maxHeight: '240px',
|
||||
}));
|
||||
|
||||
const ItemNotificationStyle = styled(Card)(({ theme }) => ({
|
||||
boxShadow: 'none',
|
||||
padding: theme.spacing(1),
|
||||
borderRadius: 0.5,
|
||||
color: 'black',
|
||||
}));
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function CardNotification({ data }: NotificationProps) {
|
||||
const [openDialog, setOpenDialog] = useState(false);
|
||||
const [dialogTitle, setDialogTitle] = useState('');
|
||||
const [isDialog, setIsDialog] = useState('');
|
||||
|
||||
const clickHandler = (isDialog: string) => {
|
||||
switch (isDialog) {
|
||||
case 'allNotification':
|
||||
setDialogTitle('Notification');
|
||||
setIsDialog(isDialog);
|
||||
setOpenDialog(true);
|
||||
break;
|
||||
case 'infoDetail':
|
||||
setDialogTitle('Claim Details');
|
||||
setIsDialog(isDialog);
|
||||
setOpenDialog(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<RootNotificationStyle>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
component="span"
|
||||
sx={{ display: 'flex', alignItems: 'center' }}
|
||||
>
|
||||
<Iconify icon="eva:bell-fill" marginRight={0.75} />
|
||||
Notification
|
||||
<span
|
||||
style={{
|
||||
width: '12px',
|
||||
height: '12px',
|
||||
backgroundColor: '#19BBBB',
|
||||
marginLeft: '0.5rem',
|
||||
borderRadius: '50%',
|
||||
}}
|
||||
/>
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Button
|
||||
sx={{ typography: 'body2' }}
|
||||
endIcon={<ChevronRight />}
|
||||
onClick={() => clickHandler('allNotification')}
|
||||
>
|
||||
View All
|
||||
</Button>
|
||||
</Stack>
|
||||
|
||||
<ItemNotificationStyle sx={{ marginTop: 2, overflowY: 'auto', maxHeight: '154px' }}>
|
||||
{data
|
||||
? data.map(({ info, date, time }, key) => (
|
||||
<div key={key}>
|
||||
{key >= 1 ? <Divider sx={{ marginY: 0.5 }} /> : ''}
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
|
||||
<Typography sx={{ typography: 'caption' }}>{info}</Typography>
|
||||
<Link
|
||||
component="button"
|
||||
variant="caption"
|
||||
underline="always"
|
||||
onClick={() => clickHandler('infoDetail')}
|
||||
>
|
||||
Info Detail
|
||||
</Link>
|
||||
</Stack>
|
||||
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
|
||||
<Typography sx={{ typography: 'caption', color: '#656565' }}>{date}</Typography>
|
||||
<Typography sx={{ typography: 'caption', color: '#656565' }}>{time}</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</div>
|
||||
))
|
||||
: ''}
|
||||
</ItemNotificationStyle>
|
||||
|
||||
{isDialog === 'allNotification' && (
|
||||
<DialogNotification
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
title={{ name: dialogTitle }}
|
||||
data={data}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isDialog === 'infoDetail' && (
|
||||
<DialogDetailClaim
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
title={{ name: dialogTitle }}
|
||||
/>
|
||||
)}
|
||||
</RootNotificationStyle>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// @mui
|
||||
import {
|
||||
Button,
|
||||
Box,
|
||||
Stepper,
|
||||
Step,
|
||||
StepLabel,
|
||||
Card,
|
||||
Typography,
|
||||
Divider,
|
||||
Stack,
|
||||
} from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
// components
|
||||
import MuiDialog from '../../components/MuiDialog';
|
||||
// theme
|
||||
import palette from '../../theme/palette';
|
||||
// React
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
type DataContent = {
|
||||
info: string;
|
||||
date: string;
|
||||
time: string;
|
||||
};
|
||||
|
||||
type MuiDialogProps = {
|
||||
title?: {
|
||||
name?: string;
|
||||
icon?: string;
|
||||
};
|
||||
openDialog: boolean;
|
||||
setOpenDialog: Function;
|
||||
content?: ReactElement;
|
||||
data?: DataContent[];
|
||||
};
|
||||
|
||||
const steps = ['Review', 'Approval', 'Disbursement'];
|
||||
|
||||
const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
|
||||
const getContent = () => (
|
||||
<>
|
||||
<Stack
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
direction="row"
|
||||
sx={{ marginTop: 1 }}
|
||||
>
|
||||
<Typography variant="subtitle1" sx={{ height: 'max-content' }}>
|
||||
Claim Request
|
||||
</Typography>
|
||||
<Stack>
|
||||
<Typography variant="caption">Submission date</Typography>
|
||||
<Typography variant="caption">15 / 05 / 2022</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Box sx={{ width: '100%', marginTop: 2 }}>
|
||||
<Stepper alternativeLabel>
|
||||
{steps.map((label) => (
|
||||
<Step key={label}>
|
||||
<StepLabel>{label}</StepLabel>
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
</Box>
|
||||
<Stack marginTop={2}>
|
||||
<Typography variant="subtitle1" paddingY={2}>
|
||||
17 Mei 2022
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction="row" spacing={2}>
|
||||
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
|
||||
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
|
||||
{/* Item 1 */}
|
||||
<Card sx={{ paddingY: 2, paddingX: 3 }}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body1">09:10 WIB</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
backgroundColor: palette.light.warning.lighter,
|
||||
color: palette.light.warning.dark,
|
||||
borderColor: palette.light.warning.dark,
|
||||
border: '1px solid',
|
||||
borderRadius: '6px',
|
||||
padding: 1,
|
||||
}}
|
||||
variant="caption"
|
||||
>
|
||||
Approval
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Divider sx={{ marginY: 2 }} />
|
||||
<Stack>
|
||||
<Typography variant="subtitle2" color="#404040">
|
||||
Details : mohon melengkapi kekurangan dokumen
|
||||
</Typography>
|
||||
<Typography variant="caption" color="#757575" sx={{ marginTop: 2, marginBottom: 1 }}>
|
||||
Lab pemeriksaan darah
|
||||
</Typography>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
fullWidth
|
||||
sx={{ typography: 'subtitle2', borderColor: '#F5F5F5' }}
|
||||
>
|
||||
Hasil Pemeriksaan Laboratorium
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
{/* Item 2 */}
|
||||
<Card sx={{ flex: 1, maxWidth: '100%', paddingY: 2, paddingX: 3 }}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body1">09:00 WIB</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
backgroundColor: palette.light.warning.lighter,
|
||||
color: palette.light.warning.dark,
|
||||
borderColor: palette.light.warning.dark,
|
||||
border: '1px solid',
|
||||
borderRadius: '6px',
|
||||
padding: 1,
|
||||
}}
|
||||
variant="caption"
|
||||
>
|
||||
Approval
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Divider sx={{ marginY: 2 }} />
|
||||
<Stack>
|
||||
<Typography variant="subtitle2" color="#404040">
|
||||
Details : Penilaian Dokter
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Card>
|
||||
{/* Item 3 */}
|
||||
<Card sx={{ flex: 1, maxWidth: '100%', paddingY: 2, paddingX: 3 }}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body1">08:00 WIB</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
backgroundColor: '#F5F5F5',
|
||||
color: '#757575',
|
||||
borderColor: '#757575',
|
||||
border: '1px solid',
|
||||
borderRadius: '6px',
|
||||
padding: 1,
|
||||
}}
|
||||
variant="caption"
|
||||
>
|
||||
Review
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Divider sx={{ marginY: 2 }} />
|
||||
<Stack>
|
||||
<Typography variant="subtitle2" color="#404040">
|
||||
Details : Klaim Diajukan
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<MuiDialog
|
||||
title={title}
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
content={getContent()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DialogDetailClaim;
|
||||
@@ -0,0 +1,93 @@
|
||||
// react
|
||||
import { ReactElement, useState } from 'react';
|
||||
// mui
|
||||
import { Card, Divider, Link, Stack, Typography } from '@mui/material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
// Component
|
||||
import MuiDialog from '../../components/MuiDialog';
|
||||
// Sections
|
||||
import DialogDetailClaim from './DialogDetailClaim';
|
||||
|
||||
type DataContent = {
|
||||
info: string;
|
||||
date: string;
|
||||
time: string;
|
||||
};
|
||||
|
||||
type MuiDialogProps = {
|
||||
title?: {
|
||||
name?: string;
|
||||
icon?: string;
|
||||
};
|
||||
openDialog: boolean;
|
||||
setOpenDialog: Function;
|
||||
content?: ReactElement;
|
||||
data?: DataContent[];
|
||||
};
|
||||
|
||||
const ItemNotificationStyle = styled(Card)(({ theme }) => ({
|
||||
boxShadow: 'none',
|
||||
padding: theme.spacing(1),
|
||||
borderRadius: 0.5,
|
||||
color: 'black',
|
||||
}));
|
||||
|
||||
const DialogNotification = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
|
||||
const [openDialogClaim, setOpenDialogClaim] = useState(false);
|
||||
const [dialogTitleClaim, setDialogTitleClaim] = useState('');
|
||||
|
||||
const clickHandler = () => {
|
||||
setDialogTitleClaim('Claim Details');
|
||||
setOpenDialogClaim(true);
|
||||
};
|
||||
|
||||
const getContent = () => (
|
||||
<Stack sx={{ marginTop: 2 }}>
|
||||
<ItemNotificationStyle>
|
||||
{data
|
||||
? data.map(({ info, date, time }: DataContent, key) => (
|
||||
<div key={key}>
|
||||
{key >= 1 ? <Divider sx={{ marginY: 0.5 }} /> : ''}
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
|
||||
<Typography sx={{ typography: 'caption' }}>{info}</Typography>
|
||||
<Link
|
||||
component="button"
|
||||
variant="caption"
|
||||
underline="always"
|
||||
onClick={clickHandler}
|
||||
>
|
||||
Info Detail
|
||||
</Link>
|
||||
</Stack>
|
||||
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
|
||||
<Typography sx={{ typography: 'caption', color: '#656565' }}>{date}</Typography>
|
||||
<Typography sx={{ typography: 'caption', color: '#656565' }}>{time}</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</div>
|
||||
))
|
||||
: ''}
|
||||
</ItemNotificationStyle>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MuiDialog
|
||||
title={title}
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
content={getContent()}
|
||||
/>
|
||||
|
||||
<DialogDetailClaim
|
||||
openDialog={openDialogClaim}
|
||||
setOpenDialog={setOpenDialogClaim}
|
||||
title={{ name: dialogTitleClaim }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DialogNotification;
|
||||
@@ -0,0 +1,209 @@
|
||||
// @mui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import {
|
||||
Typography,
|
||||
LinearProgress,
|
||||
linearProgressClasses,
|
||||
Stack,
|
||||
FormControlLabel,
|
||||
} from '@mui/material';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
// components
|
||||
import MuiDialog from '../../components/MuiDialog';
|
||||
import { FormProvider, RHFTextField } from '../../components/hook-form';
|
||||
// React
|
||||
import { ReactElement, useEffect, useState } from 'react';
|
||||
import { fCurrency } from '../../utils/formatNumber';
|
||||
// yup
|
||||
import * as Yup from 'yup';
|
||||
// form
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type DataContent = {
|
||||
info: string;
|
||||
date: string;
|
||||
time: string;
|
||||
};
|
||||
|
||||
type MuiDialogProps = {
|
||||
title?: {
|
||||
name?: string;
|
||||
icon?: string;
|
||||
};
|
||||
openDialog: boolean;
|
||||
setOpenDialog: Function;
|
||||
content?: ReactElement;
|
||||
data?: DataContent[];
|
||||
};
|
||||
|
||||
type FormValuesProps = {
|
||||
topup: string;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const testData = {
|
||||
companyName: 'PT. Aman Mineral',
|
||||
policyNumber: 12345678,
|
||||
totalMembers: 50,
|
||||
totalCases: 100,
|
||||
totalPersen: 75,
|
||||
myLimit: '375.000.000',
|
||||
totalLimit: 500000000,
|
||||
};
|
||||
|
||||
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
|
||||
height: 10,
|
||||
borderRadius: 6,
|
||||
[`&.${linearProgressClasses.colorPrimary}`]: {
|
||||
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
|
||||
},
|
||||
[`& .${linearProgressClasses.bar}`]: {
|
||||
borderRadius: 6,
|
||||
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
|
||||
},
|
||||
}));
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
|
||||
const [isDisabledCheckbox, setIsDisabledCheckbox] = useState(false);
|
||||
const [isDisabledButton, setIsDisabledButton] = useState(true);
|
||||
|
||||
const TopUpSchema = Yup.object().shape({
|
||||
topup: Yup.string()
|
||||
/*
|
||||
// @ts-ignore */
|
||||
.test('limit', 'Maximum Top Up Rp. 5.000.000', (val) => (val > 5000000 ? false : true)),
|
||||
});
|
||||
|
||||
const defaultValues = {
|
||||
topup: '0',
|
||||
};
|
||||
|
||||
const methods = useForm<FormValuesProps>({
|
||||
resolver: yupResolver(TopUpSchema),
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const {
|
||||
setValue,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting },
|
||||
} = methods;
|
||||
|
||||
useEffect(() => {
|
||||
if (openDialog === false) {
|
||||
reset();
|
||||
}
|
||||
}, [openDialog, reset]);
|
||||
|
||||
const onSubmit = async (data: FormValuesProps) => {
|
||||
reset();
|
||||
};
|
||||
|
||||
const onCheckHandler = (data: FormValuesProps) => {
|
||||
setIsDisabledCheckbox(!isDisabledCheckbox);
|
||||
setValue('topup', '5000000');
|
||||
};
|
||||
|
||||
const onTopupHandler = (value: string) => {
|
||||
value === '0' || value === '' ? setIsDisabledButton(true) : setIsDisabledButton(false);
|
||||
setValue('topup', value);
|
||||
};
|
||||
|
||||
const getContent = () => (
|
||||
<Stack spacing={1} marginTop={2}>
|
||||
<Stack>
|
||||
<Typography variant="caption" color="#637381">
|
||||
Company Name
|
||||
</Typography>
|
||||
<Typography variant="body2">{testData.companyName}</Typography>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Typography variant="caption" color="#637381">
|
||||
Policy Number
|
||||
</Typography>
|
||||
<Typography variant="body2">{testData.policyNumber}</Typography>
|
||||
</Stack>
|
||||
<Stack direction="row" spacing={22}>
|
||||
<Stack>
|
||||
<Typography variant="caption" color="#637381">
|
||||
Total Member
|
||||
</Typography>
|
||||
<Typography variant="body2">{testData.totalMembers} Person</Typography>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Typography variant="caption" color="#637381">
|
||||
Total Cases
|
||||
</Typography>
|
||||
<Typography variant="body2">{testData.totalCases} Cases</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack spacing={1} sx={{ backgroundColor: '#F4F6F8', borderRadius: 1.5, padding: 2 }}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Stack>
|
||||
<Typography variant="body2">Company Pooled Fund</Typography>
|
||||
<Typography variant="body2">{fCurrency(testData.myLimit)}</Typography>
|
||||
<Typography variant="caption" color="#919EAB">
|
||||
/ {testData.totalLimit}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Typography variant="h5">{testData.totalPersen}%</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<BorderLinearProgress variant="determinate" value={testData.totalPersen} />
|
||||
</Stack>
|
||||
<Stack spacing={2}>
|
||||
<Typography variant="subtitle1" marginTop={3}>
|
||||
Top Up Limit
|
||||
</Typography>
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<RHFTextField
|
||||
name="topup"
|
||||
label="Top Up"
|
||||
type="number"
|
||||
disabled={isDisabledCheckbox}
|
||||
onChange={(e) => onTopupHandler(e.target.value)}
|
||||
/>
|
||||
<FormControlLabel
|
||||
sx={{ typography: 'caption' }}
|
||||
control={<Checkbox />}
|
||||
label={'Max ' + fCurrency(5000000)}
|
||||
onChange={handleSubmit(onCheckHandler)}
|
||||
/>
|
||||
|
||||
<LoadingButton
|
||||
fullWidth
|
||||
size="large"
|
||||
type="submit"
|
||||
variant="contained"
|
||||
loading={isSubmitting}
|
||||
sx={{ marginTop: 2 }}
|
||||
disabled={isDisabledButton}
|
||||
>
|
||||
Login
|
||||
</LoadingButton>
|
||||
</FormProvider>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
return (
|
||||
<MuiDialog
|
||||
title={title}
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
content={getContent()}
|
||||
maxWidth="xs"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DialogTopUpLimit;
|
||||
@@ -1,80 +0,0 @@
|
||||
import merge from 'lodash/merge';
|
||||
import ReactApexChart from 'react-apexcharts';
|
||||
// @mui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Card, Typography, Stack } from '@mui/material';
|
||||
// utils
|
||||
import { fCurrency, fPercent } from '../../utils/formatNumber';
|
||||
// components
|
||||
import Iconify from '../../components/Iconify';
|
||||
import BaseOptionChart from '../../components/chart/BaseOptionChart';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const RootStyle = styled(Card)(({ theme }) => ({
|
||||
boxShadow: 'none',
|
||||
padding: theme.spacing(3),
|
||||
color: theme.palette.primary.darker,
|
||||
backgroundColor: theme.palette.primary.lighter,
|
||||
}));
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const INITIAL = 500000000
|
||||
const TOTAL = 257907000;
|
||||
const PERCENT = -3;
|
||||
const CHART_DATA = [{ data: [100, 99, 99, 85, 74, 57, 54, 51] }];
|
||||
|
||||
export default function SomethingUsage() {
|
||||
const chartOptions = merge(BaseOptionChart(), {
|
||||
chart: { sparkline: { enabled: true } },
|
||||
xaxis: { labels: { show: true } },
|
||||
yaxis: { labels: { show: false } },
|
||||
stroke: { width: 4 },
|
||||
legend: { show: false },
|
||||
grid: { show: false },
|
||||
tooltip: {
|
||||
marker: { show: false },
|
||||
y: {
|
||||
formatter: (seriesName: string) => (seriesName) + "%",
|
||||
title: {
|
||||
formatter: () => '',
|
||||
},
|
||||
},
|
||||
},
|
||||
fill: { gradient: { opacityFrom: 0, opacityTo: 0 } },
|
||||
});
|
||||
|
||||
return (
|
||||
<RootStyle>
|
||||
<Stack direction="row" justifyContent="space-between" sx={{ mb: 3 }}>
|
||||
<div>
|
||||
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
|
||||
{fCurrency(INITIAL)}
|
||||
</Typography>
|
||||
<Typography sx={{ typography: 'subtitle2' }}>Remaining Balance</Typography>
|
||||
<Typography sx={{ typography: 'h3' }}>{fCurrency(TOTAL)}</Typography>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Stack direction="row" alignItems="center" justifyContent="flex-end" sx={{ mb: 0.6 }}>
|
||||
<Iconify
|
||||
width={20}
|
||||
height={20}
|
||||
icon={PERCENT >= 0 ? 'eva:trending-up-fill' : 'eva:trending-down-fill'}
|
||||
/>
|
||||
<Typography variant="subtitle2" component="span" sx={{ ml: 0.5 }}>
|
||||
{PERCENT > 0 && '+'}
|
||||
{fPercent(PERCENT)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
|
||||
than last month
|
||||
</Typography>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
<ReactApexChart type="area" series={CHART_DATA} options={chartOptions} height={100} />
|
||||
</RootStyle>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user