[WIP] Add Claim Request

This commit is contained in:
R
2023-02-14 12:39:51 +07:00
parent a7e688a52c
commit 13542cd3c0
102 changed files with 2625 additions and 391 deletions

View File

@@ -1,7 +1,7 @@
// @mui
import { Grid, Button, Divider, Typography } from '@mui/material';
// components
import Iconify from '../../components/Iconify';
import Iconify from '@/components/Iconify';
// ----------------------------------------------------------------------

View File

@@ -9,11 +9,11 @@ import { Link as RouterLink, useNavigate } from 'react-router-dom';
import { Alert, IconButton, InputAdornment, Stack, Typography } from '@mui/material';
import { LoadingButton } from '@mui/lab';
// hooks
import useIsMountedRef from '../../../hooks/useIsMountedRef';
import useIsMountedRef from '@/hooks/useIsMountedRef';
// components
import { FormProvider, RHFTextField } from '../../../components/hook-form';
import axios from '../../../utils/axios';
import Iconify from '../../../components/Iconify';
import { FormProvider, RHFTextField } from '@/components/hook-form';
import axios from '@/utils/axios';
import Iconify from '@/components/Iconify';
// ----------------------------------------------------------------------

View File

@@ -8,13 +8,13 @@ import { yupResolver } from '@hookform/resolvers/yup';
import { Link, Stack, Alert, IconButton, InputAdornment } from '@mui/material';
import { LoadingButton } from '@mui/lab';
// routes
import { PATH_AUTH } from '../../../routes/paths';
import { PATH_AUTH } from '@/routes/paths';
// hooks
import useAuth from '../../../hooks/useAuth';
import useIsMountedRef from '../../../hooks/useIsMountedRef';
import useAuth from '@/hooks/useAuth';
import useIsMountedRef from '@/hooks/useIsMountedRef';
// components
import Iconify from '../../../components/Iconify';
import { FormProvider, RHFTextField, RHFCheckbox } from '../../../components/hook-form';
import Iconify from '@/components/Iconify';
import { FormProvider, RHFTextField, RHFCheckbox } from '@/components/hook-form';
// ----------------------------------------------------------------------

View File

@@ -7,11 +7,11 @@ import { yupResolver } from '@hookform/resolvers/yup';
import { Stack, IconButton, InputAdornment, Alert } from '@mui/material';
import { LoadingButton } from '@mui/lab';
// hooks
import useAuth from '../../../hooks/useAuth';
import useIsMountedRef from '../../../hooks/useIsMountedRef';
import useAuth from '@/hooks/useAuth';
import useIsMountedRef from '@/hooks/useIsMountedRef';
// components
import Iconify from '../../../components/Iconify';
import { FormProvider, RHFTextField } from '../../../components/hook-form';
import Iconify from '@/components/Iconify';
import { FormProvider, RHFTextField } from '@/components/hook-form';
// ----------------------------------------------------------------------

View File

@@ -6,10 +6,10 @@ import { useForm } from 'react-hook-form';
import { Alert, Stack } from '@mui/material';
import { LoadingButton } from '@mui/lab';
// hooks
import useIsMountedRef from '../../../hooks/useIsMountedRef';
import useIsMountedRef from '@/hooks/useIsMountedRef';
// components
import { FormProvider, RHFTextField } from '../../../components/hook-form';
import axios from '../../../utils/axios';
import { FormProvider, RHFTextField } from '@/components/hook-form';
import axios from '@/utils/axios';
// ----------------------------------------------------------------------

View File

@@ -9,7 +9,7 @@ import { yupResolver } from '@hookform/resolvers/yup';
import { OutlinedInput, Stack } from '@mui/material';
import { LoadingButton } from '@mui/lab';
// routes
// import { PATH_DASHBOARD } from '../../../routes/paths';
// import { PATH_DASHBOARD } from '@/routes/paths';
// ----------------------------------------------------------------------

View File

@@ -0,0 +1,140 @@
// @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';
// 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>
);
}

View File

