LMSN-217
Update slicing hospital portal
This commit is contained in:
@@ -16,6 +16,7 @@ use Illuminate\Routing\Controller;
|
|||||||
use Modules\HospitalPortal\Transformers\ClaimRequestResource;
|
use Modules\HospitalPortal\Transformers\ClaimRequestResource;
|
||||||
use Modules\HospitalPortal\Transformers\ClaimRequestShowResource;
|
use Modules\HospitalPortal\Transformers\ClaimRequestShowResource;
|
||||||
use PDF;
|
use PDF;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class ClaimRequestController extends Controller
|
class ClaimRequestController extends Controller
|
||||||
{
|
{
|
||||||
@@ -208,4 +209,114 @@ class ClaimRequestController extends Controller
|
|||||||
// Menghasilkan kode dengan format yang diinginkan
|
// Menghasilkan kode dengan format yang diinginkan
|
||||||
return self::$code_prefix . '-' . str_pad($next_number, 5, '0', STR_PAD_LEFT);
|
return self::$code_prefix . '-' . str_pad($next_number, 5, '0', STR_PAD_LEFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function get_claim_requests(Request $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
$limit = $request->has('per_page') ? $request->input('per_page') : 10;
|
||||||
|
|
||||||
|
$results = DB::table('claim_requests')
|
||||||
|
->leftJoin('claims', 'claim_requests.id', '=', 'claims.claim_request_id')
|
||||||
|
->leftJoin('members', 'claim_requests.member_id', '=', 'members.id')
|
||||||
|
->leftJoin('corporate_employees', 'members.id', '=', 'corporate_employees.member_id')
|
||||||
|
->leftJoin('corporate_divisions', 'corporate_employees.division_id', '=', 'corporate_divisions.id')
|
||||||
|
->when($request->input('search'), function ($query, $search) {
|
||||||
|
$query->where(function ($query) use ($search) {
|
||||||
|
$query->orWhere('claim_requests.code', 'like', "%" . $search . "%")
|
||||||
|
->orWhere('members.member_id', 'like', "%" . $search . "%")
|
||||||
|
->orWhere('members.name', 'like', "%" . $search . "%")
|
||||||
|
->orWhere('corporate_divisions.name', 'like', "%" . $search . "%")
|
||||||
|
->orWhere('claim_requests.status', 'like', "%" . $search . "%")
|
||||||
|
->orWhere('claim_requests.submission_date', 'like', "%" . $search . "%");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
->when($request->has('orderBy'), function ($query) use ($request) {
|
||||||
|
$orderBy = $request->orderBy;
|
||||||
|
$direction = $request->order ?? 'asc';
|
||||||
|
|
||||||
|
$query->orderBy($orderBy, $direction);
|
||||||
|
})
|
||||||
|
->select('members.id', 'claim_requests.code','members.member_id', 'members.name as full_name', 'corporate_divisions.name AS division_name',
|
||||||
|
DB::raw('
|
||||||
|
CASE
|
||||||
|
WHEN claim_requests.status = "requested" THEN "requested"
|
||||||
|
WHEN claim_requests.status = "approved" AND claims.status = "approved" THEN "approved"
|
||||||
|
WHEN claim_requests.status = "approved" AND claims.status = "declined" THEN "declined"
|
||||||
|
WHEN claim_requests.status = "approved" AND claims.status = "disbrusmented" THEN "disbrusmented"
|
||||||
|
/*WHEN claim_requests.status = "approved" AND claims.status = "received" THEN "pending"*/
|
||||||
|
WHEN claim_requests.status = "approved" AND claims.status = "received" THEN "reviewed"
|
||||||
|
ELSE ""
|
||||||
|
END AS status
|
||||||
|
'),
|
||||||
|
'claim_requests.id AS claim_request_id', 'claim_requests.submission_date')
|
||||||
|
->paginate($limit);
|
||||||
|
return response()->json(Helper::paginateResources($results));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function detail_claim_requests($claimRequestId)
|
||||||
|
{
|
||||||
|
|
||||||
|
$status = DB::table('claim_requests')
|
||||||
|
->leftJoin('claims', 'claim_requests.id', '=', 'claims.claim_request_id')
|
||||||
|
->leftJoin('members', 'claim_requests.member_id', '=', 'members.id')
|
||||||
|
->leftJoin('corporate_employees', 'members.id', '=', 'corporate_employees.member_id')
|
||||||
|
->leftJoin('corporate_divisions', 'corporate_employees.division_id', '=', 'corporate_divisions.id')
|
||||||
|
->where('claim_requests.id', '=', $claimRequestId)
|
||||||
|
->select(
|
||||||
|
'claim_requests.submission_date',
|
||||||
|
DB::raw('
|
||||||
|
CASE
|
||||||
|
WHEN claim_requests.status = "requested" THEN "requested"
|
||||||
|
WHEN claim_requests.status = "approved" AND claims.status = "approved" THEN "approved"
|
||||||
|
WHEN claim_requests.status = "approved" AND claims.status = "declined" THEN "declined"
|
||||||
|
WHEN claim_requests.status = "approved" AND claims.status = "disbrusmented" THEN "disbrusmented"
|
||||||
|
/*WHEN claim_requests.status = "approved" AND claims.status = "received" THEN "pending"*/
|
||||||
|
WHEN claim_requests.status = "approved" AND claims.status = "received" THEN "reviewed"
|
||||||
|
ELSE ""
|
||||||
|
END AS status
|
||||||
|
')
|
||||||
|
)
|
||||||
|
->first();
|
||||||
|
$results['status'] = $status;
|
||||||
|
$timeline = DB::table('claim_logs')
|
||||||
|
->where('claim_logs.claim_request_id', '=', $claimRequestId)
|
||||||
|
->select(
|
||||||
|
DB::raw('
|
||||||
|
CASE
|
||||||
|
WHEN claim_logs.status = "requested" THEN "Request"
|
||||||
|
WHEN claim_logs.status = "reviewed" THEN "Review"
|
||||||
|
WHEN claim_logs.status = "approved" THEN "Approval"
|
||||||
|
ELSE "-"
|
||||||
|
END AS txt_status
|
||||||
|
'),
|
||||||
|
DB::raw('
|
||||||
|
CASE
|
||||||
|
WHEN claim_logs.status = "requested" THEN "#159C9C"
|
||||||
|
WHEN claim_logs.status = "reviewed" THEN "#0C53B7"
|
||||||
|
WHEN claim_logs.status = "approved" THEN "#229A16"
|
||||||
|
ELSE "-"
|
||||||
|
END AS txt_status_color
|
||||||
|
'),
|
||||||
|
DB::raw('
|
||||||
|
CASE
|
||||||
|
WHEN claim_logs.status = "requested" THEN "#00AB5529"
|
||||||
|
WHEN claim_logs.status = "reviewed" THEN "#1890FF29"
|
||||||
|
WHEN claim_logs.status = "approved" THEN "#54D62C29"
|
||||||
|
ELSE "-"
|
||||||
|
END AS txt_status_backgroundColor
|
||||||
|
'),
|
||||||
|
'claim_logs.date',
|
||||||
|
'claim_logs.description',
|
||||||
|
'claim_logs.status'
|
||||||
|
)
|
||||||
|
->orderBy('claim_logs.id', 'desc')
|
||||||
|
->get();
|
||||||
|
$results['timeline'] = $timeline;
|
||||||
|
$request_files = DB::table('claim_request_files')
|
||||||
|
->where('claim_request_files.claim_request_id', '=', $claimRequestId)
|
||||||
|
->get();
|
||||||
|
$results['request_files'] = $request_files;
|
||||||
|
|
||||||
|
return Helper::responseJson($results);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ Route::prefix('v1')->group(function() {
|
|||||||
Route::post('claim-requests', [ClaimRequestController::class, 'store'])->name('claim-requests.store');
|
Route::post('claim-requests', [ClaimRequestController::class, 'store'])->name('claim-requests.store');
|
||||||
Route::get('claim-requests/{claim_request_id}/log', [ClaimRequestController::class, 'generateLog'])->name('claim-requests.generate-log');
|
Route::get('claim-requests/{claim_request_id}/log', [ClaimRequestController::class, 'generateLog'])->name('claim-requests.generate-log');
|
||||||
Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show');
|
Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show');
|
||||||
|
Route::get('get-claim-requests', [ClaimRequestController::class, 'get_claim_requests'])->name('claim-requests.get_claim_requests');
|
||||||
|
Route::get('detail-claim-requests/{id}', [ClaimRequestController::class, 'detail_claim_requests'])->name('claim-requests.detail_claim_requests');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
98
frontend/hospital-portal/src/components/Label.tsx
Normal file
98
frontend/hospital-portal/src/components/Label.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// @mui
|
||||||
|
import { alpha, Theme, useTheme, styled } from '@mui/material/styles';
|
||||||
|
import { BoxProps } from '@mui/material';
|
||||||
|
// theme
|
||||||
|
import { ColorSchema } from '../theme/palette';
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
type LabelColor = 'default' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error';
|
||||||
|
|
||||||
|
type LabelVariant = 'filled' | 'outlined' | 'ghost';
|
||||||
|
|
||||||
|
const RootStyle = styled('span')(
|
||||||
|
({
|
||||||
|
theme,
|
||||||
|
ownerState,
|
||||||
|
}: {
|
||||||
|
theme: Theme;
|
||||||
|
ownerState: {
|
||||||
|
color: LabelColor;
|
||||||
|
variant: LabelVariant;
|
||||||
|
};
|
||||||
|
}) => {
|
||||||
|
const isLight = theme.palette.mode === 'light';
|
||||||
|
const { color, variant } = ownerState;
|
||||||
|
|
||||||
|
const styleFilled = (color: ColorSchema) => ({
|
||||||
|
color: theme.palette[color].contrastText,
|
||||||
|
backgroundColor: theme.palette[color].main,
|
||||||
|
});
|
||||||
|
|
||||||
|
const styleOutlined = (color: ColorSchema) => ({
|
||||||
|
color: theme.palette[color].main,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
border: `1px solid ${theme.palette[color].main}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const styleGhost = (color: ColorSchema) => ({
|
||||||
|
color: theme.palette[color][isLight ? 'dark' : 'light'],
|
||||||
|
backgroundColor: alpha(theme.palette[color].main, 0.16),
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
height: 22,
|
||||||
|
minWidth: 22,
|
||||||
|
lineHeight: 0,
|
||||||
|
borderRadius: 6,
|
||||||
|
// cursor: 'default',
|
||||||
|
alignItems: 'center',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
display: 'inline-flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: theme.spacing(0, 1),
|
||||||
|
color: theme.palette.grey[800],
|
||||||
|
fontSize: theme.typography.pxToRem(12),
|
||||||
|
fontFamily: theme.typography.fontFamily,
|
||||||
|
backgroundColor: theme.palette.grey[300],
|
||||||
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
|
|
||||||
|
...(color !== 'default'
|
||||||
|
? {
|
||||||
|
...(variant === 'filled' && { ...styleFilled(color) }),
|
||||||
|
...(variant === 'outlined' && { ...styleOutlined(color) }),
|
||||||
|
...(variant === 'ghost' && { ...styleGhost(color) }),
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...(variant === 'outlined' && {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
border: `1px solid ${theme.palette.grey[500_32]}`,
|
||||||
|
}),
|
||||||
|
...(variant === 'ghost' && {
|
||||||
|
color: isLight ? theme.palette.text.secondary : theme.palette.common.white,
|
||||||
|
backgroundColor: theme.palette.grey[500_16],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
interface Props extends BoxProps {
|
||||||
|
color?: LabelColor;
|
||||||
|
variant?: LabelVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Label({ color = 'default', variant = 'ghost', children, sx }: Props) {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RootStyle ownerState={{ color, variant }} sx={sx} theme={theme}>
|
||||||
|
{children}
|
||||||
|
</RootStyle>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
387
frontend/hospital-portal/src/components/Table.tsx
Normal file
387
frontend/hospital-portal/src/components/Table.tsx
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
/* ---------------------------------- @mui ---------------------------------- */
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import {
|
||||||
|
Paper,
|
||||||
|
Table as TableContent,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
TextField,
|
||||||
|
Button,
|
||||||
|
TableSortLabel,
|
||||||
|
Box,
|
||||||
|
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 { Fragment, useContext, useEffect, useState } from 'react';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
/* -------------------------------- component ------------------------------- */
|
||||||
|
import BaseTablePagination from './BaseTablePagination';
|
||||||
|
/* ---------------------------------- theme --------------------------------- */
|
||||||
|
import palette from '../theme/palette';
|
||||||
|
/* ---------------------------------- utils --------------------------------- */
|
||||||
|
import { UserCurrentCorporateContext } from '../contexts/UserCurrentCorporate';
|
||||||
|
import { fSplit } from '../utils/formatNumber';
|
||||||
|
import { Download, Search as SearchIcon, Upload } from '@mui/icons-material';
|
||||||
|
/* ---------------------------------- types --------------------------------- */
|
||||||
|
import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '../@types/table';
|
||||||
|
import { InputAdornment } from '@mui/material';
|
||||||
|
import GetAppIcon from '@mui/icons-material/GetApp';
|
||||||
|
/* --------------------------------- styled --------------------------------- */
|
||||||
|
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
|
||||||
|
height: 10,
|
||||||
|
borderRadius: 6,
|
||||||
|
[`&.${linearProgressClasses.colorPrimary}`]: {
|
||||||
|
backgroundColor: '#D1F1F1',
|
||||||
|
},
|
||||||
|
[`& .${linearProgressClasses.bar}`]: {
|
||||||
|
borderRadius: 6,
|
||||||
|
backgroundColor: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
export default function Table<T>({
|
||||||
|
headCells,
|
||||||
|
rows,
|
||||||
|
paginations,
|
||||||
|
orders,
|
||||||
|
loadings,
|
||||||
|
params,
|
||||||
|
filters,
|
||||||
|
filterStatus,
|
||||||
|
filterStartDate,
|
||||||
|
filterEndDate,
|
||||||
|
searchs,
|
||||||
|
exportReport,
|
||||||
|
}: TableListProps<T>) {
|
||||||
|
/* ------------------------------- handle sort ------------------------------ */
|
||||||
|
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
|
||||||
|
const isAsc = orders?.orderBy === property && orders?.order === 'asc';
|
||||||
|
|
||||||
|
orders?.setOrder(isAsc ? 'desc' : 'asc');
|
||||||
|
orders?.setOrderBy(property);
|
||||||
|
const parameters = Object.fromEntries([
|
||||||
|
...params.searchParams.entries(),
|
||||||
|
['order', isAsc ? 'desc' : 'asc'],
|
||||||
|
['orderBy', property],
|
||||||
|
]);
|
||||||
|
params.setAppliedParams(parameters);
|
||||||
|
};
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* -------------------------- enchanced table head -------------------------- */
|
||||||
|
const EnhancedTableHead = () => {
|
||||||
|
const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
|
||||||
|
handleRequestSort(event, property);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
{headCells &&
|
||||||
|
headCells.map((headCell, index) => (
|
||||||
|
<TableCell
|
||||||
|
key={index}
|
||||||
|
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
|
||||||
|
// @ts-ignore
|
||||||
|
align={headCell.align}
|
||||||
|
sx={{ padding: 2 }}
|
||||||
|
width={headCell.width ? headCell.width : 'auto'}
|
||||||
|
>
|
||||||
|
{headCell.isSort ? (
|
||||||
|
<TableSortLabel
|
||||||
|
active={orders?.orderBy === headCell.id}
|
||||||
|
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
|
||||||
|
onClick={createSortHandler(headCell.id)}
|
||||||
|
>
|
||||||
|
{headCell.label}
|
||||||
|
{orders?.orderBy === headCell.id ? (
|
||||||
|
<Box component="span" sx={visuallyHidden}>
|
||||||
|
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
|
</TableSortLabel>
|
||||||
|
) : (
|
||||||
|
headCell.label
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* ------------------------ button change pagination ------------------------ */
|
||||||
|
const onPageChangeHandle = async (
|
||||||
|
event: React.MouseEvent<HTMLButtonElement> | null,
|
||||||
|
newPage: number
|
||||||
|
) => {
|
||||||
|
const parameters = Object.fromEntries([
|
||||||
|
...params.searchParams.entries(),
|
||||||
|
['page', newPage + 1],
|
||||||
|
['per_page', paginations.rowsPerPage]
|
||||||
|
]);
|
||||||
|
paginations.setPage(newPage);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
|
params.setAppliedParams(parameters);
|
||||||
|
};
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* --------------------------- row page per limit --------------------------- */
|
||||||
|
const onRowsPerPageChangeHandle = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
params.searchParams.delete('page');
|
||||||
|
const parameters = Object.fromEntries([
|
||||||
|
...params.searchParams.entries(),
|
||||||
|
['per_page', parseInt(event.target.value, 10)],
|
||||||
|
]);
|
||||||
|
|
||||||
|
paginations.setPage(0);
|
||||||
|
paginations.setRowsPerPage(parseInt(event.target.value, 10));
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
|
params.setAppliedParams(parameters);
|
||||||
|
};
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
return (
|
||||||
|
// <Card>
|
||||||
|
<Grid container>
|
||||||
|
{/* Field 1 */}
|
||||||
|
<Grid item xs={12} paddingX="24px" paddingY="20px">
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{filters && filters.useFilter ? (
|
||||||
|
<Fragment>
|
||||||
|
<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={filters.config.divisionValue}
|
||||||
|
label="Division"
|
||||||
|
onChange={filters.config.handleDivisionChange}
|
||||||
|
>
|
||||||
|
<MenuItem value="all">All</MenuItem>
|
||||||
|
{filters.config.divisionData.map((row: DivisionDataProps, index) => (
|
||||||
|
<MenuItem key={index} value={row.id}>
|
||||||
|
{row.name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} lg={9} xl={10}>
|
||||||
|
<form onSubmit={searchs.handleSearchSubmit}>
|
||||||
|
<TextField
|
||||||
|
id="search-input"
|
||||||
|
label="Search"
|
||||||
|
variant="outlined"
|
||||||
|
onChange={(event) => searchs.setSearchText(event.target.value)}
|
||||||
|
value={searchs.searchText}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Grid>
|
||||||
|
</Fragment>
|
||||||
|
) : null }
|
||||||
|
|
||||||
|
{searchs && searchs.useSearchs ? (
|
||||||
|
<Fragment>
|
||||||
|
{filterStatus && filterStatus.useFilter ? (
|
||||||
|
<Grid item xs={12} lg={4} xl={4}>
|
||||||
|
<form onSubmit={searchs.handleSearchSubmit}>
|
||||||
|
<TextField
|
||||||
|
id="search-input"
|
||||||
|
variant="outlined"
|
||||||
|
onChange={(event) => searchs.setSearchText(event.target.value)}
|
||||||
|
value={searchs.searchText}
|
||||||
|
fullWidth
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
placeholder="Search Name or Member ID... "
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Grid>
|
||||||
|
) :
|
||||||
|
<Grid item xs={12} lg={6} xl={6}>
|
||||||
|
<form onSubmit={searchs.handleSearchSubmit}>
|
||||||
|
<TextField
|
||||||
|
id="search-input"
|
||||||
|
variant="outlined"
|
||||||
|
onChange={(event) => searchs.setSearchText(event.target.value)}
|
||||||
|
value={searchs.searchText}
|
||||||
|
fullWidth
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
placeholder="Search Name or Member ID... "
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Grid>
|
||||||
|
}
|
||||||
|
|
||||||
|
</Fragment>
|
||||||
|
) : null }
|
||||||
|
|
||||||
|
{/* Start date */}
|
||||||
|
{filterStartDate && filterStartDate.useFilter ? (
|
||||||
|
<Grid item xs={12} lg={2} xl={2}>
|
||||||
|
<form onChange={filterStartDate.handleStartDateChange}>
|
||||||
|
<TextField
|
||||||
|
id="date-input"
|
||||||
|
type="date"
|
||||||
|
variant="outlined"
|
||||||
|
value={filterStartDate.startDate}
|
||||||
|
onChange={(event) => filterStartDate.setStartDate(event.target.value)}
|
||||||
|
fullWidth
|
||||||
|
label="Start Date"
|
||||||
|
InputLabelProps={{
|
||||||
|
shrink: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Grid>
|
||||||
|
) : null }
|
||||||
|
|
||||||
|
{/* End Date */}
|
||||||
|
|
||||||
|
{filterEndDate && filterEndDate.useFilter ? (
|
||||||
|
<Grid item xs={12} lg={2} xl={2}>
|
||||||
|
<form onChange={filterEndDate.handleEndDateChange}>
|
||||||
|
<TextField
|
||||||
|
id="date-input"
|
||||||
|
type="date"
|
||||||
|
variant="outlined"
|
||||||
|
value={filterEndDate.endDate}
|
||||||
|
onChange={(event) => filterEndDate.setEndDate(event.target.value)}
|
||||||
|
fullWidth
|
||||||
|
label="End Date"
|
||||||
|
InputLabelProps={{
|
||||||
|
shrink: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Grid>
|
||||||
|
) : null }
|
||||||
|
|
||||||
|
{/* Filter status */}
|
||||||
|
{filterStatus && filterStatus.useFilter ? (
|
||||||
|
<Grid item xs={12} lg={2} xl={2}>
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id="simple-status-select-lable">Status</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="simple-status-select-lable"
|
||||||
|
id="status-select-lable"
|
||||||
|
value={filterStatus.config.statusValue}
|
||||||
|
label="Status"
|
||||||
|
onChange={filterStatus.config.handleStatusChange}
|
||||||
|
>
|
||||||
|
<MenuItem value="all">All</MenuItem>
|
||||||
|
{filterStatus.config.filterData.map((row: StatusDataProps, index) => (
|
||||||
|
<MenuItem key={index} value={row.id}>
|
||||||
|
{row.name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
) : null }
|
||||||
|
|
||||||
|
{/* Export Report */}
|
||||||
|
|
||||||
|
{exportReport && exportReport.useExport ? (
|
||||||
|
<Grid item xs={12} lg={2} xl={2}>
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<Button variant='contained' sx={{p:2}}>
|
||||||
|
<Download />
|
||||||
|
<Typography variant='inherit' sx={{marginLeft: 1}}>Export</Typography>
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
) : null }
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
{/* End Field 1 */}
|
||||||
|
{/* Field 2 */}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
{/* Table */}
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<TableContent aria-label="collapsible table" size="small">
|
||||||
|
{/* Table Header */}
|
||||||
|
<EnhancedTableHead />
|
||||||
|
{/* End Table Header */}
|
||||||
|
{/* Table Body */}
|
||||||
|
<TableBody>
|
||||||
|
{loadings.isLoading && rows.length >= 1 ? (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={headCells?.length} align="center">
|
||||||
|
Loading . . .
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : rows && rows.length >= 1 ? (
|
||||||
|
rows.map((row, rowIndex) => (
|
||||||
|
<TableRow key={rowIndex}>
|
||||||
|
{headCells &&
|
||||||
|
//@ts-ignore
|
||||||
|
headCells.map((head, headIndex) => (
|
||||||
|
//@ts-ignore
|
||||||
|
<TableCell align={head.align} key={headIndex}>
|
||||||
|
{row[head.id]}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={6} align="center">
|
||||||
|
No Data Found
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
{/* End Table Body */}
|
||||||
|
</TableContent>
|
||||||
|
</TableContainer>
|
||||||
|
{/* End Table */}
|
||||||
|
|
||||||
|
{/* Pagination */}
|
||||||
|
<BaseTablePagination
|
||||||
|
count={paginations.paginationTable.total}
|
||||||
|
onPageChange={onPageChangeHandle}
|
||||||
|
page={paginations.page}
|
||||||
|
rowsPerPage={paginations.rowsPerPage}
|
||||||
|
onRowsPerPageChange={onRowsPerPageChangeHandle}
|
||||||
|
/>
|
||||||
|
{/* End Pagination */}
|
||||||
|
</Grid>
|
||||||
|
{/* End Field 2 */}
|
||||||
|
</Grid>
|
||||||
|
// </Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import { Stack } from '@mui/system';
|
|||||||
import { Input } from '@mui/material';
|
import { Input } from '@mui/material';
|
||||||
//sections
|
//sections
|
||||||
import TableList from '@/sections/dashboard/TableList';
|
import TableList from '@/sections/dashboard/TableList';
|
||||||
|
import TableList2 from '@/sections/dashboard/TableList2';
|
||||||
import { fDate } from '@/utils/formatTime';
|
import { fDate } from '@/utils/formatTime';
|
||||||
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
|
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
|
||||||
|
|
||||||
@@ -93,7 +94,7 @@ export default function Dashboard() {
|
|||||||
<CardNotification data={itemList} />
|
<CardNotification data={itemList} />
|
||||||
</Grid>*/}
|
</Grid>*/}
|
||||||
<Grid item xs={12} lg={12} md={12}>
|
<Grid item xs={12} lg={12} md={12}>
|
||||||
<TableList dataLoaded={handleDataLoaded}/>
|
<TableList2 dataLoaded={handleDataLoaded}/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ export default function Router() {
|
|||||||
path: 'dashboard',
|
path: 'dashboard',
|
||||||
element: <Dashboard />,
|
element: <Dashboard />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/detail/:id',
|
||||||
|
element: <DetailClaimReport />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -98,3 +102,5 @@ const ForgetPassword = Loadable(lazy(() => import('@/pages/auth/ForgetPassword')
|
|||||||
// Dashboard
|
// Dashboard
|
||||||
const Dashboard = Loadable(lazy(() => import('@/pages/Dashboard')));
|
const Dashboard = Loadable(lazy(() => import('@/pages/Dashboard')));
|
||||||
const NotFound = Loadable(lazy(() => import('@/pages/Page404')));
|
const NotFound = Loadable(lazy(() => import('@/pages/Page404')));
|
||||||
|
|
||||||
|
const DetailClaimReport = Loadable(lazy(()=> import('@/sections/dashboard/Detail')));
|
||||||
|
|||||||
67
frontend/hospital-portal/src/sections/dashboard/Detail.tsx
Normal file
67
frontend/hospital-portal/src/sections/dashboard/Detail.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// mui
|
||||||
|
import { Container, Grid, Stack, Typography } from '@mui/material';
|
||||||
|
// components
|
||||||
|
import Page from '../../components/Page';
|
||||||
|
// utils
|
||||||
|
import useSettings from '../../hooks/useSettings';
|
||||||
|
// section
|
||||||
|
import CardFamilyInformation from '../../sections/alarm-center/user-profile/CardFamilyInformation';
|
||||||
|
// react
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import ButtonBack from '../../components/ButtonBack';
|
||||||
|
import { useEffect, useState, useContext } from 'react';
|
||||||
|
import axios from '../../utils/axios';
|
||||||
|
// pages
|
||||||
|
import DetailTimeline from '../../sections/dashboard/DetailTimeline';
|
||||||
|
import DetailStepper from '../../sections/dashboard/DetailStepper';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
export default function Detail() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { themeStretch } = useSettings();
|
||||||
|
const [data, setData] = useState();
|
||||||
|
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
axios
|
||||||
|
.get('/detail-claim-requests/' + id)
|
||||||
|
.then((response) => {
|
||||||
|
setData(response.data);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page title="Detail">
|
||||||
|
<Container maxWidth={themeStretch ? false : 'xl'}>
|
||||||
|
<Stack direction="row" alignItems="center" sx={{ marginBottom: 3 }}>
|
||||||
|
<ArrowBackIosIcon onClick={() => navigate(-1)} sx={{cursor:'pointer'}}/>
|
||||||
|
<Typography variant="h5" sx={{marginLeft:2}}>Detail</Typography>
|
||||||
|
{data ? (
|
||||||
|
<Stack direction="row" spacing={2} ml="auto">
|
||||||
|
<Typography variant="body2" sx={{color: '#757575'}}>Submission Date</Typography>
|
||||||
|
<Typography variant="body2" fontWeight="bold">{(data && data.data) ? format(new Date(data.data.status.submission_date), "d MMM yyyy") : ''}</Typography>
|
||||||
|
</Stack>
|
||||||
|
) : ''}
|
||||||
|
</Stack>
|
||||||
|
{data ? (
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={12} md={12}>
|
||||||
|
<DetailStepper data={data}/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} md={12}>
|
||||||
|
<DetailTimeline data={data}/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
) : ''}
|
||||||
|
</Container>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Stepper from '@mui/material/Stepper';
|
||||||
|
import Step from '@mui/material/Step';
|
||||||
|
import StepLabel from '@mui/material/StepLabel';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import ClearIcon from '@mui/icons-material/Clear';
|
||||||
|
|
||||||
|
const steps = [
|
||||||
|
'Request',
|
||||||
|
'Review',
|
||||||
|
'Approval',
|
||||||
|
'Decline',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function HorizontalLinearAlternativeLabelStepper({data}) {
|
||||||
|
const [active, setActive] = useState(0);
|
||||||
|
const [status, SetStatus] = useState(null);
|
||||||
|
let updatedSteps = [...steps];
|
||||||
|
useEffect(() => {
|
||||||
|
if (data && data.data) {
|
||||||
|
if (data.data.status.status === 'requested') {
|
||||||
|
setActive(1);
|
||||||
|
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
|
||||||
|
}
|
||||||
|
else if (data.data.status.status === 'reviewed') {
|
||||||
|
setActive(2);
|
||||||
|
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
|
||||||
|
}
|
||||||
|
else if (data.data.status.status === 'approved')
|
||||||
|
{
|
||||||
|
setActive(3);
|
||||||
|
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
|
||||||
|
}
|
||||||
|
else if(data.data.status.status === 'declined')
|
||||||
|
{
|
||||||
|
setActive(4)
|
||||||
|
updatedSteps = updatedSteps.filter(step => step !== 'Approval');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SetStatus(updatedSteps);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ width: '100%', marginBottom: 2 }}>
|
||||||
|
<Stepper activeStep={active} alternativeLabel>
|
||||||
|
{status?.map((label) => (
|
||||||
|
<Step key={label}>
|
||||||
|
<StepLabel icon={label==='Decline' ? <ClearIcon sx={{ color: 'white', backgroundColor: 'red', borderRadius: '50%' }} /> : ''}>{label}</StepLabel>
|
||||||
|
</Step>
|
||||||
|
))}
|
||||||
|
</Stepper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Timeline from '@mui/lab/Timeline';
|
||||||
|
import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
|
||||||
|
import TimelineSeparator from '@mui/lab/TimelineSeparator';
|
||||||
|
import TimelineConnector from '@mui/lab/TimelineConnector';
|
||||||
|
import TimelineContent from '@mui/lab/TimelineContent';
|
||||||
|
import TimelineDot from '@mui/lab/TimelineDot';
|
||||||
|
import {Typography, Card, Stack} from '@mui/material';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import Paper from '@mui/material/Paper';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
import Iconify from '../../components/Iconify';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
|
||||||
|
const Item1 = styled(Paper)(({ theme }) => ({
|
||||||
|
...theme.typography.body2,
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
textAlign: 'center',
|
||||||
|
backgroundColor: '#919EAB29',
|
||||||
|
color: '#637381',
|
||||||
|
width: 'fit-content',
|
||||||
|
marginRight: 'auto',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Item2 = styled(Paper)(({ theme }) => ({
|
||||||
|
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
|
||||||
|
...theme.typography.body2,
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
textAlign: 'center',
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
width: 'fit-content',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default function NoOppositeContent({data}) {
|
||||||
|
const [timeline, setTimeline] = useState(null);
|
||||||
|
const [requestFile, setRequestFile] = useState(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (data && data.data) {
|
||||||
|
setTimeline(data.data.timeline);
|
||||||
|
setRequestFile(data.data.request_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [data]);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{timeline?.map((dataTimeline, index) => (
|
||||||
|
<Timeline
|
||||||
|
sx={{
|
||||||
|
[`& .${timelineItemClasses.root}:before`]: {
|
||||||
|
flex: 0,
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" gutterBottom fontWeight="bold">{dataTimeline.date ? format(new Date(dataTimeline.date), "d MMM yyyy") : ''}</Typography>
|
||||||
|
<TimelineItem>
|
||||||
|
<TimelineSeparator>
|
||||||
|
<TimelineDot />
|
||||||
|
<TimelineConnector />
|
||||||
|
</TimelineSeparator>
|
||||||
|
<TimelineContent spacing={3}>
|
||||||
|
<Card sx={{ borderRadius: '6px', paddingY: 2 }}>
|
||||||
|
<Stack sx={{marginLeft: 2, marginRight: 2, marginTop: 2 }}>
|
||||||
|
<Stack direction="row" spacing={2} sx={{marginBottom: 2, paddingBottom: 2, borderBottom: '1px solid #919EAB52' }}>
|
||||||
|
<Item1>{dataTimeline.date ? format(new Date(dataTimeline.date), "HH : ii") : ''}</Item1>
|
||||||
|
<Item2 sx={{backgroundColor: dataTimeline.txt_status_backgroundColor, color: dataTimeline.txt_status_color}}>{dataTimeline.txt_status}</Item2>
|
||||||
|
</Stack>
|
||||||
|
<Stack direction="row" spacing={2} sx={{marginBottom: 2}}>
|
||||||
|
<Typography variant="body2" gutterBottom>Detail:</Typography>
|
||||||
|
<Typography variant="body2" gutterBottom>{dataTimeline.description}</Typography>
|
||||||
|
</Stack>
|
||||||
|
{dataTimeline.status === 'reviewed' && requestFile ? (
|
||||||
|
<>
|
||||||
|
{requestFile?.map((dataRequestFile, index) => (
|
||||||
|
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
|
||||||
|
<Typography variant="body2" gutterBottom fontWeight="bold">{dataRequestFile.description}</Typography>
|
||||||
|
<Button variant="outlined" color="primary" startIcon={< AddIcon/>}>
|
||||||
|
<Typography variant="button" display="block">
|
||||||
|
{dataRequestFile.type === 'claim-diagnosis' ?
|
||||||
|
'Dokumen Diagnosa'
|
||||||
|
: dataRequestFile.type === 'claim-kondisi' ?
|
||||||
|
'Dokumen Kondisi'
|
||||||
|
: dataRequestFile.type === 'claim-result' ?
|
||||||
|
'Dokumen Hasil Penunjang'
|
||||||
|
: ''}
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : ''}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
</TimelineContent>
|
||||||
|
</TimelineItem>
|
||||||
|
</Timeline>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
284
frontend/hospital-portal/src/sections/dashboard/TableList2.tsx
Normal file
284
frontend/hospital-portal/src/sections/dashboard/TableList2.tsx
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
/* ---------------------------------- @mui ---------------------------------- */
|
||||||
|
import { Stack, Button, MenuItem } from '@mui/material';
|
||||||
|
/* ---------------------------------- axios --------------------------------- */
|
||||||
|
// import axios from 'axios';
|
||||||
|
import axios from '../../utils/axios';
|
||||||
|
/* ---------------------------------- react --------------------------------- */
|
||||||
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
/* -------------------------------- component ------------------------------- */
|
||||||
|
import Iconify from '../../components/Iconify';
|
||||||
|
import TableComponent from '../../components/Table';
|
||||||
|
/* ---------------------------------- theme --------------------------------- */
|
||||||
|
import palette from '../../theme/palette';
|
||||||
|
//import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
|
||||||
|
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
|
||||||
|
import { useSearchParams, useNavigate } from 'react-router-dom';
|
||||||
|
import { fDate, fDateSuffix } from '../../utils/formatTime';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import TableMoreMenu from '../../components/table/TableMoreMenu';
|
||||||
|
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
|
||||||
|
import HistoryIcon from '@mui/icons-material/History';
|
||||||
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
|
import Label from '../../components/Label';
|
||||||
|
import { enqueueSnackbar } from 'notistack';
|
||||||
|
|
||||||
|
export default function TableList2() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
//const { corporateValue } = useContext(UserCurrentCorporateContext);
|
||||||
|
//const { corporateValue } = useContext(null);
|
||||||
|
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
|
||||||
|
// Download LOG
|
||||||
|
async function handleDownloadLog(claimRequest) {
|
||||||
|
return axios
|
||||||
|
.get(`claim-requests/${claimRequest}/log`, {
|
||||||
|
responseType: 'blob',
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
window.open(URL.createObjectURL(response.data));
|
||||||
|
// setLoadingLog(false);
|
||||||
|
})
|
||||||
|
// .then((blobFile) => {
|
||||||
|
// new File([blobFile], 'asdads.pdf', { type: blobFile.type })
|
||||||
|
// setLoadingLog(false);
|
||||||
|
// })
|
||||||
|
.catch((response) => {
|
||||||
|
console.log(response);
|
||||||
|
enqueueSnackbar(response.message, { variant: 'error' });
|
||||||
|
// setLoadingLog(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* setting up for the table */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
const loadings = {
|
||||||
|
isLoading: isLoading,
|
||||||
|
setIsLoading: setIsLoading,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------ handle params ----------------------------- */
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
const [appliedParams, setAppliedParams] = useState({});
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
searchParams: searchParams,
|
||||||
|
setSearchParams: setSearchParams,
|
||||||
|
appliedParams: appliedParams,
|
||||||
|
setAppliedParams: setAppliedParams,
|
||||||
|
};
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* ------------------------------ handle order ------------------------------ */
|
||||||
|
const [order, setOrder] = useState<Order>('desc');
|
||||||
|
const [orderBy, setOrderBy] = useState('member_id');
|
||||||
|
|
||||||
|
const orders = {
|
||||||
|
order: order,
|
||||||
|
setOrder: setOrder,
|
||||||
|
orderBy: orderBy,
|
||||||
|
setOrderBy: setOrderBy,
|
||||||
|
};
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* ---------------------------- handle pagination --------------------------- */
|
||||||
|
const [page, setPage] = useState(0);
|
||||||
|
const [rowsPerPage, setRowsPerPage] = useState(10);
|
||||||
|
|
||||||
|
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
|
||||||
|
current_page: 0,
|
||||||
|
from: 0,
|
||||||
|
last_page: 0,
|
||||||
|
links: [],
|
||||||
|
path: '',
|
||||||
|
per_page: 0,
|
||||||
|
to: 0,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const paginations = {
|
||||||
|
page: page,
|
||||||
|
setPage: setPage,
|
||||||
|
rowsPerPage: rowsPerPage,
|
||||||
|
setRowsPerPage: setRowsPerPage,
|
||||||
|
paginationTable: paginationTable,
|
||||||
|
setPaginationTable: setPaginationTable,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* ------------------------------ handle search ----------------------------- */
|
||||||
|
const [searchText, setSearchText] = useState('');
|
||||||
|
|
||||||
|
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (searchText === '') {
|
||||||
|
searchParams.delete('search');
|
||||||
|
const params = Object.fromEntries([...searchParams.entries()]);
|
||||||
|
setAppliedParams(params);
|
||||||
|
} else {
|
||||||
|
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
|
||||||
|
setAppliedParams(params);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchs = {
|
||||||
|
useSearchs: true,
|
||||||
|
searchText: searchText,
|
||||||
|
setSearchText: setSearchText,
|
||||||
|
handleSearchSubmit: handleSearchSubmit,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------- headCell -------------------------------- */
|
||||||
|
const headCells: HeadCell<never>[] = [
|
||||||
|
{
|
||||||
|
id: 'submission_date',
|
||||||
|
align: 'center',
|
||||||
|
label: 'Request Date',
|
||||||
|
isSort: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'member_id',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Member ID',
|
||||||
|
isSort: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Claim Code',
|
||||||
|
isSort: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'full_name',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Name',
|
||||||
|
isSort: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'status',
|
||||||
|
align: 'center',
|
||||||
|
label: 'Status',
|
||||||
|
isSort: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'action',
|
||||||
|
align: 'right',
|
||||||
|
label: '',
|
||||||
|
isSort: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
||||||
|
|
||||||
|
const parameters =
|
||||||
|
Object.keys(appliedParams).length !== 0
|
||||||
|
? appliedParams
|
||||||
|
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
|
||||||
|
|
||||||
|
const response = await axios.get(`/get-claim-requests`, {
|
||||||
|
params: { ...parameters, type: 'claim-report' },
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(
|
||||||
|
response.data.data.map((obj: any) => ({
|
||||||
|
...obj,
|
||||||
|
status:
|
||||||
|
obj.status === 'requested' ? (
|
||||||
|
<Label color='primary'>
|
||||||
|
Request
|
||||||
|
</Label>
|
||||||
|
) : obj.status === 'approved' ? (
|
||||||
|
<Label color='success' >
|
||||||
|
Approval
|
||||||
|
</Label>
|
||||||
|
) : obj.status === 'declined' ? (
|
||||||
|
<Label color='error'>
|
||||||
|
Decline
|
||||||
|
</Label>
|
||||||
|
) : obj.status === 'pending' ? (
|
||||||
|
<Label color='primary'>
|
||||||
|
Pending
|
||||||
|
</Label>
|
||||||
|
) : obj.status === 'reviewed' ? (
|
||||||
|
<Label color='info'>
|
||||||
|
Review
|
||||||
|
</Label>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
startIcon={<Iconify icon="fa6-solid:clock" />}
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#CD7B2E',
|
||||||
|
color: '#FFFF',
|
||||||
|
padding: '1px, 8px',
|
||||||
|
paddingY: 1,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: '#BF6919',
|
||||||
|
color: '#FFFF',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Ongoing
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
submission_date:
|
||||||
|
<Label>
|
||||||
|
{obj.submission_date ? fDateSuffix(obj.submission_date) : ''}
|
||||||
|
</Label>
|
||||||
|
,
|
||||||
|
action:
|
||||||
|
<TableMoreMenu actions={
|
||||||
|
<>
|
||||||
|
<MenuItem onClick={() => navigate ('/detail/'+obj.claim_request_id)}>
|
||||||
|
<Iconify icon="eva:eye-fill" />
|
||||||
|
View
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => handleDownloadLog(obj.claim_request_id)}>
|
||||||
|
<Iconify icon="eva:download-fill" />
|
||||||
|
Download LOG
|
||||||
|
</MenuItem>
|
||||||
|
</>
|
||||||
|
} />
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
setPaginationTable(response.data);
|
||||||
|
setRowsPerPage(response.data.per_page);
|
||||||
|
|
||||||
|
if (searchParams.get('page')) {
|
||||||
|
//@ts-ignore
|
||||||
|
const currentPage = parseInt(searchParams.get('page')) - 1;
|
||||||
|
|
||||||
|
paginationTable.current_page = currentPage;
|
||||||
|
setPage(currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
})();
|
||||||
|
}, [appliedParams, searchParams, order, orderBy, setSearchParams]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<TableComponent
|
||||||
|
headCells={headCells}
|
||||||
|
rows={data}
|
||||||
|
orders={orders}
|
||||||
|
paginations={paginations}
|
||||||
|
loadings={loadings}
|
||||||
|
params={params}
|
||||||
|
searchs={searchs}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import { format, getTime, formatDistanceToNow } from 'date-fns';
|
import { format, parseISO, getTime, setHours, setMinutes, formatDistanceToNow } from 'date-fns';
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
export function fDate(date: Date | string | number, dateFormat = 'dd MMMM yyyy' ) {
|
export function fDate(date: Date | string | number) {
|
||||||
return format(new Date(date), dateFormat);
|
//console.log(date);
|
||||||
|
return format(new Date(date), 'dd MMMM yyyy');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fDateTime(date: Date | string | number) {
|
export function fDateTime(date: Date | string | number) {
|
||||||
return format(new Date(date), 'dd MMM yyyy p');
|
return format(new Date(date), 'dd MMM yyyy hh:mm');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fTimestamp(date: Date | string | number) {
|
export function fTimestamp(date: Date | string | number) {
|
||||||
@@ -18,13 +19,30 @@ export function fDateTimeSuffix(date: Date | string | number) {
|
|||||||
return format(new Date(date), 'dd/MM/yyyy hh:mm p');
|
return format(new Date(date), 'dd/MM/yyyy hh:mm p');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function fDateSuffix(date: Date | string | number) {
|
||||||
|
return format(new Date(date), 'dd MMM yyyy');
|
||||||
|
}
|
||||||
|
|
||||||
export function fToNow(date: Date | string | number) {
|
export function fToNow(date: Date | string | number) {
|
||||||
return formatDistanceToNow(new Date(date), {
|
return formatDistanceToNow(new Date(date), {
|
||||||
addSuffix: true
|
addSuffix: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function fPostFormat(date: Date | string | number, dateFormat = 'yyyy-MM-dd HH:mm:ss') {
|
||||||
export function fPostFormat(date: Date | string | number, dateFormat = 'yyyy-MM-dd HH:mm:ss' ) {
|
|
||||||
return format(new Date(date), dateFormat);
|
return format(new Date(date), dateFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export function fDateString(date) {
|
||||||
|
// const dateObj = parseISO(date);
|
||||||
|
// const formattedDate = format(dateObj, 'dd MMMM yyyy');
|
||||||
|
// return formattedDate;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function fFormattedDateString(date : String) {
|
||||||
|
// console.log(date);
|
||||||
|
// const datePart = date.split(' ')[0]; // Memisahkan bagian tanggal
|
||||||
|
// const formattedDate = fDateString(datePart); // Menggunakan fungsi sebelumnya untuk memformat tanggal
|
||||||
|
// return formattedDate;
|
||||||
|
// }
|
||||||
|
|||||||
Reference in New Issue
Block a user