From f2e5a22c64a9530dadec5c080dfabc58504b7bac Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Mon, 16 Oct 2023 16:21:47 +0700 Subject: [PATCH] LMSN-217 Update slicing hospital portal --- .../Api/ClaimRequestController.php | 111 +++++ Modules/HospitalPortal/Routes/api.php | 2 + .../hospital-portal/src/components/Label.tsx | 98 +++++ .../hospital-portal/src/components/Table.tsx | 387 ++++++++++++++++++ .../hospital-portal/src/pages/Dashboard.tsx | 3 +- frontend/hospital-portal/src/routes/index.tsx | 6 + .../src/sections/dashboard/Detail.tsx | 67 +++ .../src/sections/dashboard/DetailStepper.tsx | 58 +++ .../src/sections/dashboard/DetailTimeline.tsx | 104 +++++ .../src/sections/dashboard/TableList2.tsx | 284 +++++++++++++ .../hospital-portal/src/utils/formatTime.ts | 32 +- 11 files changed, 1144 insertions(+), 8 deletions(-) create mode 100644 frontend/hospital-portal/src/components/Label.tsx create mode 100644 frontend/hospital-portal/src/components/Table.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Detail.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/DetailStepper.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/DetailTimeline.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/TableList2.tsx diff --git a/Modules/HospitalPortal/Http/Controllers/Api/ClaimRequestController.php b/Modules/HospitalPortal/Http/Controllers/Api/ClaimRequestController.php index 27bd30e3..ccee7ce5 100644 --- a/Modules/HospitalPortal/Http/Controllers/Api/ClaimRequestController.php +++ b/Modules/HospitalPortal/Http/Controllers/Api/ClaimRequestController.php @@ -16,6 +16,7 @@ use Illuminate\Routing\Controller; use Modules\HospitalPortal\Transformers\ClaimRequestResource; use Modules\HospitalPortal\Transformers\ClaimRequestShowResource; use PDF; +use Illuminate\Support\Facades\DB; class ClaimRequestController extends Controller { @@ -208,4 +209,114 @@ class ClaimRequestController extends Controller // Menghasilkan kode dengan format yang diinginkan 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); + } } diff --git a/Modules/HospitalPortal/Routes/api.php b/Modules/HospitalPortal/Routes/api.php index 3ff6dc32..ffe1e1bf 100644 --- a/Modules/HospitalPortal/Routes/api.php +++ b/Modules/HospitalPortal/Routes/api.php @@ -51,6 +51,8 @@ Route::prefix('v1')->group(function() { 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/{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'); }); }); }); diff --git a/frontend/hospital-portal/src/components/Label.tsx b/frontend/hospital-portal/src/components/Label.tsx new file mode 100644 index 00000000..c633ec4f --- /dev/null +++ b/frontend/hospital-portal/src/components/Label.tsx @@ -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 ( + + {children} + + ); +} + + diff --git a/frontend/hospital-portal/src/components/Table.tsx b/frontend/hospital-portal/src/components/Table.tsx new file mode 100644 index 00000000..5a9ac040 --- /dev/null +++ b/frontend/hospital-portal/src/components/Table.tsx @@ -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({ + headCells, + rows, + paginations, + orders, + loadings, + params, + filters, + filterStatus, + filterStartDate, + filterEndDate, + searchs, + exportReport, +}: TableListProps) { + /* ------------------------------- handle sort ------------------------------ */ + const handleRequestSort = async (event: React.MouseEvent, 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) => { + handleRequestSort(event, property); + }; + + return ( + + + {headCells && + headCells.map((headCell, index) => ( + + {headCell.isSort ? ( + + {headCell.label} + {orders?.orderBy === headCell.id ? ( + + {orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + ) : ( + headCell.label + )} + + ))} + + + ); + }; + /* -------------------------------------------------------------------------- */ + + /* ------------------------ button change pagination ------------------------ */ + const onPageChangeHandle = async ( + event: React.MouseEvent | 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) => { + 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 ( + // + + {/* Field 1 */} + + + {filters && filters.useFilter ? ( + + + + Division + + + + +
+ searchs.setSearchText(event.target.value)} + value={searchs.searchText} + fullWidth + /> + +
+
+ ) : null } + + {searchs && searchs.useSearchs ? ( + + {filterStatus && filterStatus.useFilter ? ( + +
+ searchs.setSearchText(event.target.value)} + value={searchs.searchText} + fullWidth + InputProps={{ + startAdornment: ( + + + + ), + }} + placeholder="Search Name or Member ID... " + /> + +
+ ) : + +
+ searchs.setSearchText(event.target.value)} + value={searchs.searchText} + fullWidth + InputProps={{ + startAdornment: ( + + + + ), + }} + placeholder="Search Name or Member ID... " + /> + +
+ } + +
+ ) : null } + + {/* Start date */} + {filterStartDate && filterStartDate.useFilter ? ( + +
+ filterStartDate.setStartDate(event.target.value)} + fullWidth + label="Start Date" + InputLabelProps={{ + shrink: true, + }} + /> + +
+ ) : null } + + {/* End Date */} + + {filterEndDate && filterEndDate.useFilter ? ( + +
+ filterEndDate.setEndDate(event.target.value)} + fullWidth + label="End Date" + InputLabelProps={{ + shrink: true, + }} + /> + +
+ ) : null } + + {/* Filter status */} + {filterStatus && filterStatus.useFilter ? ( + + + Status + + + + ) : null } + + {/* Export Report */} + + {exportReport && exportReport.useExport ? ( + + + + + + ) : null } + +
+
+ {/* End Field 1 */} + {/* Field 2 */} + + {/* Table */} + + + {/* Table Header */} + + {/* End Table Header */} + {/* Table Body */} + + {loadings.isLoading && rows.length >= 1 ? ( + + + Loading . . . + + + ) : rows && rows.length >= 1 ? ( + rows.map((row, rowIndex) => ( + + {headCells && + //@ts-ignore + headCells.map((head, headIndex) => ( + //@ts-ignore + + {row[head.id]} + + ))} + + )) + ) : ( + + + No Data Found + + + )} + + {/* End Table Body */} + + + {/* End Table */} + + {/* Pagination */} + + {/* End Pagination */} + + {/* End Field 2 */} +
+ //
+ ); +} diff --git a/frontend/hospital-portal/src/pages/Dashboard.tsx b/frontend/hospital-portal/src/pages/Dashboard.tsx index ab6274f6..6ff99da1 100644 --- a/frontend/hospital-portal/src/pages/Dashboard.tsx +++ b/frontend/hospital-portal/src/pages/Dashboard.tsx @@ -13,6 +13,7 @@ import { Stack } from '@mui/system'; import { Input } from '@mui/material'; //sections import TableList from '@/sections/dashboard/TableList'; +import TableList2 from '@/sections/dashboard/TableList2'; import { fDate } from '@/utils/formatTime'; import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim'; @@ -93,7 +94,7 @@ export default function Dashboard() { */} - + diff --git a/frontend/hospital-portal/src/routes/index.tsx b/frontend/hospital-portal/src/routes/index.tsx index 26fcfbdc..4815a322 100644 --- a/frontend/hospital-portal/src/routes/index.tsx +++ b/frontend/hospital-portal/src/routes/index.tsx @@ -76,6 +76,10 @@ export default function Router() { path: 'dashboard', element: , }, + { + path: '/detail/:id', + element: , + }, ], }, @@ -98,3 +102,5 @@ const ForgetPassword = Loadable(lazy(() => import('@/pages/auth/ForgetPassword') // Dashboard const Dashboard = Loadable(lazy(() => import('@/pages/Dashboard'))); const NotFound = Loadable(lazy(() => import('@/pages/Page404'))); + +const DetailClaimReport = Loadable(lazy(()=> import('@/sections/dashboard/Detail'))); diff --git a/frontend/hospital-portal/src/sections/dashboard/Detail.tsx b/frontend/hospital-portal/src/sections/dashboard/Detail.tsx new file mode 100644 index 00000000..46405438 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Detail.tsx @@ -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 ( + + + + navigate(-1)} sx={{cursor:'pointer'}}/> + Detail + {data ? ( + + Submission Date + {(data && data.data) ? format(new Date(data.data.status.submission_date), "d MMM yyyy") : ''} + + ) : ''} + + {data ? ( + + + + + + + + + ) : ''} + + + ); +} \ No newline at end of file diff --git a/frontend/hospital-portal/src/sections/dashboard/DetailStepper.tsx b/frontend/hospital-portal/src/sections/dashboard/DetailStepper.tsx new file mode 100644 index 00000000..18dc959a --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/DetailStepper.tsx @@ -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 ( + + + {status?.map((label) => ( + + : ''}>{label} + + ))} + + + ); +} diff --git a/frontend/hospital-portal/src/sections/dashboard/DetailTimeline.tsx b/frontend/hospital-portal/src/sections/dashboard/DetailTimeline.tsx new file mode 100644 index 00000000..1e93f302 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/DetailTimeline.tsx @@ -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) => ( + + {dataTimeline.date ? format(new Date(dataTimeline.date), "d MMM yyyy") : ''} + + + + + + + + + + {dataTimeline.date ? format(new Date(dataTimeline.date), "HH : ii") : ''} + {dataTimeline.txt_status} + + + Detail: + {dataTimeline.description} + + {dataTimeline.status === 'reviewed' && requestFile ? ( + <> + {requestFile?.map((dataRequestFile, index) => ( + + {dataRequestFile.description} + + + ))} + + ) : ''} + + + + + + ))} + + ); +} diff --git a/frontend/hospital-portal/src/sections/dashboard/TableList2.tsx b/frontend/hospital-portal/src/sections/dashboard/TableList2.tsx new file mode 100644 index 00000000..22c895eb --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/TableList2.tsx @@ -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('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({ + 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) => { + 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[] = [ + { + 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' ? ( + + ) : obj.status === 'approved' ? ( + + ) : obj.status === 'declined' ? ( + + ) : obj.status === 'pending' ? ( + + ) : obj.status === 'reviewed' ? ( + + ) : ( + + ), + submission_date: + + , + action: + + navigate ('/detail/'+obj.claim_request_id)}> + + View + + handleDownloadLog(obj.claim_request_id)}> + + Download LOG + + + } /> + })) + ); + + 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 ( + + + + ); +} diff --git a/frontend/hospital-portal/src/utils/formatTime.ts b/frontend/hospital-portal/src/utils/formatTime.ts index b2656c01..ed88dcae 100644 --- a/frontend/hospital-portal/src/utils/formatTime.ts +++ b/frontend/hospital-portal/src/utils/formatTime.ts @@ -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' ) { - return format(new Date(date), dateFormat); +export function fDate(date: Date | string | number) { + //console.log(date); + return format(new Date(date), 'dd MMMM yyyy'); } 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) { @@ -18,13 +19,30 @@ export function fDateTimeSuffix(date: Date | string | number) { 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) { 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); } + +// 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; +// }