@@ -0,0 +1,153 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Button,
Card,
Typography,
Link,
Divider,
Stack,
TextField,
Grid,
InputAdornment,
} from '@mui/material';
import { ChevronRight } from '@mui/icons-material';
// React
import React, { useState } from 'react';
import { LoadingButton } from '@mui/lab';
import Iconify from '@/components/Iconify';
import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { fPostFormat } from '@/utils/formatTime';
import { DateCalendar } from '@mui/x-date-pickers/DateCalendar';
import DialogBenefit from './DiaglogBenefit';
import MuiDialog from '@/components/MuiDialog';
import axios from '@/utils/axios';
import { useSnackbar } from 'notistack';
import DialogMember from './DialogMember';
// ----------------------------------------------------------------------
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 CardSearchMember() {
const {enqueueSnackbar} = useSnackbar();
const [noPolis, setNoPolis] = useState('');
const [tanggalLahir, setTanggalLahir] = useState('');
const [loadingBenefit, setLoadingBenefit] = useState(false);
const [loadingClaim, setLoadingClaim] = useState(false);
const [openDialogBenefit, setOpenDialogBenefit] = useState(false);
const [openDialogClaim, setOpenDialogClaim] = useState(false);
const [currentMember, setCurrentMember] = useState(null);
function handleSearchMember() {
setLoadingBenefit(true)
axios.post('/search-member', {
no_polis: noPolis,
birth_date: tanggalLahir ? fPostFormat(tanggalLahir, 'yyyy-MM-dd') : null
})
.then((response) => {
setOpenDialogBenefit(true)
setCurrentMember(response.data.data)
})
.catch(({response}) => {
enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.message : 'Opps, Something went Wrong!'), {variant : "error"})
})
.then(() => {
setLoadingBenefit(false)
});
}
return (
<div>
<RootNotificationStyle sx={{ p: 2, height: 'auto' }}>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
sx={{ paddingBottom: 2, paddingTop: 1 }}
>
<Typography>
<Typography
variant="body2"
component="span"
sx={{ display: 'flex', alignItems: 'center' }}
>
Pengajuan Jaminan
</Typography>
</Typography>
</Stack>
<Stack gap={2}>
<TextField variant="outlined" label="Nomor Polis" value={noPolis} onChange={(event) => {
setNoPolis(event.target.value)
}}></TextField>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Tanggal Lahir"
value={tanggalLahir}
onChange={(newValue) => {
setTanggalLahir( (newValue));
}}
inputFormat="dd-MM-yyyy"
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<Stack gap={2} flexDirection="row">
<LoadingButton
variant="outlined"
sx={{
background: '#fff',
p: 1,
width: '100%',
}}
loading={loadingBenefit}
onClick={() => {
handleSearchMember()
}}
>
<Iconify icon="eva:eye-fill" marginRight={0.75} />
Cari Member
</LoadingButton>
{/* <LoadingButton
variant="contained"
sx={{
p: 1,
width: '100%',
}}
loading={loadingClaim}
>
Ajukan Penjaminan
</LoadingButton> */}
</Stack>
</Stack>
</RootNotificationStyle>
{/*
<DialogBenefit open={openDialogBenefit} setOpen={setOpenDialogBenefit}></DialogBenefit> */}
<MuiDialog
title={{name: "Member"}}
openDialog={openDialogBenefit}
setOpenDialog={setOpenDialogBenefit}
content={DialogMember(currentMember, () => {setOpenDialogBenefit(false)})}
maxWidth="md"
/>
</div>
);
}

View File

@@ -0,0 +1,315 @@
// @mui
import {
Autocomplete,
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,
TablePagination,
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 { Member } from '@/@types/member';
import Iconify from '@/components/Iconify';
export default function DashboardTable() {
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<HTMLInputElement>(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 (
<form onSubmit={handleSearchSubmit} style={{ flex: '1' }}>
<TextField
id="search-input"
ref={searchInput}
label="Search"
variant="outlined"
fullWidth
onChange={handleSearchChange}
value={searchText}
/>
</form>
);
}
function ImportForm(props: any) {
// IMPORT
// Create Button Menu
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const createMenu = Boolean(anchorEl);
const importForm = useRef<HTMLInputElement>(null);
const [currentImportFileName, setCurrentImportFileName] = useState(null);
const handleImportChange = (event: any) => {
if (event.target.files[0]) {
setCurrentImportFileName(event.target.files[0].name);
} else {
setCurrentImportFileName(null);
}
};
const options = ['All', 'Option 2'];
const [value, setValue] = React.useState<string | null>(options[0]);
const [inputValue, setInputValue] = React.useState('');
return (
<div>
<Stack direction={'row'} justifyContent="space-between" spacing={2} sx={{ p: 2 }}>
{/* Filter Division */}
{/* <Autocomplete
value={value}
onChange={(event: any, newValue: string | null) => {
setValue(newValue);
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
id="controllable-states-demo"
options={options}
sx={{ minWidth: 240 }}
renderInput={(params) => <TextField {...params} label="Division" />}
/> */}
{/* Search */}
<SearchInput onSearch={applyFilter} />
{/* Button Import */}
<Button
id="import-button"
variant="outlined"
startIcon={<Iconify icon="eva:download-fill" />}
sx={{ p: 1.8, minWidth: '104px' }}
// onClick={() => {}}
>
Import
</Button>
{/* <input
type="file"
id="file"
ref={importForm}
style={{ display: 'none' }}
onChange={handleImportChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/> */}
{/* Button Add Task */}
<Button variant="contained" startIcon={<AddIcon />} sx={{ p: 1.8, minWidth: '142px' }}>
Add Data
</Button>
</Stack>
</div>
);
}
// 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<typeof createData> }) {
// const { row } = props;
// const [open, setOpen] = React.useState(true);
// return (
// <React.Fragment>
// <TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
// <TableCell>
// <IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
// {open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
// </IconButton>
// </TableCell>
// <TableCell align="left">{row.member_id}</TableCell>
// <TableCell align="left">{row.payor_id}</TableCell>
// <TableCell align="left">{row.name}</TableCell>
// <TableCell align="left">{row.nik}</TableCell>
// <TableCell align="left">{row.nric}</TableCell>
// <TableCell align="right">
// <Button variant="outlined" color="success" size="small">
// Active
// </Button>
// </TableCell>
// {/* <TableCell align="right"><Button variant="outlined" color="error" size="small">Disable</Button></TableCell> */}
// </TableRow>
// {/* COLLAPSIBLE ROW */}
// <TableRow>
// <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={99}>
// <Collapse in={open} timeout="auto" unmountOnExit>
// <Box sx={{ borderBottom: 1 }}>
// <Typography variant="body2" gutterBottom component="div">
// <Grid></Grid>
// </Typography>
// </Box>
// </Collapse>
// </TableCell>
// </TableRow>
// </React.Fragment>
// );
// }
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = useState(true);
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>({
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 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 });
};
useEffect(() => {
loadDataTableData();
}, []);
return (
<Card>
<ImportForm />
<Stack>
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left">
MemberID
</TableCell>
<TableCell style={headStyle} align="left">
Name
</TableCell>
<TableCell style={headStyle} align="left">
Divisi
</TableCell>
<TableCell style={headStyle} align="left">
Limit
</TableCell>
<TableCell style={headStyle} align="right" width={100}>
Status
</TableCell>
<TableCell style={headStyle} align="right" width={100}>
Action
</TableCell>
</TableRow>
</TableBody>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
No Data Found
</TableCell>
</TableRow>
</TableBody>
) : dataTableData.data.length === 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
No Data
</TableCell>
</TableRow>
</TableBody>
) : (
<TableBody>
{/* {dataTableData.data.map((row) => (
<Row key={row.id} row={row} />
))} */}
Testing
</TableBody>
)}
</Table>
</TableContainer>
{/* <TablePagination
rowsPerPageOptions={[5, 10, 25]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/> */}
</Stack>
</Card>
);
}

View File

@@ -0,0 +1,100 @@
// mui
import { styled } from '@mui/material/styles';
import { LoadingButton, TabPanel } from "@mui/lab";
import { Card, Divider, Grid, LinearProgress, linearProgressClasses, Typography } from "@mui/material";
import { Tab, Tabs } from "@mui/material";
import { Box, Stack } from "@mui/material";
import { useEffect, useState } from "react";
import { fCurrency } from '@/utils/formatNumber';
import { fPostFormat } from '@/utils/formatTime';
import { Avatar } from '@mui/material';
import Iconify from '@/components/Iconify';
import FormRequestClaim from './FormRequestClaim';
export default function DialogMember(member, closeDialog) {
const [currentTab, setCurrentTab] = useState('request')
// ----------------------------------------------------------------------
useEffect(() => {
setCurrentTab('benefit')
}, [member])
function handleChangeTab(event: React.SyntheticEvent, newValue: string) {
setCurrentTab(newValue)
}
// ----------------------------------------------------------------------
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%)',
},
}));
function TabPanel(props) {
const { children, value, index, ...other } = props;
console.log('current', value)
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<div>{children}</div>
</Box>
)}
</div>
);
}
return (
<div>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs
value={currentTab}
onChange={handleChangeTab}
aria-label="wrapped label tabs example"
>
<Tab
value="benefit"
label="Benefit Summary"
/>
<Tab value="request" label="Request Penjaminan" />
</Tabs>
<TabPanel value={currentTab} index={'benefit'}>
<Grid container spacing={2}>
{ member && member?.current_plan?.corporate_benefits?.map((corporateBenefit, index) => {return (
<Grid item sm={6} key={index}>
<Card sx={{p: 2}}>
<Typography variant="body1" sx={{fontWeight: 500}}>{corporateBenefit.benefit.code}</Typography>
<Typography variant="body2">{corporateBenefit.benefit.description}</Typography>
<Typography variant="body2" sx={{ marginTop: 2 }}>Yearly Limits</Typography>
<BorderLinearProgress variant="determinate" value={100 - (corporateBenefit.limit_amount ? ((corporateBenefit.usage ?? 0) / corporateBenefit.limit_amount) : 0)} sx={{ mb: 1 }} />
<Typography sx={{ textAlign: 'right'}}>{corporateBenefit.usage ?? 0} / {fCurrency(corporateBenefit.limit_amount ?? 0)}</Typography>
</Card>
</Grid>
)})}
</Grid>
</TabPanel>
<TabPanel value={currentTab} index={'request'}>
<FormRequestClaim member={member} handleSubmitSuccess={closeDialog} />
</TabPanel>
</Box>
</div>
)
}

View File

@@ -0,0 +1,158 @@
import { styled } from '@mui/material/styles';
import Iconify from '@/components/Iconify';
import { fCurrency } from '@/utils/formatNumber';
import { LoadingButton } from '@mui/lab';
import { Avatar, Divider, LinearProgress, linearProgressClasses } from '@mui/material';
import { Card } from '@mui/material';
import { Stack, Typography } from '@mui/material';
import { fPostFormat } from '@/utils/formatTime';
import axios from '@/utils/axios';
import { enqueueSnackbar } from 'notistack';
import { useRef, useState } from 'react';
// TODO Fix any
export default function FormRequestClaim({ member, handleSubmitSuccess }) {
const [submitLoading, setSubmitLoading] = useState(false)
const fileResultInput = useRef<HTMLInputElement>(null);
const [filesResult, setFilesResult] = useState([]);
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 handleImportButton = () => {
if (fileResultInput?.current) {
fileResultInput.current ? fileResultInput.current.click() : console.log('No File selected');
} else {
alert('No file selected');
}
};
const handleResultInputChange = (event) => {
if (event.target.files[0]) {
// console.log('pushing', event.target.files[0])
// let currentFiles = filesResult;
// currentFiles.push(event.target.files[0])
setFilesResult([...filesResult, ...event.target.files])
} else {
console.log('NO FILE')
}
}
const removeFiles = (filesState, index) => {
setFilesResult(filesState.filter((file, fileIndex) => {
console.log('looing through', fileIndex)
return fileIndex != index;
}))
}
function submitRequest() {
setSubmitLoading(true)
axios.post('/claim-requests', {
'member_id' : member.id,
})
.then((response) => {
enqueueSnackbar(response.data.message ?? 'Berhasil membuat data', {variant: 'success'})
handleSubmitSuccess()
})
.catch(({response}) => {
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', {variant: 'error'});
})
.then(() => {
setSubmitLoading(false)
})
}
return (
<Stack>
<Stack direction="row" justifyContent={'end'} sx={{ marginBottom: 2}}>
<Typography textAlign={'right'}>
Submission Date : <br /> {fPostFormat(new Date(), 'dd/MM/yyyy')}
</Typography>
</Stack>
<Card sx={{ p: 1, background: '#f4f6f8', marginBottom: 2 }}>
<Stack direction="row">
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
alt={member?.full_name ?? ''}
sx={{ marginTop: 1, width: 48, height: 48 }}
/>
<Stack sx={{ p: 1 }}>
<Typography>{member?.full_name ?? ''}</Typography>
<Typography>{member?.member_id ?? ''}</Typography>
</Stack>
</Stack>
</Card>
<Card sx={{ paddingY: 1, paddingX: 2 }}>
<Typography variant="body1" sx={{ marginBottom: 1, fontWeight: 600 }}>
Total Limit
</Typography>
<BorderLinearProgress
variant="determinate"
value={
100 -
(member?.current_plan?.limit_rules
? (member?.current_plan?.usage ?? 0) / member?.current_plan?.limit_rules
: 0)
}
/>
<Typography sx={{ textAlign: 'right', marginTop: 1 }}>
{fCurrency(member?.current_plan?.usage ?? 0)} /{' '}
{fCurrency(member?.current_plan?.limit_rules ?? 0)}
</Typography>
</Card>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
Hasil Penunjang
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{filesResult && filesResult.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography>{file.name}</Typography>
<Iconify icon="eva:trash-2-outline" color={'darkred'} onClick={() => {removeFiles(filesResult, index)}}></Iconify>
</Stack>
))}
{/* <Stack direction="row" justifyContent={'space-between'}>
<Typography>Nama File .pdf</Typography>
<Iconify icon="eva:trash-2-outline" color={'darkred'}></Iconify>
</Stack> */}
</Stack>
{/* { JSON.stringify(filesResult) } */}
<input
type="file"
id="file"
ref={fileResultInput}
style={{ display: 'none' }}
multiple
onChange={handleResultInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
<LoadingButton variant="outlined" onClick={() => {fileResultInput.current.click()}}>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
<LoadingButton variant="contained" sx={{ marginTop: 2, p: 2 }} onClick={() => {submitRequest()}} loading={submitLoading}>
LOG Request
</LoadingButton>
</Stack>
);
}

View File

@@ -0,0 +1,109 @@
// @mui
import { styled } from '@mui/material/styles';
import { Button, Card, Divider, Link, Typography, Stack } from '@mui/material';
import { ChevronRight } from '@mui/icons-material';
// components
import Iconify from '@/components/Iconify';
// ----------------------------------------------------------------------
const RootStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: '1rem 0.5rem',
color: 'black',
backgroundColor: theme.palette.grey[200],
maxHeight: '250px',
}));
const ItemStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(1),
borderRadius: 0.5,
color: 'black',
height: '180px',
}));
const itemList = [
{ info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '08:00 WIB' },
{ info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '09:00 WIB' },
{ info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '10:00 WIB' },
{ info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '11:00 WIB' },
];
// ----------------------------------------------------------------------
export default function NotificationCard() {
return (
<RootStyle>
<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
<div
style={{
width: '12px',
height: '12px',
backgroundColor: '#19BBBB',
marginLeft: '0.5rem',
borderRadius: '50%',
}}
/>
</Typography>
</Typography>
<Button sx={{ typography: 'body2' }} endIcon={<ChevronRight />}>
View All
</Button>
</Stack>
<Stack sx={{ marginTop: 2 }}>
<ItemStyle>
{itemList.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={() => {
alert('Info Detail');
}}
>
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>
))}
</ItemStyle>
</Stack>
{/* <BorderLinearProgress variant="determinate" value={50} sx={{ mb: 1 }} />
<Stack sx={{ backgroundColor: '#B2E8E8', paddingY: 1, paddingX: 1.5, mb: 2 }}>
<Typography sx={{ typography: 'caption' }}>Lock Fund ( 25% )</Typography>
<Typography sx={{ typography: 'caption' }}>125.000.000 / 125.000.000</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Button variant="outlined" fullWidth={true}>
Submit Claim
</Button>
<Button variant="contained" startIcon={<Payments />} fullWidth={true}>
Top Up
</Button>
</Stack> */}
</RootStyle>
);
}

View File

@@ -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 }}>
&nbsp;than last month
</Typography>
</div>
</Stack>
<ReactApexChart type="area" series={CHART_DATA} options={chartOptions} height={100} />
</RootStyle>
);
}

View File

@@ -0,0 +1,448 @@
/* ---------------------------------- @mui ---------------------------------- */
import { styled } from '@mui/material/styles';
import {
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Button,
TableSortLabel,
Box,
IconButton,
Card,
Grid,
FormControl,
InputLabel,
Select,
MenuItem,
SelectChangeEvent,
Stack,
Typography,
LinearProgress,
linearProgressClasses,
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
/* ---------------------------------- axios --------------------------------- */
import axios from '@/utils/axios';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '@/components/Iconify';
import BaseTablePagination from '@/components/BaseTablePagination';
/* ---------------------------------- theme --------------------------------- */
import palette from '@/theme/palette';
import { useSearchParams } from 'react-router-dom';
// import { UserCurrentCorporateContext } from '@/contexts/UserCurrentCorporate';
import { fSplit } from '@/utils/formatNumber';
/* ---------------------------------- types --------------------------------- */
type PaginationTableProps = {
current_page: number;
from: number;
last_page: number;
links: [];
path: string;
per_page: number;
to: number;
total: number;
};
type DataTableProps = {
fullName: string;
memberId: string;
division: string;
limit: {
current: number;
total: number;
percentage: number;
};
status: number;
};
type Order = 'asc' | 'desc';
interface HeadCell {
id: string;
align: string;
label: string;
isSort: boolean;
}
interface EnhancedTableProps {
onRequestSort: (event: React.MouseEvent<unknown>, property: string) => void;
order: Order;
orderBy: string;
}
type DivisionDataProps = {
id: number;
name: string;
};
/* -------------------------------------------------------------------------- */
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: '#D1F1F1',
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: theme.palette.primary.main,
},
}));
/* -------------------------------------------------------------------------- */
/* -------------------------- enchanced table head -------------------------- */
const headCells: readonly HeadCell[] = [
{
id: 'code',
align: 'left',
label: 'Request Code',
isSort: true,
},
{
id: 'member.name',
align: 'center',
label: 'Member',
isSort: true,
},
{
id: 'submission_date',
align: 'center',
label: 'Submission Date',
isSort: true,
},
{
id: 'log_url',
align: 'right',
label: 'Download LOG',
isSort: false,
},
{
id: 'status',
align: 'right',
label: 'Status',
isSort: true,
},
];
function EnhancedTableHead({ order, orderBy, onRequestSort }: EnhancedTableProps) {
const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
sortDirection={orderBy === headCell.id ? order : false}
// @ts-ignore
align={headCell.align}
sx={{ padding: 2 }}
>
{headCell.isSort ? (
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</Box>
) : null}
</TableSortLabel>
) : (
headCell.label
)}
</TableCell>
))}
<TableCell align="center">{''}</TableCell>
</TableRow>
</TableHead>
);
}
/* -------------------------------------------------------------------------- */
export default function TableList(props: any) {
const [dataTable, setDataTable] = useState([]);
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
current_page: 0,
from: 0,
last_page: 0,
links: [],
path: '',
per_page: 0,
to: 0,
total: 0,
});
const [isLoading, setIsLoading] = useState(true);
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const [order, setOrder] = useState<Order>('asc');
const [orderBy, setOrderBy] = useState('name');
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
/* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
const isAsc = orderBy === property && order === 'asc';
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(property);
const params = Object.fromEntries([
...searchParams.entries(),
['order', isAsc ? 'desc' : 'asc'],
['orderBy', property],
]);
setAppliedParams(params);
};
/* -------------------------------------------------------------------------- */
/* ----------------------------- division field ----------------------------- */
const [divisionValue, setDivisionValue] = useState('all');
const [divisionData, setDivisionData] = useState([]);
const handleDivisionChange = (event: SelectChangeEvent) => {
setDivisionValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('division');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['division', event.target.value as string],
]);
setAppliedParams(params);
}
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ Search field ------------------------------ */
const [searchText, setSearchText] = useState('');
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setIsLoading(true);
if (searchText === '') {
searchParams.delete('search');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
setAppliedParams(params);
}
await new Promise((resolve) => setTimeout(resolve, 500));
setIsLoading(false);
};
/* -------------------------------------------------------------------------- */
/* ------------------------ button change pagination ------------------------ */
const onPageChangeHandle = async (
event: React.MouseEvent<HTMLButtonElement> | null,
newPage: number
) => {
setIsLoading(true);
const params = Object.fromEntries([...searchParams.entries(), ['page', newPage + 1]]);
setPage(newPage);
await new Promise((resolve) => setTimeout(resolve, 500));
setAppliedParams(params);
setIsLoading(false);
};
/* -------------------------------------------------------------------------- */
/* --------------------------- row page per limit --------------------------- */
const onRowsPerPageChangeHandle = async (event: React.ChangeEvent<HTMLInputElement>) => {
setIsLoading(true);
searchParams.delete('page');
const params = Object.fromEntries([
...searchParams.entries(),
['per_page', parseInt(event.target.value, 10)],
]);
setRowsPerPage(parseInt(event.target.value, 10));
await new Promise((resolve) => setTimeout(resolve, 500));
setAppliedParams(params);
setIsLoading(false);
};
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
setIsLoading(true);
// const division = await axios.get(`${corporateValue}/division`);
// setDivisionData(division.data);
const params =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
const response = await axios.get(`/claim-requests`, {
params: { ...params, claimMember: false },
});
setSearchParams(params);
setDataTable(response.data.data.data);
setPaginationTable(response.data.data);
setRowsPerPage(response.data.data.per_page);
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams]);
return (
<Card>
<Grid container>
{/* Field 1 */}
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Grid container spacing={2}>
<Grid item xs={12} lg={3} xl={2}>
{/* <FormControl fullWidth>
<InputLabel id="simple-division-select-lable">Division</InputLabel>
<Select
labelId="simple-division-select-lable"
id="division-select-lable"
value={divisionValue}
label="Division"
onChange={handleDivisionChange}
>
<MenuItem value="all">All</MenuItem>
{divisionData.map((row: DivisionDataProps, index) => (
<MenuItem key={index} value={row.id}>
{row.name}
</MenuItem>
))}
</Select>
</FormControl> */}
</Grid>
<Grid item xs={12}>
<form onSubmit={handleSearchSubmit}>
<TextField
id="search-input"
label="Search"
variant="outlined"
onChange={(event) => setSearchText(event.target.value)}
value={searchText}
fullWidth
/>
</form>
</Grid>
</Grid>
</Grid>
{/* End Field 1 */}
{/* Field 2 */}
<Grid item xs={12}>
<TableContainer component={Paper}>
<Table aria-label="collapsible table" size="small">
<EnhancedTableHead
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
/>
<TableBody>
{isLoading ? (
<TableRow>
<TableCell colSpan={6} align="center">
Loading . . .
</TableCell>
</TableRow>
) : dataTable.length >= 1 ? (
dataTable.map((row: DataTableProps, index) => (
<TableRow key={index}>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="center">{row.member?.full_name ?? ''}</TableCell>
<TableCell align="center">{row.submission_date}</TableCell>
<TableCell align="right">{ row.log_url ? (
<Button
sx={{
backgroundColor: palette.light.grey[400],
color: palette.dark.grey[900],
paddingY: 0,
'&:hover': {
backgroundColor: 'rgba(84, 214, 44, 0.32)',
color: palette.dark.success.darker,
},
}}
>
Download LOG
</Button>
) : (
<Typography>Belum Tersedia</Typography>
)}
</TableCell>
<TableCell align="right">
{row.status == 'requested' ? (
<Button
sx={{
backgroundColor: 'rgba(84, 214, 44, 0.16)',
color: palette.dark.success.dark,
paddingY: 0,
'&:hover': {
backgroundColor: 'rgba(84, 214, 44, 0.32)',
color: palette.dark.success.darker,
},
}}
>
Requested
</Button>
) : (
<Button
sx={{
backgroundColor: 'rgba(255, 72, 66, 0.16)',
color: palette.dark.error.dark,
paddingY: 0,
'&:hover': {
backgroundColor: 'rgba(255, 72, 66, 0.32)',
color: palette.dark.error.darker,
},
}}
>
Declined
</Button>
)}
</TableCell>
{/* <TableCell align="right">
<IconButton>
<Iconify icon="ic:baseline-more-vert" />
</IconButton>
</TableCell> */}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={6} align="center">
No Data Found
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
{/* Pagination */}
<BaseTablePagination
count={paginationTable.total}
onPageChange={onPageChangeHandle}
page={page}
rowsPerPage={rowsPerPage}
onRowsPerPageChange={onRowsPerPageChangeHandle}
/>
</Grid>
{/* End Field 2 */}
</Grid>
</Card>
);
}