From 127ef3ff50c67fc13d583ad841e026d5bd1006a2 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Tue, 10 Feb 2026 10:03:28 +0700 Subject: [PATCH] Update Add Input Hospital Portal --- Modules/HospitalPortal/Routes/api.php | 38 +- Modules/Internal/Routes/api.php | 47 + .../Transformers/RequestLogShowResource.php | 7 +- database/seeders/NavigationSeeder.php | 8 +- database/seeders/PermissionTableSeeder.php | 1 + .../pages/CustomerService/FinalLog/Detail.tsx | 183 ++- .../Request/Components/CardSearchMember.tsx | 149 ++ .../Request/Components/DialogMember.tsx | 204 +++ .../Request/Components/FormRequestLog.tsx | 322 ++++ .../pages/CustomerService/Request/Index.tsx | 43 +- .../src/components/MoreMenu.tsx | 58 + .../src/components/MuiDialog.tsx | 11 +- .../dashboard/navbar/NavbarVertical.tsx | 49 +- .../hospital-portal/src/pages/Dashboard.tsx | 30 +- frontend/hospital-portal/src/routes/index.tsx | 9 + .../dashboard/Components/CardService.tsx | 105 ++ .../dashboard/Components/DialogBenefit.tsx | 682 +++++++++ .../Components/DialogConfirmation.tsx | 233 +++ .../Components/DialogDeleteBenefit.tsx | 138 ++ .../Components/DialogDeleteFileLog.tsx | 144 ++ .../Components/DialogDeleteMedicine.tsx | 69 + .../Components/DialogEditBenefit.tsx | 448 ++++++ .../Components/DialogEditFinalLOG.tsx | 318 ++++ .../dashboard/Components/DialogMedicine.tsx | 171 +++ .../dashboard/Components/DialogSendWa.tsx | 185 +++ .../Components/DialogUploadFileFinalLog.tsx | 322 ++++ .../dashboard/DetailRequestFinalLog.tsx | 1337 +++++++++++++++++ .../sections/dashboard/Model/Functions.tsx | 190 +++ .../src/sections/dashboard/Model/Types.ts | 175 +++ .../sections/dashboard/TableListReqLog.tsx | 70 +- .../hospital-portal/src/utils/formatTime.ts | 14 + 31 files changed, 5674 insertions(+), 86 deletions(-) create mode 100644 frontend/dashboard/src/pages/CustomerService/Request/Components/CardSearchMember.tsx create mode 100644 frontend/dashboard/src/pages/CustomerService/Request/Components/DialogMember.tsx create mode 100644 frontend/dashboard/src/pages/CustomerService/Request/Components/FormRequestLog.tsx create mode 100644 frontend/hospital-portal/src/components/MoreMenu.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/CardService.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogBenefit.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogConfirmation.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteBenefit.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteFileLog.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteMedicine.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogEditBenefit.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogEditFinalLOG.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogMedicine.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogSendWa.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Components/DialogUploadFileFinalLog.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/DetailRequestFinalLog.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Model/Functions.tsx create mode 100644 frontend/hospital-portal/src/sections/dashboard/Model/Types.ts diff --git a/Modules/HospitalPortal/Routes/api.php b/Modules/HospitalPortal/Routes/api.php index 00dbf376..ed9a8eeb 100755 --- a/Modules/HospitalPortal/Routes/api.php +++ b/Modules/HospitalPortal/Routes/api.php @@ -7,6 +7,10 @@ use Modules\HospitalPortal\Http\Controllers\Api\MemberController; use Modules\HospitalPortal\Http\Controllers\ClaimController; use Modules\HospitalPortal\Http\Controllers\Api\NotificationController; use Modules\HospitalPortal\Http\Controllers\Api\RequestLogController; +use Modules\Internal\Http\Controllers\Api\RequestLogController as RequestLogControllerInternal; +use Modules\Internal\Http\Controllers\Api\RequestLogBenefitController; +use Modules\Internal\Http\Controllers\Api\RequestLogMedicineController; + use Modules\HospitalPortal\Http\Controllers\ApotekController; use Modules\HospitalPortal\Http\Middleware\Authentication; use Modules\HospitalPortal\Http\Middleware\Authorization; @@ -35,12 +39,42 @@ Route::prefix('v1')->group(function() { Route::post('forget-password', [AuthController::class, 'forgetPassword']); Route::post('verify-email', [AuthController::class, 'verifyEmail'])->name('verify-email'); Route::post('verify-code', [AuthController::class, 'verifCode']); - + Route::get('service-member/{id}', [AutocompleteController::class, 'serviceCode']); Route::get('specialis', [AutocompleteController::class, 'specialisList']); + Route::get('diagnosis', [RequestLogControllerInternal::class, 'diagnosis']); Route::middleware('auth:sanctum')->group(function () { + //dari Internal Prime center + Route::get('customer-service/request', [RequestLogControllerInternal::class, 'index']); + Route::post('customer-service/request', [RequestLogControllerInternal::class, 'createNew']); + Route::put('customer-service/request/{id}', [RequestLogControllerInternal::class, 'update']); + Route::get('customer-service/request/{id}', [RequestLogControllerInternal::class, 'show']); + Route::put('customer-service/request/delete/{id}', [RequestLogControllerInternal::class, 'destroy']); + Route::put('customer-service/request/final_log/{id}', [RequestLogControllerInternal::class, 'deleteFinalLog']); + Route::get('customer-service/request/{id}/download', [RequestLogControllerInternal::class, 'generateRequestLog']); + Route::post('customer-service/request/import', [RequestLogControllerInternal::class, 'importRequestLog']); + Route::post('customer-service/request/import-invoice', [RequestLogControllerInternal::class, 'importInvoice']); + Route::post('customer-service/request/exportFiledInvoice', [RequestLogControllerInternal::class, 'exportFiledInvoice']); + Route::get('customer-service/request/data', [RequestLogControllerInternal::class, 'generateDataRequestLogExcel']); + Route::post('customer-service/request/{id}/add_file', [RequestLogControllerInternal::class, 'requestFiles']); + Route::post('customer-service/request/{id}/approval_files', [RequestLogControllerInternal::class, 'approvalFiles']); + Route::post('customer-service/request/{id}/delete_file', [RequestLogControllerInternal::class, 'deleteFiles']); + + Route::post('customer-service/request/final-log', [RequestLogControllerInternal::class, 'updateFinalLog']); + + // insert benefit + Route::post('customer-service/request/insert-benefit', [RequestLogBenefitController::class, 'store']); + Route::post('customer-service/request/benefit_data/{id}', [RequestLogBenefitController::class, 'destroy']); + Route::put('customer-service/request/benefit_data/{id}', [RequestLogBenefitController::class, 'update']); + + // insert medicine + Route::post('customer-service/request/medicine-data', [RequestLogMedicineController::class, 'store']); + Route::delete('customer-service/request/medicine-data/{id}', [RequestLogMedicineController::class, 'destroy']); + Route::put('customer-service/request/medicine-data/{id}', [RequestLogMedicineController::class, 'update']); + // end prime center + // Navigation Route::get('navigations', [NavigationController::class, 'index']); @@ -96,6 +130,6 @@ Route::prefix('v1')->group(function() { Route::post('claim-requests/{id}/request-files', [ClaimRequestController::class, 'requestFiles']); }); - + }); }); diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index daf58971..057566ee 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -54,6 +54,10 @@ use Modules\Internal\Http\Controllers\Api\CorporateManageController; use Modules\Internal\Http\Controllers\Api\UserManagementController; use Modules\Internal\Http\Controllers\ClaimEncounterController; use Modules\Linksehat\Http\Controllers\Api\AutocompleteController; +use Modules\HospitalPortal\Http\Controllers\Api\MemberController as MemberControllerHospitalPortal; +use Modules\HospitalPortal\Http\Controllers\Api\RequestLogController as RequestLogControllerHospitalPortal; + + // Report use Modules\Internal\Http\Controllers\Api\ReportLogController; @@ -311,6 +315,49 @@ Route::prefix('internal')->group(function () { Route::get('claims/{id}/benefit-configuration', [ClaimController::class, 'getBenefitConfiguration']); // Bagaskoro, BSD 03 November 2023 Route::put('claims/benefit-configuration/edit/{id}', [ClaimController::class, 'editBenefitConfiguration']); // Bagaskoro, BSD 03 November 2023 + //dari hospital-portal + //Search Member + Route::post( + 'search-member', + [MemberControllerHospitalPortal::class, 'search'] + ); + // Request LOG + Route::post( + 'request-log', + [RequestLogControllerHospitalPortal::class, 'requestLog'] + ); + + Route::get( + 'get-request-log', + [RequestLogControllerHospitalPortal::class, 'getRequestLog'] + ); + + Route::get( + 'get-final-log', + [RequestLogControllerHospitalPortal::class, 'getFinalLog'] + ); + + Route::post( + 'request-final-log', + [RequestLogControllerHospitalPortal::class, 'requestFinalLog'] + ); + + Route::get( + 'download-log/{request_log_id}', + [RequestLogControllerHospitalPortal::class, 'downlodLog'] + ); + + Route::get( + 'download-final-log/{request_log_id}', + [RequestLogControllerHospitalPortal::class, 'downlodFinalLog'] + ); + + Route::post( + 'submit-claims', + [RequestLogControllerHospitalPortal::class, 'submitClaims'] + ); + //end dari hospital-portal + Route::get('customer-service/request', [RequestLogController::class, 'index']); Route::post('customer-service/request', [RequestLogController::class, 'createNew']); Route::put('customer-service/request/{id}', [RequestLogController::class, 'update']); diff --git a/Modules/Internal/Transformers/RequestLogShowResource.php b/Modules/Internal/Transformers/RequestLogShowResource.php index 54a67090..f5a8390b 100755 --- a/Modules/Internal/Transformers/RequestLogShowResource.php +++ b/Modules/Internal/Transformers/RequestLogShowResource.php @@ -30,13 +30,15 @@ class RequestLogShowResource extends JsonResource { $requestLog = parent::toArray($request); + $corporateId = $requestLog['member']['current_plan']['corporate_id'] ?? 0; $member_id = $requestLog['member_id']; $planMember = MemberPlan::where('member_id', $member_id)->get('plan_id'); - + $planId = Plan::whereIn('id', $planMember)->where('service_code', $requestLog['service_code'])->first(); $benefit = CorporateBenefit::with(['benefit', 'plan'])->where('plan_id', $planId->id)->get()->toArray(); $benefitDetailLog = RequestLogBenefit::with('benefit')->where('request_log_id', $requestLog['id'])->get()->toArray(); + $medicineDetailLog = RequestLogMedicine::where('request_log_id', $requestLog['id'])->get()->toArray(); $provider = Organization::where('id', $requestLog['organization_id'])->first(); $claimRequest = ClaimRequest::where('request_log_id', $requestLog['id'])->first(); @@ -139,8 +141,8 @@ class RequestLogShowResource extends JsonResource 'invoice_no' => $requestLog['invoice_no'], 'billing_no' => $requestLog['billing_no'], 'specialities_id' => $name, + 'specialitiesID' => $requestLog['specialities_id'], 'dppj' => $dppj, - 'code' => $requestLog['code'], 'code_claim' => $claimCode, 'member_id' => $requestLog['member']['member_id'], 'type_of_member' => $requestLog['type_of_member'], @@ -160,6 +162,7 @@ class RequestLogShowResource extends JsonResource 'approved_final_log_at' => $requestLog['approved_final_log_at'], // submission final log 'discharge_date' => $requestLog['discharge_date'], 'service_type' => Helper::serviceName($requestLog['service_code']), + 'service_code' => $requestLog['service_code'], 'claim_method' => $requestLog['payment_type'], 'status' => $requestLog['status'], 'status_final_log' => $requestLog['status_final_log'], diff --git a/database/seeders/NavigationSeeder.php b/database/seeders/NavigationSeeder.php index 19809bd1..55cb5d42 100644 --- a/database/seeders/NavigationSeeder.php +++ b/database/seeders/NavigationSeeder.php @@ -205,7 +205,7 @@ class NavigationSeeder extends Seeder 'permission' => 'report-livechat-payment' ], [ - 'title' => 'Prescription', + 'title' => 'Prescription', 'path' => '/report/prescription', 'permission' => 'report-prescription' ], @@ -346,6 +346,12 @@ class NavigationSeeder extends Seeder 'icon' => 'ic_booking', 'permission' => 'dashboard-claim-hospital-portal' ], + [ + 'title' => 'Request LOG', + 'path' => '', + 'icon' => '', + 'permission' => 'request-log-hospital-portal' + ], ####################### CS LMS & APOTEK PORTAL ######################### [ 'title' => 'Dashboard', diff --git a/database/seeders/PermissionTableSeeder.php b/database/seeders/PermissionTableSeeder.php index 7256f115..75da78fd 100644 --- a/database/seeders/PermissionTableSeeder.php +++ b/database/seeders/PermissionTableSeeder.php @@ -111,6 +111,7 @@ class PermissionTableSeeder extends Seeder 'datas' => [ 'dashboard-hospital-portal', 'dashboard-claim-hospital-portal', + 'request-log-hospital-portal', 'dashboard-apotek-portal', ] ], diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx index e3e27566..7f5f5582 100644 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx @@ -23,6 +23,8 @@ import Page from '../../../components/Page'; import Iconify from '@/components/Iconify'; import { FormProvider, RHFDatepicker, RHFSelect, RHFTextField } from '@/components/hook-form'; import RHFTextFieldMoney from '@/components/hook-form/v2/RHFTextFieldMoney'; +import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; // utils import useSettings from '../../../hooks/useSettings'; import { useFieldArray, useForm } from 'react-hook-form'; @@ -35,7 +37,7 @@ import { enqueueSnackbar } from 'notistack'; import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; import { DetailFinalLogType } from './Model/Types'; import { fDate, fDateTimesecond } from '@/utils/formatTime'; -import { Button } from '@mui/material'; +import { Button, Autocomplete, FormHelperText} from '@mui/material'; import DialogConfirmation from '../FinalLog/Components/DialogConfirmation'; import Label from '@/components/Label'; import { Box } from '@mui/system'; @@ -45,6 +47,11 @@ import {BenefitData } from '../FinalLog/Model/Types' import AddIcon from '@mui/icons-material/Add'; import { LoadingButton } from '@mui/lab'; import { makeFormData } from '@/utils/jsonToFormData'; +import TextField from '@mui/material/TextField'; +import { fPostFormat } from '@/utils/formatTime'; + + + // Import Card Detail Final LOG import CardDetail from '../Components/CardDetail'; @@ -71,11 +78,86 @@ import DialogDeleteFileLog from './Components/DialogDeleteFileLog'; import DialogUploadFileFinalLog from './Components/DialogUploadFileFinalLog'; import DialogSendWa from './Components/DialogSendWa'; import { set } from 'nprogress'; +import { format } from 'date-fns'; + // ---------------------------------------------------------------------- export default function Detail() { + //dari hospital portal + const [dischargeDate, setDischargeDate] = useState(format(new Date(), "yyyy MMM d HH:mm:ss")); + const [serviceOptions, setServiceOptions] = useState([ + { value: '-', label: '-' } +]); +const [specialisOptions, setSpecialisOptions] = useState([ + { value: '-', label: '-' } +]); +useEffect(() => { + axios.get('service-member/'+1) + .then((response) => { + setServiceOptions(response.data); + }).catch((error) => { + console.error('Error fetching ICD options:', error); + }); + + axios.get('specialis') + .then((response) => { + setSpecialisOptions(response.data); + }).catch((error) => { + console.error('Error fetching ICD options:', error); + }); + + }, []); +const [serviceCode, setServiceCode] = useState(""); +const [idSpecialities, setIdSpecialities] = useState(""); +const [inputDppj, setInputDppj] = useState(""); +function submitRequestFinalLog() { + if(dischargeDate == '') + { + enqueueSnackbar('Tanggal Keluar', { variant: 'warning' }); + return false; + } + //cek spesialis + if(!idSpecialities) + { + enqueueSnackbar('Spesialis', { variant: 'warning' }); + return false; + } + //cek dpjp + if(!inputDppj) + { + enqueueSnackbar('DPPJ', { variant: 'warning' }); + return false; + } + setSubmitLoading(true); + const formData = makeFormData({ + // request_logs_id: member.id, + // result_files: fileHasilPenunjangs, + // diagnosa_files: fileDiagnosas, + // kondisi_files: fileKondisis, + discharge_date: fPostFormat(dischargeDate, 'yyyy-MM-dd HH:mm:ss'), + service_code: serviceCode, + spescialis_id: idSpecialities, + dppj: inputDppj, + }); + axios + .post('/request-final-log', formData) + .then((response) => { + enqueueSnackbar(response.data.meta.message ?? 'Berhasil membuat data', { variant: 'success' }); + // handleSubmitSuccess(); + // onClose({ someData: 'example data' }, getData); + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.meta.message ?? 'Something Went Wrong', { variant: 'error' }); + }) + .then(() => { + setSubmitLoading(false); + }); + } +//end dari hospital portal + + const location = useLocation(); const queryParams = new URLSearchParams(location.search); @@ -106,7 +188,7 @@ export default function Detail() { .post(`/customer-service/request/${id}/approval_files`, formData) .then((response) => { enqueueSnackbar('Berhasil membuat data', { variant: 'success' }); - + window.location.reload() }) .catch(({ response }) => { @@ -231,10 +313,10 @@ export default function Detail() { // Handle Delete File LOG const [pathFile, setPathFile] = useState('') const [dialogDeleteFIleLog, setDialogDeleteFileLog] = useState(false) - + // Handle Upload File LOG const [dialogUploadFileLog, setDialogUploadFileLog] = useState(false) - + const fileDiagnosaInput = useRef(null); const [fileApprovals, setFileApproval] = useState([]); @@ -425,6 +507,91 @@ export default function Detail() { > */} + + + + {/* Kolom Tanggal Discharge */} + + Tanggal Keluar + + { + setDischargeDate(newValue); + }} + inputFormat="dd-MM-yyyy HH:mm" + renderInput={(params) => } + /> + + + + {/* Kolom Service Type */} + + Tipe Service + option.label || ""} + value={serviceOptions.find((opt) => opt.value == serviceCode) || null} + onChange={(event, newValue) => { + setServiceCode(newValue?.value || ""); + }} + renderInput={(params) => ( + + )} + /> + + + + + {/* Specialist */} + + + Spesialis + option.label || ''} + value={specialisOptions.find((opt) => opt.value === idSpecialities) || null} + onChange={(event, newValue) => { + setIdSpecialities(newValue?.value || 0); + }} + renderInput={(params) => ( + + )} + /> + + + + + + + DPPJ + { + setInputDppj(event.target.value); + }} + fullWidth + /> + + + { + submitRequestFinalLog(); + }} + loading={submitLoading} + > + Simpan + + + {/* Surat persetujuan Tindakan */} @@ -592,7 +759,7 @@ export default function Detail() { {/* FILE YANG SUDAH TERUPLOAD */} {requestLog?.files - ?.filter((document) => document.type === 'approval') + ?.filter((document) => document.type === 'approval') ?.map((documentType, index) => ( - + {/* Benefit */} @@ -993,7 +1160,7 @@ export default function Detail() { {requestLog?.files - ?.filter((document) => document.type !== 'approval') + ?.filter((document) => document.type !== 'approval') ?.map((documentType, index) => ( @@ -1043,7 +1210,7 @@ export default function Detail() { variant="outlined" sx={{ color: '#FF4842', borderColor: '#FF4842' }} onClick={() => { - + }} > Decline diff --git a/frontend/dashboard/src/pages/CustomerService/Request/Components/CardSearchMember.tsx b/frontend/dashboard/src/pages/CustomerService/Request/Components/CardSearchMember.tsx new file mode 100644 index 00000000..b01b39c3 --- /dev/null +++ b/frontend/dashboard/src/pages/CustomerService/Request/Components/CardSearchMember.tsx @@ -0,0 +1,149 @@ +// @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, { useContext, 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 { fDateOnly } from '@/utils/formatTime'; +import MuiDialog from '@/components/MuiDialog'; +import axios from '@/utils/axios'; +import { useSnackbar } from 'notistack'; +import DialogMember from './DialogMember'; +// import { LanguageContext } from '@/contexts/LanguageContext'; + +// ---------------------------------------------------------------------- + +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(handleSubmitSuccess:()=> void) { +// const { localeData }: any = useContext(LanguageContext); + const {enqueueSnackbar} = useSnackbar(); + + const [noPolis, setNoPolis] = useState(''); + const [birthDate, setBirthDate] = useState(null); + 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); + const [nameMember, setNameMember] = useState(''); + + function handleSearchMember() { + setLoadingBenefit(true) + + axios.post('/search-member', { + no_polis: noPolis, + birth_date: birthDate ? fDateOnly(birthDate, 'yyyy-MM-dd') : null + }) + .then((response) => { + setOpenDialogBenefit(true) + setCurrentMember(response.data.data) + setNameMember(response.data.data.members.name); + }) + .catch(({response}) => { + enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"}) + }) + .then(() => { + setLoadingBenefit(false) + }); + } + + return ( +
+ + + + Pengajuan Jaminan + + + + { + setNoPolis(event.target.value) + }} + sx={{width:'40%'}} + required + /> + + + { + setBirthDate( (newValue)); + }} + inputFormat="dd-MM-yyyy" + renderInput={(params) => } + /> + + + { + handleSearchMember() + }} + > + + Cari Anggota + + + +{/* + */} + {setOpenDialogBenefit(false); handleSubmitSuccess()})} + maxWidth="sm" + /> +
+ ); +} diff --git a/frontend/dashboard/src/pages/CustomerService/Request/Components/DialogMember.tsx b/frontend/dashboard/src/pages/CustomerService/Request/Components/DialogMember.tsx new file mode 100644 index 00000000..0fa821b9 --- /dev/null +++ b/frontend/dashboard/src/pages/CustomerService/Request/Components/DialogMember.tsx @@ -0,0 +1,204 @@ +// mui +import { styled } from '@mui/material/styles'; +import { LoadingButton, TabPanel } from "@mui/lab"; +import { + Button, + Card, + Divider, + Grid, + LinearProgress, + linearProgressClasses, + Typography, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Collapse, } from "@mui/material"; +import { Tab, Tabs } from "@mui/material"; +import { Box, Stack } from "@mui/material"; +import React, { useEffect, useState, useContext } from "react"; +import { fCurrency } from '@/utils/formatNumber'; +import { fPostFormat } from '@/utils/formatTime'; +import { Avatar } from '@mui/material'; +import Iconify from '@/components/Iconify'; +import FormRequestLog from './FormRequestLog'; +// import { LanguageContext } from '@/contexts/LanguageContext'; +import { format } from 'date-fns'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; + +export default function DialogMember(member:any, handleSubmitSuccess:() => void) { + // const { localeData }: any = useContext(LanguageContext); + const [currentTab, setCurrentTab] = useState('request'); + + // ---------------------------------------------------------------------- + + useEffect(() => { + setCurrentTab('detail') + }, [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:any) { + const { children, value, index, ...other } = props; + return ( + + ); + } + + const [openRows, setOpenRows] = useState({}); + + const handleRowToggle = (index:number) => { + setOpenRows((prevOpenRows:any) => ({ + ...prevOpenRows, + [index]: !prevOpenRows[index], + })); + }; + + return ( +
+ + + + + {member?.type !== 'view' ? ( + + ) : ''} + + + + + + Member ID + { member?.members.member_id ?? '-'} + + + Policy Number + { member?.members.policy_id ?? '-'} + + + Deposit Corporate + { member?.total_premi ? fCurrency(member?.total_premi) : '-'} + + + Limit Peserta + { member?.limit_rules ? fCurrency(member?.limit_rules) : '-'} + + + NRIC + {member?.members.nik ?? '-'} + + + NIK + {member?.members.nik ?? '-'} + + + Email + {member?.members.email ?? '-'} + + + Tanggal Lahir + {member?.members.birth_date ? format(new Date(member.members.birth_date), "d MMM yyyy") : '-'} + + + Jenis Kelamin + {member?.members.gender ?? '-'} + + + Status Perkawinan + {member?.members.marital_status ?? '-'} + + + Bahasa + {member?.members.language ?? '-'} + + + Race + {member?.members.race ?? '-'} + + + Hubungan + {member?.members.relation_with_principal != '' ? member?.members.relation_with_principal : '-'} + + + + + + + {member && member.groupServices && Object.keys(member.groupServices).map((serviceCode, index) => ( + + + {serviceCode} + + {openRows[index] ? ( + handleRowToggle(index)} /> + ) : ( + handleRowToggle(index)} /> + )} + + + + + {/* COLLAPSIBLE ROW */} + + + {/* Loop through the array for the current serviceCode */} + {member.groupServices[serviceCode].map((item:any, innerIndex:number) => ( + + + {item.description} + {item.code} + + + ))} + + + + + + ))} +
+
+
+ + + +
+
+ ) +} \ No newline at end of file diff --git a/frontend/dashboard/src/pages/CustomerService/Request/Components/FormRequestLog.tsx b/frontend/dashboard/src/pages/CustomerService/Request/Components/FormRequestLog.tsx new file mode 100644 index 00000000..e100ad99 --- /dev/null +++ b/frontend/dashboard/src/pages/CustomerService/Request/Components/FormRequestLog.tsx @@ -0,0 +1,322 @@ +import { LoadingButton } from '@mui/lab'; +import +{ + Avatar, + FormControl, + InputLabel, + Select, + FormHelperText, + MenuItem +} from '@mui/material'; +import { Card } from '@mui/material'; +import { Stack, Typography } from '@mui/material'; +import axios from '@/utils/axios'; +import { enqueueSnackbar } from 'notistack'; +import React, { useRef, useState, useContext, useEffect } from 'react'; +import { makeFormData } from '@/utils/jsonToFormData'; +import { format } from 'date-fns'; +// import { LanguageContext } from '@/contexts/LanguageContext'; +import Autocomplete from '@mui/material/Autocomplete'; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import { fPostFormat } from '@/utils/formatTime'; + +interface MemberType { + members: any; + services: any; + providers:any; + companies:any; + specialities:any; +} +interface FormRequestClaimProps { + member: MemberType; + handleSubmitSuccess: () => void; +} +export default function FormRequestClaim({ member, handleSubmitSuccess }: FormRequestClaimProps) { + // const { localeData }: any = useContext(LanguageContext); + const [serviceCode, setServiceCode] = useState(''); + const [idProvider, setIdProvider] = useState(0); + const [idSpecialities, setIdSpecialities] = useState(0); + const [inputDppj, setInputDppj] = useState(''); + //Submission date + const [submissionDate, setSubmissionDate] = useState(format(new Date(), "yyyy MMM d HH:mm:ss")); + + const [submitLoading, setSubmitLoading] = useState(false); + + const [corporate_id_partner, setCorporateIdPartner] = useState([]); + useEffect(() => { + setCorporateIdPartner(member?.companies?.map((item: { id: any; name: any; }) => ({ value: item.id, label: item.name }))); + }, []); + const [selectedCorporatID, setSelectedCorporateID] = useState([]); + + const handleSelectChangePatner = (event:any, selectedOptions:any) => { + const selectedValues = selectedOptions.map((option: { value: any; }) => option.value); + setSelectedCorporateID(selectedValues); + }; + + function submitRequest() { + if(!idProvider&& (name == '' || alamat == '')) + { + enqueueSnackbar("Mohon masukan provider", { variant: 'warning' }); + return false; + } + if(serviceCode == '') + { + enqueueSnackbar("Mohon pilih layanan", { variant: 'warning' }); + return false; + } + if(submissionDate == '') + { + enqueueSnackbar("Mohon pilih tanggal masuk", { variant: 'warning' }); + return false; + } + //cek spesialis + if(!idSpecialities) + { + enqueueSnackbar("Mohon pilih Spesialis", { variant: 'warning' }); + return false; + } + //cek dpjp + if(!inputDppj) + { + enqueueSnackbar("Mohon isi DPJP", { variant: 'warning' }); + return false; + } + setSubmitLoading(true); + const formData = { + member_id: member.members.id, + service_code: serviceCode, + organization_id: idProvider, + organization_name : name, + address_provider: alamat, + submission_date: fPostFormat(submissionDate, 'yyyy-MM-dd HH:mm:ss'), + corporate_id_partner: selectedCorporatID, + specialities_id: idSpecialities, + dppj: inputDppj + }; + axios + .post('/request-log', formData) + .then((response) => { + if (response && response.data && response.data.meta) { + setTimeout(() => { + window.location.reload(); + }, 1500); + enqueueSnackbar(response.data.meta.message, { variant: 'success' }); + handleSubmitSuccess(); + + } + }) + .catch(({ response }) => { + if (response && response.data && response.data.meta) { + enqueueSnackbar(response.data.meta.message, { variant: 'error' }); + } + }) + .then(() => { + setSubmitLoading(false); + }); + } + + interface MemberService { + service_code: string; + name: string; + } + + interface Providers { + id: number; + name: string; + } + + interface Specialities { + id: number; + name: string; + } + + const [showAddNewForm, setShowAddNewForm] = useState(false); + const [name, setName] = useState(''); + const [alamat, setAlamat] = useState(''); + + const handleAddNewData = () => { + // Logika untuk menambahkan data baru ke database + // Pastikan untuk menyesuaikan logika ini sesuai dengan kebutuhan aplikasi Anda + console.log('Adding new data:', { name, alamat }); + + // Setelah menambahkan data baru, Anda mungkin ingin melakukan sesuatu seperti menutup formulir tambahan atau melakukan pengaturan lainnya + setShowAddNewForm(false); + }; + + return ( + + + + Tanggal Buat + + {format(new Date(), "d MMM yyyy")} + + + + + Provider * + option.name || ''} + value={member?.providers.find((item: Providers) => item.id === idProvider) || null} + onChange={(event: React.ChangeEvent<{}>, newValue: Providers | null) => { + if (newValue?.id === 0) { + // Pengguna memilih opsi "Tambahkan Data Baru" + setIdProvider(0); // Reset nilai + setShowAddNewForm(true); // Menampilkan formulir tambahan + } else { + // Pengguna memilih opsi dari hasil pencarian + setIdProvider(newValue?.id || 0); + setShowAddNewForm(false); // Menyembunyikan formulir tambahan + } + }} + renderInput={(params) => ( + + )} + /> + {showAddNewForm && ( + + Tambah Baru * + setName(e.target.value)} + /> + setAlamat(e.target.value)} + /> + option.label} + value={corporate_id_partner.filter((option: { value: any; }) => selectedCorporatID.includes(option.value))} + onChange={handleSelectChangePatner} + multiple + renderInput={(params) => ( + + )} + /> + + )} + + + + + + + Layanan * + option.name || ''} + value={member?.services.find((item: MemberService) => item.service_code === serviceCode) || null} + onChange={(event: React.ChangeEvent<{}>, newValue: MemberService | null) => { + setServiceCode(newValue?.service_code || ''); + }} + renderInput={(params) => ( + + )} + /> + + + + + {/* Specialist */} + + + Spesialis * + option.name || ''} + value={member?.specialities.find((item: Specialities) => item.id === idSpecialities) || null} + onChange={(event: React.ChangeEvent<{}>, newValue : Specialities | null) => { + setIdSpecialities(newValue?.id || 0); + }} + renderInput={(params) => ( + + )} + /> + + + + + + + DPJP * + { + setInputDppj(event.target.value); + }} + fullWidth + /> + + + + + + Tanggal Masuk * + + { + setSubmissionDate( (newValue)); + }} + inputFormat="dd-MM-yyyy HH:mm" + renderInput={(params) => } + /> + + + + + + + + + {member?.members.name ?? ''} + {member?.members.member_id ?? ''} + + + + + { + submitRequest(); + }} + loading={submitLoading} + > + Request LOG + + + ); +} diff --git a/frontend/dashboard/src/pages/CustomerService/Request/Index.tsx b/frontend/dashboard/src/pages/CustomerService/Request/Index.tsx index 6b1e6fd4..75b197a8 100644 --- a/frontend/dashboard/src/pages/CustomerService/Request/Index.tsx +++ b/frontend/dashboard/src/pages/CustomerService/Request/Index.tsx @@ -1,7 +1,8 @@ -import { Card, Stack } from "@mui/material"; +import { Card, Stack,Grid,Typography,Divider } from "@mui/material"; import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; import Page from "../../../components/Page"; import List from "./List"; +import CardSearchMember from "./Components/CardSearchMember"; @@ -9,22 +10,32 @@ export default function RequestLog() { const pageTitle = 'Request LOG'; return ( - + + - - - {/* */} + + + + Pengajuan Jaminan + + + + + + + Daftar Request LOG + - {/* */} - + +
+ + + ); } diff --git a/frontend/hospital-portal/src/components/MoreMenu.tsx b/frontend/hospital-portal/src/components/MoreMenu.tsx new file mode 100644 index 00000000..90aed362 --- /dev/null +++ b/frontend/hospital-portal/src/components/MoreMenu.tsx @@ -0,0 +1,58 @@ +import Iconify from '@/components/Iconify'; +import MenuPopover from './MenuPopover'; +import { IconButton, MenuItem } from '@mui/material'; +import { useEffect, useState } from 'react'; + +// ---------------------------------------------------------------------- + +type Props = { + actions: React.ReactNode; +}; + +export default function MoreMenu({ actions }: Props) { + const [open, setOpen] = useState(null); + + // Close menu popover + useEffect(() => { + setOpen(null); + }, [actions]) + + const handleOpen = (event: React.MouseEvent) => { + setOpen(event.currentTarget); + }; + + const handleClose = () => { + setOpen(null); + }; + + return ( + <> + + + + + + {actions} + + + + ); +} diff --git a/frontend/hospital-portal/src/components/MuiDialog.tsx b/frontend/hospital-portal/src/components/MuiDialog.tsx index 54ee1110..e0f07276 100644 --- a/frontend/hospital-portal/src/components/MuiDialog.tsx +++ b/frontend/hospital-portal/src/components/MuiDialog.tsx @@ -1,4 +1,4 @@ -import { Dialog, DialogTitle, DialogContent, Stack, Typography, IconButton } from '@mui/material'; +import { Dialog, DialogTitle, DialogContent, Stack, Typography, IconButton, DialogActions } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import { ReactElement } from 'react'; import Iconify from './Iconify'; @@ -13,12 +13,13 @@ type MuiDialogProps = { openDialog: boolean; setOpenDialog: Function; content?: ReactElement; + action?: ReactElement|null; maxWidth?: string; }; // ---------------------------------------------------------------------- -const MuiDialog = ({ title, openDialog, setOpenDialog, content, maxWidth }: MuiDialogProps) => { +const MuiDialog = ({ title, openDialog, setOpenDialog, content, action, maxWidth }: MuiDialogProps) => { const handleClose = () => { setOpenDialog(false); }; @@ -46,9 +47,15 @@ const MuiDialog = ({ title, openDialog, setOpenDialog, content, maxWidth }: MuiD
+ {content ? content : 'Testing Content Dialog'} + + {action ? ( + {action} + ) : ''} + ); }; diff --git a/frontend/hospital-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx b/frontend/hospital-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx index 32928bc5..1f2d4843 100644 --- a/frontend/hospital-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx +++ b/frontend/hospital-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx @@ -80,7 +80,7 @@ export default function NavbarVertical({ isOpenSidebar, onCloseSidebar }: Props) // console.log(data); // Pastikan user dan user.permissions terdefinisi dan merupakan array - const userPermissions = user?.permissions?.map(permission => permission.name) || []; + // const userPermissions = user?.permissions?.map(permission => permission.name) || []; // Fungsi untuk memeriksa apakah pengguna memiliki izin untuk item tertentu const hasPermission = (permission) => { @@ -88,37 +88,26 @@ export default function NavbarVertical({ isOpenSidebar, onCloseSidebar }: Props) }; // Filter data berdasarkan izin pengguna - const filteredNavConfig = data.map(section => { - if (section.children && section.children.length > 0) { - // Cek apakah ada satu atau lebih children yang memiliki izin - const filteredChildren = section.children.filter(child => hasPermission(child.permission)); + const userPermissions = + user?.permissions?.map(permission => permission.name) || []; - if (filteredChildren.length > 0) { - return { - ...section, - children: filteredChildren - }; - } else { - return null; // Lewati bagian yang tidak memiliki children dengan izin - } - } - // Jika tidak ada children, cek izin untuk section itu sendiri - // console.log(section.permission); - return hasPermission(section.permission) ? section : null; - }).filter(section => section !== null); + const filteredNavConfig = data + .filter(item => + userPermissions.includes(item.permission) && + item.path && item.path.trim() !== '' + ) + .map(item => ({ + items: [ + { + title: item.title, + path: item.path, + icon: ICONS[item.icon], + }, + ], + })); - // console.log(filteredNavConfig); + setNavConfig(filteredNavConfig); - const formattedNavConfig = filteredNavConfig.map(item => ({ - - items: [{ - title: item.title, - path: item.path, - icon: ICONS[item.icon] - }] - })); - - setNavConfig(formattedNavConfig); } catch (error) { console.error('Gagal mengambil konfigurasi navigasi:', error); @@ -127,7 +116,7 @@ export default function NavbarVertical({ isOpenSidebar, onCloseSidebar }: Props) fetchNavConfig(); }, [user]); - console.log(navConfig); + // console.log(navConfig); useEffect(() => { if (isOpenSidebar) { diff --git a/frontend/hospital-portal/src/pages/Dashboard.tsx b/frontend/hospital-portal/src/pages/Dashboard.tsx index 2d537013..d8157f51 100644 --- a/frontend/hospital-portal/src/pages/Dashboard.tsx +++ b/frontend/hospital-portal/src/pages/Dashboard.tsx @@ -7,6 +7,7 @@ import Page from '@/components/Page'; // theme import CardNotification from '@/sections/dashboard/CardNotification'; import CardSearchMember from '@/sections/dashboard/CardSearchMember' +import HeaderBreadcrumbs from '@/components/HeaderBreadcrumbs'; import { useContext, useEffect, useState } from 'react'; import axios from '@/utils/axios'; import { Stack } from '@mui/system'; @@ -15,6 +16,7 @@ import { Input } from '@mui/material'; import TableList from '@/sections/dashboard/TableList'; import { fDate } from '@/utils/formatTime'; import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim'; +import useAuth from '@/hooks/useAuth'; // ---------------------------------------------------------------------- @@ -60,6 +62,15 @@ const defaultPolicyData = { /* -------------------------------------------------------------------------- */ export default function Dashboard() { + const { user } = useAuth(); + +const userPermissions = + user?.permissions?.map(permission => permission.name) || []; + +const canSearchMember = + userPermissions.includes('request-log-hospital-portal'); + + const { themeStretch } = useSettings(); // const [tableData, setTableData] = useState([]); @@ -87,7 +98,24 @@ export default function Dashboard() { - + {canSearchMember ? ( + + ) : ( + + )} + {/* diff --git a/frontend/hospital-portal/src/routes/index.tsx b/frontend/hospital-portal/src/routes/index.tsx index 392871a2..57db2776 100644 --- a/frontend/hospital-portal/src/routes/index.tsx +++ b/frontend/hospital-portal/src/routes/index.tsx @@ -13,6 +13,7 @@ import { AuthProvider } from '@/contexts/LaravelAuthContext'; import AuthGuard from '@/guards/AuthGuard'; import useAuth from '@/hooks/useAuth'; import RoleBasedGuard from '@/guards/RoleBasedGuard'; +import DetailRequestFinalLog from '@/sections/dashboard/DetailRequestFinalLog'; // ---------------------------------------------------------------------- @@ -116,6 +117,14 @@ export default function Router() { ), }, + { + path: '/detail-request-final-log/:id', + element: ( + + + + ), + }, ], }, { diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/CardService.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/CardService.tsx new file mode 100644 index 00000000..0b887ef2 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/CardService.tsx @@ -0,0 +1,105 @@ +import { Card, Typography } from "@mui/material"; +import { Stack } from '@mui/material'; +import { DetailFinalLogType } from "../Model/Types"; +import { fDate, fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import Label from '@/components/Label'; + + + +type CardDetail = { + requestLog: DetailFinalLogType|undefined; + isFinalLog: boolean +} + +const style1 = { + color: '#919EAB', + width: '30%' +} +const style2 = { + width: '70%' +} +const marginBottom1 = { + marginBottom: 1, +} +const marginBottom2 = { + marginBottom: 2, +} + + +export default function CardService({requestLog, isFinalLog = true} : CardDetail ) { + return ( + + Service + + Service Type + {requestLog?.service_type} + + + Claim Method + {toTitleCase(requestLog?.claim_method ?? '-')} + + {/* + Benefit + +
    + {requestLog?.benefit.length > 0 ? requestLog?.benefit.map((r, index) => ( +
  • {r.code } - {r.description}
  • + )) :
  • -
  • } +
+
+
*/} + {/* General Practitioner */} + + General Practitioner + External Doctor : + {requestLog?.config_service?.gp_external_doctor_online == '1' ? () : '-'} + {requestLog?.config_service?.gp_external_doctor_offline == '1' ? () : '-'} + + + + + Internal Doctor : + {requestLog?.config_service?.gp_internal_doctor_online == '1' ? () : '-'} + {requestLog?.config_service?.gp_internal_doctor_offline == '1' ? () : '-'} + + + + {/* Specialist Practitioner */} + + Specialist Practitioner + External Doctor : + {requestLog?.config_service?.sp_external_doctor_online == '1' ? () : '-'} + {requestLog?.config_service?.sp_external_doctor_offline == '1' ? () : '-'} + + + + + Internal Doctor : + {requestLog?.config_service?.gp_internal_doctor_online == '1' ? () : '-'} + {requestLog?.config_service?.gp_internal_doctor_offline == '1' ? () : '-'} + + + + {/* Medicine */} + {/* + Medicine + +
    + {requestLog?.config_service?.vitamins == '1' ? (
  • Suplemen
  • ) : (
  • -
  • )} + {requestLog?.config_service?.delivery_fee == '1' ? (
  • Delivery Fee
  • ) : (
  • -
  • )} +
+
+
*/} + + Admin Fee + + {requestLog?.config_service?.general_practitioner_fee == '1' ? () : '-'} + {requestLog?.config_service?.specialist_practitioner_fee == '1' ? () : '-'} + + + + +
+ ) + +} \ No newline at end of file diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogBenefit.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogBenefit.tsx new file mode 100644 index 00000000..b19297c3 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogBenefit.tsx @@ -0,0 +1,682 @@ +import * as Yup from 'yup'; +import { yupResolver } from '@hookform/resolvers/yup'; + +import MuiDialog from "@/components/MuiDialog"; +import { Checkbox, Typography, FormControl, Card, Grid, DialogActions, IconButton, Autocomplete } from "@mui/material"; +import { Paper } from "@mui/material"; +import { Stack } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { DetailFinalLogType } from "../Model/Types"; +import { BenefitConfigurationListType } from "../Model/Types"; +import { InputLabel, Select, FormHelperText } from "@mui/material"; +import FormGroup from '@mui/material/FormGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Button from '@mui/material/Button'; +import { fNumber } from "@/utils/formatNumber"; +import palette from "@/theme/palette"; +import { Box } from "@mui/material"; +import { FormProvider, RHFTextField } from "@/components/hook-form"; +import RHFTextFieldMoney from '@/components/hook-form/v2/RHFTextFieldMoney'; + +import { useFieldArray, useForm, useWatch } from 'react-hook-form'; +import { LoadingButton } from '@mui/lab'; +import { postAddBenefit } from '../Model/Functions'; +import { useNavigate } from 'react-router'; +import { description } from '@/_mock/text'; +import { Delete } from '@mui/icons-material'; +import { TextField } from '@mui/material'; +import RHFAutocomplete from '@/components/hook-form/RHFAutocomplete'; + + +type DialogConfirmationType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + requestLog: DetailFinalLogType|undefined; + claimInput:boolean +} + +type BenefitSelected = { + id: number, + description: string, + benefit_id: number, + family_plan: string, + family_plan_plans: string, + limit_amount: number, + limit_amount_plan: number, + max_frequency_period: number, +} + + + +export default function DialogBenefit({requestLog, setOpenDialog, openDialog, claimInput = false } : DialogConfirmationType ) { + + // Add Benefit + const [addBenefit, setAddBenefit] = useState(false) + const navigate = useNavigate() + //Benefit Name + const [valBenefitNames, setValBenefitNames] = useState([]); + const [valBenefitNameError, setValBenefitNameError] = useState(''); + const benefitNameData = requestLog?.benefit; + const [benefitSelected, setBenefitSelected] = useState([]); + const [isDisabled, setisDisable] = useState(false); + + const handleConditionChangeService = (event) => { + const selectedItem = event.target.value; + + if (valBenefitNames.includes(selectedItem)) { + // Item is already selected, remove it + setValBenefitNames(valBenefitNames.filter(item => item !== selectedItem)); + } else { + // Item is not selected, add it + setValBenefitNames([...valBenefitNames, selectedItem]); + } + }; + + const reasons = [ + { value: 'Wrong Setting', label: 'Wrong Setting' }, + { value: 'Hospital Request', label: 'Hospital Request' } + ]; + + useEffect(() => { + const datax: any[] = [] + valBenefitNames.map((data) => { + benefitNameData?.map((row) => { + if(row.id == data) { + datax.push(row) + } + }) + }) + + // for data information + let temp = datax.map((item, indx) => { + return { + benefit_id: item.id, + description: item.description, + request_log_id: requestLog?.id, + amount_incurred: 0, + amount_approved: 0, + amount_not_approved: 0, + excess_paid: 0, + keterangan: '', + reason: null + } + }) + + reset({benefit_data: temp}) + + setBenefitSelected(datax) + }, [valBenefitNames]) + + + const handleCloseDialogBenefit = () => { + setOpenDialog(false); + setAddBenefit(false) + setBenefitSelected([]) + setValBenefitNames([]) + } + + const handleAddDialogBenefit = () => { + setAddBenefit(true) + } + + const defaultValues: BenefitConfigurationListType = { + request_log_id: requestLog?.id, + benefit_name: '', + amount_incurred: 0, + amount_approved: 0, + amount_not_approved: 0, + excess_paid: 0, + reason: null + }; + + const validationSchema = Yup.object().shape({ + benefit_data: Yup.array().of( + claimInput ? + Yup.object().shape({ + amount_incurred : Yup.number().typeError('').required(''), + amount_approved : Yup.number().typeError('').required(''), + amount_not_approved : Yup.number().typeError('').required(''), + excess_paid : Yup.number().typeError('').required(''), + // reason : Yup.string().required(''), + }) : + Yup.object().shape({ + amount_incurred : Yup.number().typeError('').required(''), + amount_approved : Yup.number().typeError('').required(''), + amount_not_approved : Yup.number().typeError('').required(''), + excess_paid : Yup.number().typeError('').required(''), + }) + ) + }) + + const methods = useForm({ + resolver: yupResolver(validationSchema), + defaultValues, + reValidateMode: "onChange" + }); + + let width = claimInput ? 2 : 2.36; + + const {fields, append, remove} = useFieldArray({name: 'benefit_data',control: methods.control,}); + const { handleSubmit, reset, watch, setValue, setError, clearErrors, formState: { isDirty, isSubmitting, errors,isValid } } = methods + + const errorsExist = errors ? Object.keys(errors).length > 0 : false; + // Calculate + const benefitData = watch('benefit_data'); + const totalAll = () => { + let totalAmountIncurred = (requestLog?.benefit_data || []).reduce((accumulator, item) => { + return accumulator + (item.amount_incurred || 0); + }, 0); + let totalAmountApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => { + return accumulator + (item.amount_approved || 0); + }, 0); + let totalAmountNotApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => { + return accumulator + (item.amount_not_approved || 0); + }, 0); + let totalExcessPaid = (requestLog?.benefit_data || []).reduce((accumulator, item) => { + return accumulator + (item.excess_paid || 0); + }, 0); + + benefitData?.map((item, index) => { + totalAmountIncurred += parseFloat(item.amount_incurred); + totalAmountApproved += parseFloat(item.amount_approved); + totalAmountNotApproved += parseFloat(item.amount_not_approved); + totalExcessPaid += parseFloat(item.excess_paid); + }); + + return { + totalAmountIncurred, + totalAmountApproved, + totalAmountNotApproved, + totalExcessPaid + } + } + + const totalUsage = () => { + let realTimeUsageMember = 0 + for (let key in requestLog?.member_usage_benefit) { + if (requestLog?.member_usage_benefit.hasOwnProperty(key)) { + let value = requestLog?.member_usage_benefit[key]; + // Menggunakan parseFloat() untuk mengonversi nilai menjadi angka + let numericValue = parseFloat(value); + // Memeriksa apakah numericValue adalah angka yang valid + if (!isNaN(numericValue)) { + realTimeUsageMember += numericValue; + } + } + } + + let totalAmountMember = 0 + for (let key in benefitData) { + // Menambahkan nilai amount_approved ke totalAmount + totalAmountMember += Number(benefitData[key].amount_approved); + } + + return realTimeUsageMember+totalAmountMember + } + + const handleOnChangeNominal = (key) => { + if (benefitSelected[key].family_plan == 'S' || benefitSelected[key].family_plan == 'F'){ + if (requestLog?.member_usage_benefit && benefitSelected[key] && benefitData[key]) { + let limitAmount = Number(benefitSelected[key].limit_amount) || 0; + let limitAmountPlan = Number(benefitSelected[key].limit_amount_plan) || 0; + // Periksa apakah limitAmount Benefit lebih besar dari realTimeUsage + if (limitAmountPlan != 999999999){ + let realTimeUsage = 0; + let value = 0; + for (let key in requestLog?.member_usage_benefit) { + if (requestLog?.member_usage_benefit.hasOwnProperty(key)) { + let value = requestLog?.member_usage_benefit[key]; + // Menggunakan parseFloat() untuk mengonversi nilai menjadi angka + let numericValue = parseFloat(value); + // Memeriksa apakah numericValue adalah angka yang valid + if (!isNaN(numericValue)) { + realTimeUsage += numericValue; + } + } + } + // console.log(benefitData, 'test') + let totalAmount = 0; + + for (let key in benefitData) { + // Menambahkan nilai amount_approved ke totalAmount + totalAmount += Number(benefitData[key].amount_approved); + } + + // Hitung penggunaan waktu nyata + realTimeUsage += totalAmount; + let excess = realTimeUsage - limitAmountPlan + let incurred = Number(benefitData[key].amount_incurred); + + if (realTimeUsage === limitAmountPlan){ + setisDisable(true) + setValue(`benefit_data.${key}.amount_not_approved`, incurred); + } else { + setisDisable(false) + } + if (limitAmountPlan < realTimeUsage) { + setValue(`benefit_data.${key}.amount_not_approved`, excess); + setValue(`benefit_data.${key}.amount_approved`, incurred - excess); + setError(`benefit_data.${key}.amount_approved`, { message: `Total Amount Approve sudah melebihi limit ${ fNumber(limitAmountPlan) } , silakan isikan di Amount Excess` }); + } else if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`benefit_data.${key}.amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`benefit_data.${key}.amount_approved`); + } + } else if (limitAmount != 999999999) { // Periksa apakah limitAmount Benefit lebih besar dari realTimeUsage + // Konversi nilai ke angka dengan aman + let memberUsage = Number(requestLog.member_usage_benefit[benefitSelected[key].id]) || 0; + let amountApproved = Number(benefitData[key].amount_approved) || 0; + // Hitung penggunaan waktu nyata + let realTimeUsage = memberUsage + amountApproved; + let value = realTimeUsage - limitAmount + + if (limitAmount < realTimeUsage) { + setValue(`benefit_data.${key}.amount_not_approved`, value); + setError(`benefit_data.${key}.amount_approved`, { message: `Total Amount Approve sudah melebihi limit ${ fNumber(limitAmount) } , silakan isikan di Amount Excess` }); + } else if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`benefit_data.${key}.amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`benefit_data.${key}.amount_approved`); + } + } else { + if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`benefit_data.${key}.amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`benefit_data.${key}.amount_approved`); + } + } + + if (totalAll().totalAmountApproved + totalAll().totalAmountNotApproved === totalAll().totalAmountIncurred) { + clearErrors(`benefit_data.${key}.excess_paid`); + } else { + setError(`benefit_data.${key}.excess_paid`, { message: 'Total Amount Excess tidak sama dengan Total Amount Incurred' }); + } + } + } else { + if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){ + setError(`benefit_data.${key}.amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'}); + } else { + clearErrors(`benefit_data.${key}.amount_approved`); + } + } + } + + const handleOnChangeNotApprove = (key, value) => { + setValue(`benefit_data.${key}.excess_paid`, value); + if (totalAll().totalAmountApproved + totalAll().totalAmountNotApproved === totalAll().totalAmountIncurred) { + clearErrors(`benefit_data.${key}.excess_paid`); + } else { + setError(`benefit_data.${key}.excess_paid`, { message: 'Total Amount Excess tidak sama dengan Total Amount Incurred' }); + } + }; + + // Submit Form + // ===================================== + const submitHandler = async (data: BenefitConfigurationListType) => { + const mapData = data.benefit_data.map((item) => ({ + ...item, + reason: item.reason ? item.reason.value : null + })); + + const newData = { + ...data, + benefit_data: mapData + }; + + const response = await postAddBenefit(newData); + + if (response == true) { + reset(); + // navigate('custormer-service/final-log/detail/'+requestLog?.id); + window.location.reload() + } + } + + const getContent = () => !addBenefit ? ( + + + + Benefit Name* + + + Benefit Name + + + {valBenefitNameError} + + + + + ) : + + ( + + + {/* */} + {fields?.map((item, index) => + ( + + + + + {item.description} + + + + + + + + Amount Incurred* + + + + { + setValue(`benefit_data.${index}.amount_incurred`, event.target.value) + handleOnChangeNominal(index)} + } + /> + + + + + + + + + Amount Approved* + + + + append({amount_approved: ''}) } + id='amount_approved' + key={item.id} + name={`benefit_data.${index}.amount_approved`} + placeholder='Amount Approved' + required + onChange={(event) => { + setValue(`benefit_data.${index}.amount_approved`, event.target.value) + handleOnChangeNominal(index)} + } + disabled={isDisabled} + /> + + + + + + + + + Amount Not Approved* + + + + append({amount_not_approved: ''}) } + id='amount_not_approved' + key={item.id} + name={`benefit_data.${index}.amount_not_approved`} + placeholder='Amount Not Approved' + required + onChange={(event) => { + setValue(`benefit_data.${index}.amount_not_approved`, event.target.value) + handleOnChangeNotApprove(index, event.target.value)} + } + /> + + + + + + + + + Excess Paid* + + + + append({excess_paid: ''}) } + id='excess_paid' + key={item.id} + name={`benefit_data.${index}.excess_paid`} + placeholder='Excess Paid' + required + /> + + + + + + + + + Keterangan + + + + append({keterangan: ''}) } + id='keterangan' + key={item.id} + name={`benefit_data.${index}.keterangan`} + placeholder='Keterangan' + /> + + + + {claimInput ? ( + + + + + Reason* + + + + option.label} + fullWidth + value={benefitData[index]?.reason} // Use find to match the default value + onChange={(event, newValue) => { + // Update the value in the form data + setValue(`benefit_data.${index}.reason`, newValue); + }} + renderInput={(params) => ( + + )} + /> + + + + + ) : null} + { fields.length > 1 ? ( + + remove(index)}> + + + + ) : null } + + + ))} + +
+
+
+ + + + + + Total Benefit + + + + + + + + + {/* Amount Incurred */} + + + + + Amount Incurred + + + + + {fNumber(totalAll().totalAmountIncurred)} + + + + + + {/* Amount Approved */} + + + + + Amount Approved + + + + + {fNumber(totalAll().totalAmountApproved)} + + + + + + {/* Amount Not Approved */} + + + + + Amount Not Approved + + + + + {fNumber(totalAll().totalAmountNotApproved)} + + + + + + {/* Excess Paid* */} + + + + + Excess Paid + + + + + {fNumber(totalAll().totalExcessPaid)} + + + + + + + + + + + + Total Usage / Limit + + + + + {fNumber(totalUsage())} / {fNumber(benefitSelected[0].limit_amount_plan) } + + + + + + + + + + {/*
*/} + + + + + Save + + + +
+
+ + ); + + const getAction = () => !addBenefit ? ( + + + + + ) : null; + + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogConfirmation.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogConfirmation.tsx new file mode 100644 index 00000000..411adff5 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogConfirmation.tsx @@ -0,0 +1,233 @@ +import MuiDialog from "@/components/MuiDialog"; +import { Autocomplete, Button, Card, DialogActions, Grid, MenuItem, Select, TextField, Typography } from "@mui/material"; +import { Stack } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { DetailFinalLogType } from "../Model/Types"; +import { fDateOnly, fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import axios from "@/utils/axios"; +import { enqueueSnackbar } from "notistack"; +import { useNavigate } from "react-router"; + + +type DialogConfirmationType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + approve: string; + requestLog: DetailFinalLogType|undefined; +} + +export default function DialogConfirmation({requestLog, setOpenDialog, openDialog, approve, onSubmit} : DialogConfirmationType ) { + + const navigate = useNavigate(); + const [formData, setFormData] = useState({ + discharge_date: requestLog?.discharge_date, + id: requestLog?.id, + status: approve || '', + catatan: '', + type_of_member: requestLog?.type_of_member, + // icdCodes: requestLog?.diagnosis.length ? requestLog.diagnosis.map(diagnosis => ({ value: diagnosis.id, label: diagnosis.name })) : [] + icdCodes: requestLog?.diagnosis + }); + + const [error, setError] = useState(false); + + const [icdOptions, setIcdOptions] = useState([ + { value: '-', label: '-' } + ]); + + const [searchIcd, setSearchIcd] = useState(''); + + useEffect(() => { + // Update formData setiap kali approve berubah + setFormData(prevData => ({ + ...prevData, + status: approve || '', + })); + }, [approve]); + + const handleChange = (field, value) => { + setFormData((prevData) => ({ + ...prevData, + [field]: value, + })); + }; + + const handleApprove = () => { + setFormData((prevData) => ({ + ...prevData, + status: approve, + })); + handleSubmit(); + }; + + const handleSearch = (search) => { + setSearchIcd(search); + axios.get('diagnosis?search=' + search) + .then((response) => { + setIcdOptions(response.data.data); + }) + .catch((error) => { + console.error('Error fetching ICD options:', error); + }); + } + + const handleSubmit = () => { + if (formData.type_of_member == "" && requestLog?.corporate_id == 5) { // corporate vale + setError(true); + alert('Silakan pilih Type Of Member sebelum mengirimkan data.'); + } + else { + axios + .post(`customer-service/request/final-log`, formData) + .then((response) => { + enqueueSnackbar('Request Final LOG Successfully', { variant: 'success' }); + setOpenDialog(false); + if (requestLog?.service_type === 'Inpatient') { + navigate('/'); + } else { + navigate('/'); + } + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' }); + }); + } + } + + const style1 = { + color: '#919EAB', + width: '30%' + }; + const style2 = { + width: '70%' + }; + const marginBottom1 = { + marginBottom: 1, + }; + const marginBottom2 = { + marginBottom: 2, + }; + const handleCloseDialog = () => { + setOpenDialog(false); + }; + + const getContent = () => ( + + Are you sure to {approve === 'approved' || approve === 'requested' ? 'request' : 'decline'} this final log? + + + + Member ID + {requestLog?.member_id} + + + Member Of Type + {requestLog?.type_of_member} + + + Policy Number + {requestLog?.policy_number} + + + Name + {requestLog?.name} + + + Submission Date + {requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'} + + + Claim Method + {requestLog?.claim_method ? toTitleCase(requestLog?.claim_method) : '-'} + + + Service Type + {requestLog?.service_type} + + + + + + Discharge Date + handleChange('discharge_date', e.target.value)} + /> + + + Catatan + handleChange('catatan', e.target.value)} + /> + + + Diagnosis ICD - X + option.label} + fullWidth + value={formData.icdCodes} + onChange={(e, newValues) => handleChange('icdCodes', newValues)} + inputValue={searchIcd} + onInputChange={(e, newInputValue) => handleSearch(newInputValue)} + renderInput={(params) => ( + + )} + /> + + + Type Of Member* + + + + + + + + {approve === 'approved' || approve === 'requested' ? ( + + ) : ( + + )} + + + ); + + return ( + + ); +} diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteBenefit.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteBenefit.tsx new file mode 100644 index 00000000..6ff116d9 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteBenefit.tsx @@ -0,0 +1,138 @@ +import MuiDialog from "@/components/MuiDialog"; +import { Autocomplete, Button, Card, Checkbox, DialogActions, Grid, TextField, Typography } from "@mui/material"; +import { Paper } from "@mui/material"; +import { Stack } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { DetailFinalLogType } from "../Model/Types"; +import { fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import axios from "@/utils/axios"; +import { enqueueSnackbar } from "notistack"; +import { useNavigate } from "react-router"; + + +type DialogDeleteType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + id: number|undefined; +} + +export default function DialogDeleteBenefit({id, setOpenDialog, openDialog,onSubmit} : DialogDeleteType ) { + + const [formData, setFormData] = useState({ + reason: null + }); + + const resetForm = () => { + setFormData({ + reason: null, + }); + }; + + useEffect(() => { + // Update formData setiap kali approve berubah + setFormData(prevData => ({ + ...prevData, + })); + }, []); + + const handleChange = (field, value) => { + setFormData((prevData) => ({ + ...prevData, + [field]: value, + })); + if (field === 'reason') { + setIsReasonSelected(!!value); + } + + }; + + const handleSubmit = () => { + if (isReasonSelected && formData.reason !== '') { + axios + .post(`customer-service/request/benefit_data/${id}`, formData) + .then((response) => { + enqueueSnackbar('Benefit Data has Deleted', { variant: 'success' }); + setOpenDialog(false); + window.location.reload() + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' }); + }); + } else { + setIsReasonSelected(false); + alert('Silakan pilih alasan sebelum menghapus data.'); + } + + } + + const style1 = { + color: '#919EAB', + width: '30%' + } + const style2 = { + width: '70%' + } + const marginBottom2 = { + marginBottom: 2, + } + + const [isReasonSelected, setIsReasonSelected] = useState(false); + + const reasons = [ + { value: 'agreement', label: 'Agreement changed' }, + { value: 'endorsement', label: 'Endorsement' }, + { value: 'renewal', label: 'Renewal' }, + { value: 'wrong_setting', label: 'Wrong Setting' }, + // Add more options as needed + ]; + const handleCloseDialog = () => { + setOpenDialog(false); + } + + const getContent = () => ( + + Are you sure to delete this detail benefit ? + + + + Reason* + option.label} + fullWidth + value={reasons.find((r) => r.value === formData.reason) || null} // Use find to match the default value + onChange={(e, newValue) => handleChange('reason', newValue?.value)} + renderInput={(params) => ( + + )} + /> + + + + + + + + + ); + + + return ( + + ); +} diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteFileLog.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteFileLog.tsx new file mode 100644 index 00000000..a4e48c1b --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteFileLog.tsx @@ -0,0 +1,144 @@ +import MuiDialog from "@/components/MuiDialog"; +import { Autocomplete, Button, Card, Checkbox, DialogActions, Grid, Typography } from "@mui/material"; +import { Paper } from "@mui/material"; +import { Stack } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import axios from "@/utils/axios"; +import { enqueueSnackbar } from "notistack"; +import { useNavigate } from "react-router"; +import { TextField } from "@mui/material"; + + +type DialogDeleteType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + id: number|undefined; + path: string; +} + +export default function DialogDeleteFileLog({id, path, setOpenDialog, openDialog,onSubmit} : DialogDeleteType ) { + const style1 = { + color: '#919EAB', + width: '30%' + } + const style2 = { + width: '70%' + } + const marginBottom2 = { + marginBottom: 2, + } + + const handleCloseDialog = () => { + setOpenDialog(false); + resetForm(); + } + + const [isReasonSelected, setIsReasonSelected] = useState(false); + + const reasons = [ + { value: 'agreement', label: 'Agreement changed' }, + { value: 'endorsement', label: 'Endorsement' }, + { value: 'renewal', label: 'Renewal' }, + { value: 'wrong_setting', label: 'Wrong Setting' }, + // Add more options as needed + ]; + const [formData, setFormData] = useState({ + path: path, + reason: null + }); + + const resetForm = () => { + setFormData({ + reason: null, + path: path + }); + }; + + useEffect(() => { + // Update formData setiap kali approve berubah + setFormData(prevData => ({ + ...prevData, + path: path || '', + })); + }, [path]); + + const handleChange = (field, value) => { + setFormData((prevData) => ({ + ...prevData, + [field]: value, + })); + if (field === 'reason') { + setIsReasonSelected(!!value); + } + + }; + + const handleSubmit = () => { + if (isReasonSelected && formData.reason !== '') { + console.log(formData) + axios + .post(`customer-service/request/${id}/delete_file`, formData) + .then((response) => { + enqueueSnackbar('File LOG has Deleted', { variant: 'success' }); + setOpenDialog(false); + window.location.reload() + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' }); + }); + } else { + setIsReasonSelected(false); + alert('Silakan pilih alasan sebelum menghapus data.'); + } + + } + + + const getContent = () => ( + + Are you sure to delete this file Final LOG ? + + + + Reason* + option.label} + fullWidth + value={reasons.find((r) => r.value === formData.reason) || null} // Use find to match the default value + onChange={(e, newValue) => handleChange('reason', newValue?.value)} + renderInput={(params) => ( + + )} + /> + + + + + + + + + ); + + + return ( + + ); +} diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteMedicine.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteMedicine.tsx new file mode 100644 index 00000000..c8c04e92 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogDeleteMedicine.tsx @@ -0,0 +1,69 @@ +import MuiDialog from "@/components/MuiDialog"; +import { Button, Card, Checkbox, DialogActions, Grid, Typography } from "@mui/material"; +import { Paper } from "@mui/material"; +import { Stack } from '@mui/material'; +import React, { useState } from 'react'; +import { DetailFinalLogType } from "../Model/Types"; +import { fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import axios from "@/utils/axios"; +import { enqueueSnackbar } from "notistack"; +import { useNavigate } from "react-router"; + + +type DialogDeleteType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + id: number|undefined; +} + +export default function DialogDeleteMedicine({id, setOpenDialog, openDialog,onSubmit} : DialogDeleteType ) { + const handleSubmit = () => { + axios + .delete(`customer-service/request/medicine-data/${id}`) + .then((response) => { + enqueueSnackbar('Medicine Data has Deleted', { variant: 'success' }); + setOpenDialog(false); + window.location.reload() + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' }); + }); + } + + const style1 = { + color: '#919EAB', + width: '30%' + } + const style2 = { + width: '70%' + } + const marginBottom1 = { + marginBottom: 1, + } + + const handleCloseDialog = () => { + setOpenDialog(false); + } + + const getContent = () => ( + + Are you sure to delete this detail medicine ? + + + + + + ); + + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogEditBenefit.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogEditBenefit.tsx new file mode 100644 index 00000000..8af53a7e --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogEditBenefit.tsx @@ -0,0 +1,448 @@ +import * as Yup from 'yup'; +import { yupResolver } from '@hookform/resolvers/yup'; + +import MuiDialog from "@/components/MuiDialog"; +import { Autocomplete, Box, Button, Card, Checkbox, DialogActions, Grid, TextField, Typography } from "@mui/material"; +import { Paper } from "@mui/material"; +import { Stack } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { DetailFinalLogType } from "../Model/Types"; +import { fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import axios from "@/utils/axios"; +import { enqueueSnackbar } from "notistack"; +import { useNavigate } from "react-router"; +import { BenefitConfigurationListType } from "../Model/Types"; +import { postEditBenefit } from "../Model/Functions"; +import { useForm } from 'react-hook-form'; +import { FormProvider, RHFTextField } from "@/components/hook-form"; +import RHFTextFieldMoney from "@/components/hook-form/v2/RHFTextFieldMoney"; +import { LoadingButton } from "@mui/lab"; +import { find } from 'lodash'; +import { fNumber } from '@/utils/formatNumber'; +import palette from '@/theme/palette'; + + +type DialogDeleteType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + data: BenefitConfigurationListType|undefined; + id: number|undefined; + total: any +} + +type BenefitSelected = { + id: number, + description: string, + benefit_id: number, + family_plan: string, + limit_amount: number, +} + +export default function DialogEditBenefit({id, data, setOpenDialog, openDialog, onSubmit, total} : DialogDeleteType ) { + const handleCloseDialog = () => { + setOpenDialog(false); + } + const [benefitSelected, setBenefitSelected] = useState([]); + + // setup form + // ==================================== + const defaultValues: BenefitConfigurationListType = { + request_log_id: 0, + benefit_name: '', + amount_incurred: 0, + amount_approved: 0, + amount_not_approved: 0, + excess_paid: 0, + keterangan: '-', + description: '-', + reason: null + }; + + const validationSchema = Yup.object().shape({ + amount_incurred : Yup.string().typeError('').required(''), + amount_approved : Yup.string().typeError('').required(''), + amount_not_approved : Yup.string().typeError('').required(''), + excess_paid : Yup.string().typeError('').required(''), + }); + + const methods = useForm({ + resolver: yupResolver(validationSchema), + defaultValues + }); + + const { handleSubmit, reset, watch, setValue, setError, clearErrors, formState: { isDirty, isSubmitting, errors } } = methods; + const errorsExist = errors ? Object.keys(errors).length > 0 : false; + const totalAll = () => { + // Ambil nilai dari form menggunakan watch + const amountIncurred = parseFloat(watch('amount_incurred')); + const amountApproved = parseFloat(watch('amount_approved')); + const amountNotApproved = parseFloat(watch('amount_not_approved')); + const excessPaid = parseFloat(watch('excess_paid')); + + // Hitung total baru + const totalAmountIncurred = total.totalAmountIncurred - data?.amount_incurred + amountIncurred; + const totalAmountApproved = total.totalAmountApproved - data?.amount_approved + amountApproved; + const totalAmountNotApproved = total.totalAmountNotApproved - data?.amount_not_approved + amountNotApproved; + const totalExcessPaid = total.totalExcessPaid - data?.excess_paid + excessPaid; + + return { + totalAmountIncurred, + totalAmountApproved, + totalAmountNotApproved, + totalExcessPaid + } + } + + const findItemById = (id) => { + return total.benefit.find(item => item.id === id); + } + + const handleOnChangeNominal = (key) => { + let benefitData = findItemById(data?.benefit_id) + if (benefitData.family_plan == 'S' || benefitData.family_plan == 'F'){ + // Konversi nilai ke angka dengan aman + let limitAmount = Number(benefitData.limit_amount) || 0; + let limitAmountPlan = Number(benefitData.limit_amount_plan) || 0; + + if (limitAmountPlan != 999999999){ + let realTimeUsage = totalAll().totalAmountApproved; + console.log(limitAmountPlan, 'test') + if (limitAmountPlan < realTimeUsage) { + setError(`amount_approved`, { message: `Total Amount Approve sudah melebihi limit ${ fNumber(limitAmountPlan) } , silakan isikan di Amount Excess` }); + } else if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`amount_approved`); + } + } else if (limitAmount != 999999999) { + let memberUsage = Number(total.totalLimit[benefitData.id]) || 0; + let amountApproved = Number(parseFloat(watch('amount_approved'))) || 0; + // Hitung penggunaan waktu nyata + let realTimeUsage = memberUsage + amountApproved; + // Periksa apakah limitAmount lebih besar dari realTimeUsage + if (limitAmount < realTimeUsage) { + setError(`amount_approved`, { message: `Total Amount Approve sudah melebihi limit ${ fNumber(limitAmount) } , silakan isikan di Amount Excess` }); + } else if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){ + // setValue(`benefit_data.${key}.amount_approved`, 0); + setError(`amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'}); + } else { + clearErrors(`amount_approved`); + } + } else { + if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`amount_approved`); + } + } + } else { + if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){ + setError(`amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'}); + } else { + clearErrors(`amount_approved`); + } + } + } + + const handleOnChangeNotApprove = (key, value) => { + let amountApproved = Number(parseFloat(watch('amount_approved'))) || 0; + let amountNotApproved = Number(parseFloat(watch('amount_not_approved'))) || 0; + let amountIncurred = Number(parseFloat(watch('amount_incurred'))) || 0; + setValue(`excess_paid`, value); + console.log(amountApproved + amountNotApproved, amountIncurred, 'test') + if ((amountApproved + amountNotApproved) !== amountIncurred) { + setError(`amount_not_approved`, {message: 'Amount Not Approve tidak sama dengan total Amount Incurred'}); + } else { + clearErrors(`amount_not_approved`); + } + }; + + // if (totalAmountIncurred !== (totalAmountApproved+totalAmountNotApproved)){ + // // alert('Total Incurred tidak sama dengan Total Approve + Total Not Approve') + // // setValue('amount_approved', data?.amount_approved) + // } + + // Submit Form + // ===================================== + const submitHandler = async (data: BenefitConfigurationListType) => { + + const response = await postEditBenefit(id, data); + + if (response == true) { + reset(); + // navigate('custormer-service/final-log/detail/'+requestLog?.id); + window.location.reload() + } + } + + const reasons = [ + { value: 'Wrong Setting', label: 'Wrong Setting' }, + { value: 'Hospital Request', label: 'Hospital Request' } + ]; + + // Set Value Form + // ===================================== + useEffect(() => { + setValue('amount_incurred', data?.amount_incurred) + setValue('amount_approved', data?.amount_approved) + setValue('amount_not_approved', data?.amount_not_approved) + setValue('excess_paid', data?.excess_paid) + setValue('keterangan', data?.keterangan) + setValue('reason', data?.reason) + }, [data]) + + + const getContent = () => ( + + + {/* */} + + + + + {data?.benefit?.description} + + + + + + + + Amount Incurred* + + + + { + setValue(`amount_incurred`, event.target.value) + handleOnChangeNominal(id)} + } + /> + + + + + + + + + Amount Approved* + + + + append({amount_approved: ''}) } + id='amount_approved' + key={id} + name={`amount_approved`} + placeholder='Amount Approved' + required + onChange={(event) => { + setValue(`amount_approved`, event.target.value) + handleOnChangeNominal(id)} + } + /> + + + + + + + + + Amount Not Approved* + + + + append({amount_not_approved: ''}) } + id='amount_not_approved' + key={id} + name={`amount_not_approved`} + placeholder='Amount Not Approved' + required + onChange={(event) => { + setValue(`amount_not_approved`, event.target.value) + handleOnChangeNotApprove(id, event.target.value)} + } + /> + + + + + + + + + Excess Paid* + + + + append({excess_paid: ''}) } + id='excess_paid' + key={id} + name={`excess_paid`} + placeholder='Excess Paid' + required + /> + + + + + + + + + Keterangan + + + + append({keterangan: ''}) } + id='keterangan' + key={id} + name={`keterangan`} + placeholder='Keterangan' + /> + + + + + + + + Reason* + + + + option.label} + fullWidth + value={reasons.find((r) => r.value === watch('reason')) || null}// Use find to match the default value + onChange={(event, newValue) => { + setValue(`reason`, newValue?.value); + }} + renderInput={(params) => ( + + )} + /> + + + + + + + + Total Current Benefit + + + + + + + + {/* Amount Incurred */} + + + + + Amount Incurred + + + + + {totalAll().totalAmountIncurred ? fNumber(totalAll().totalAmountIncurred) : 0} + + + + + + {/* Amount Approved */} + + + + + Amount Approved + + + + + {totalAll().totalAmountApproved ? fNumber(totalAll().totalAmountApproved) : 0} + + + + + + {/* Amount Not Approved */} + + + + + Amount Not Approved + + + + + {totalAll().totalAmountNotApproved ? fNumber(totalAll().totalAmountNotApproved) : 0} + + + + + + {/* Excess Paid* */} + + + + + Excess Paid + + + + + {totalAll().totalExcessPaid ? fNumber(totalAll().totalExcessPaid) : 0} + + + + + + + + + + {/* */} + + + + + Save + + + + + + ); + + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogEditFinalLOG.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogEditFinalLOG.tsx new file mode 100644 index 00000000..3dfb0898 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogEditFinalLOG.tsx @@ -0,0 +1,318 @@ +import MuiDialog from "@/components/MuiDialog"; +import { Autocomplete, Button, Card, Checkbox, DialogActions, Grid, TextField, Typography, Select } from "@mui/material"; +import { Paper } from "@mui/material"; +import { Stack, MenuItem } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { DetailFinalLogType } from "../Model/Types"; +import { fDateOnly, fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import axios from "@/utils/axios"; +import { enqueueSnackbar } from "notistack"; +import { useNavigate } from "react-router"; + + +type DialogConfirmationType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + requestLog: DetailFinalLogType|undefined; +} + +export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialog, onSubmit} : DialogConfirmationType ) { + + const navigate = useNavigate(); + const [formData, setFormData] = useState({ + billing_no: requestLog?.billing_no, + invoice_no: requestLog?.invoice_no, + discharge_date: requestLog?.discharge_date, + id: requestLog?.id, + catatan: requestLog?.catatan, + icdCodes: requestLog?.diagnosis, + reason: requestLog?.reason, + type_of_member: requestLog?.type_of_member, + status: 'requested', + }); + + const [error, setError] = useState(false); + + const [icdOptions, setIcdOptions] = useState([ + { value: '-', label: '-' } + ]); + + const [searchIcd, setSearchIcd] = useState(''); + + useEffect(() => { + // Ambil data dari API dan atur opsi ICD + axios.get('diagnosis') + .then((response) => { + setIcdOptions(response.data.data); + }) + .catch((error) => { + console.error('Error fetching ICD options:', error); + }); + + }, []); // useEffect dijalankan hanya sekali saat komponen dimount + + useEffect(() => { + setFormData({ + discharge_date: requestLog?.discharge_date|| '', + billing_no: requestLog?.billing_no|| '', + invoice_no: requestLog?.invoice_no|| '', + id: requestLog?.id|| 0, + catatan: requestLog?.catatan|| '', + icdCodes: requestLog?.diagnosis|| [], + reason: requestLog?.reason|| '', + type_of_member: requestLog?.type_of_member|| '', + status: 'requested', + }); + }, [requestLog]); + + + const handleChange = (field, value) => { + setFormData((prevData) => ({ + ...prevData, + [field]: value, + })); + if (field === 'reason') { + setIsReasonSelected(!!value); + } + + }; + + const handleApprove = () => { + setFormData((prevData) => ({ + ...prevData, + })); + handleSubmit(); + }; + + const handleSearch = (search) => { + setSearchIcd(search); + axios.get('diagnosis?search=' + search) + .then((response) => { + setIcdOptions(response.data.data); + }) + .catch((error) => { + console.error('Error fetching ICD options:', error); + }); + } + + + const handleSubmit = () => { + if (formData.type_of_member == "" && requestLog?.corporate_id == 5) { + setError(true); + alert('Silakan pilih Type Of Member sebelum mengirimkan data.'); + } + else if (isReasonSelected && formData.reason !== '') { + axios + .post(`customer-service/request/final-log`, formData) + .then((response) => { + enqueueSnackbar('Request Final LOG Success', { variant: 'success' }); + setOpenDialog(false); + navigate('/detail-request-final-log/' + requestLog?.id) + window.location.reload() + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' }); + }); + } else { + setIsReasonSelected(false); + alert('Silakan pilih alasan sebelum mengirimkan data.'); + } + } + + const style1 = { + color: '#919EAB', + width: '30%' + } + const style2 = { + width: '70%' + } + const marginBottom1 = { + marginBottom: 1, + } + const marginBottom2 = { + marginBottom: 2, + } + + const resetForm = () => { + setFormData({ + discharge_date: requestLog?.discharge_date ?? '', + id: requestLog?.id ?? 0, + billing_no: requestLog?.billing_no ?? '', + invoice_no: requestLog?.invoice_no ?? '', + catatan: requestLog?.catatan ?? '', + icdCodes: requestLog?.diagnosis ?? [], + reason: requestLog?.reason ?? '', + type_of_member: requestLog?.type_of_member ?? '', + status: 'requested' + }); + }; + const [isReasonSelected, setIsReasonSelected] = useState(true); + + const handleCloseDialog = () => { + setOpenDialog(false); + resetForm(); + } + + const reasons = [ + { value: 'agreement', label: 'Agreement changed' }, + { value: 'endorsement', label: 'Endorsement' }, + { value: 'renewal', label: 'Renewal' }, + { value: 'wrong_setting', label: 'Wrong Setting' }, + // Add more options as needed + ]; + +// console.log(formData.type_of_member) + + const getContent = () => ( + + Are you sure to edit this final log ? + + + + Member ID + {requestLog?.member_id} + + + Policy Number + {requestLog?.policy_number} + + + Name + {requestLog?.name} + + + Submission Date + {requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'} + + + Claim Method + {requestLog?.claim_method ? toTitleCase(requestLog?.claim_method) : '-'} + + + Service Type + {requestLog?.service_type} + + + + + + Invoice Provider + handleChange('invoice_no', e.target.value)} + /> + + + Billing Number + handleChange('billing_no', e.target.value)} + /> + + + Type Of Member* + + + + Discharge Date + handleChange('discharge_date', e.target.value)} + /> + + + Catatan + handleChange('catatan', e.target.value)} + /> + + + Diagnosis ICD - X + option.label} + fullWidth + value={formData.icdCodes} + onChange={(e, newValues) => handleChange('icdCodes', newValues)} + inputValue={searchIcd} + onInputChange={(e, newInputValue) => handleSearch(newInputValue)} + renderInput={(params) => ( + + )} + /> + + + Reason* + option.label} + fullWidth + value={reasons.find((r) => r.value == formData.reason) || null} // Use find to match the default value + onChange={(e, newValue) => handleChange('reason', newValue?.value)} + renderInput={(params) => ( + + )} + /> + + + + + + + + + ); + + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogMedicine.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogMedicine.tsx new file mode 100644 index 00000000..0b9d01ed --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogMedicine.tsx @@ -0,0 +1,171 @@ +import * as Yup from 'yup'; +import { yupResolver } from '@hookform/resolvers/yup'; + +import MuiDialog from "@/components/MuiDialog"; +import { Button, Autocomplete, Card, Checkbox, DialogActions, Grid, Typography } from "@mui/material"; +import { Paper } from "@mui/material"; +import { Stack } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { DetailFinalLogType } from "../Model/Types"; +import { MedicineType } from "../Model/Types"; +import { fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import { useFieldArray, useForm } from 'react-hook-form'; +import { FormProvider, RHFDatepicker, RHFSelect, RHFTextField } from '@/components/hook-form'; + +import axios from "@/utils/axios"; +import { enqueueSnackbar } from "notistack"; +import { useNavigate } from "react-router"; +import { LoadingButton } from '@mui/lab'; +import AddIcon from '@mui/icons-material/Add'; +import RemoveIcon from '@mui/icons-material/Remove'; +import RHFTextFieldMoney from "@/components/hook-form/v2/RHFTextFieldMoney"; +import { IconButton } from '@mui/material'; +import { postAddMedince } from '../Model/Functions'; + +type DialogConfirmationType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + requestLog: DetailFinalLogType|undefined; +} + +export default function DialogMedicine({requestLog, setOpenDialog, openDialog } : DialogConfirmationType ) { + const handleCloseDialogMedicine = () => { + setOpenDialog(false); + } + + const requestID = requestLog?.id + + const defaultValues: MedicineType = { + medicine : [{ + id: 0, + medicine_name: '', + medicine_price: 0, + request_log_id: requestID, + medicine: '', // input to database + price: 0, // input to database + }], + }; + + const validationSchema = Yup.object().shape({ + medicine: Yup.array().of( + Yup.object().shape({ + medicine_name : Yup.string().typeError('').required(''), + medicine_price : Yup.number().typeError('').required(''), + request_log_id : Yup.number().typeError('').required(''), + }) + ) + }) + + const methods = useForm({ + resolver: yupResolver(validationSchema), + defaultValues + }); + + const {fields, append, remove} = useFieldArray({name: 'medicine',control: methods.control}) + + useEffect(() => { + let temp = fields.map((item, i) => { + return { + medicine_name: 'test', + medicine_price: 0, + request_log_id: 3, + } + }) + + reset({medicine: temp}) + }, []) + + + + const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods; + // Submit Form + // ===================================== + const submitHandler = async (data: MedicineType) => { + const response = await postAddMedince(data); + + if (response == true) { + reset(); + // navigate('custormer-service/final-log/detail/'+requestLog?.id); + window.location.reload() + } + } + + const getContent = () => ( + + + + {/* Medicine */} + + + Medicine* + + + + + {fields.map((field, index) => ( + + + + + + + + { + index != (fields.length-1) ? + ( + + remove(index)}> + + + + ) : null + } + + + ))} + + + + + + + + Add + + + + + + ); + + const getAction = () => null; + + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogSendWa.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogSendWa.tsx new file mode 100644 index 00000000..5a48361d --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogSendWa.tsx @@ -0,0 +1,185 @@ +import { Stack, Typography, Button, Paper, Grid, IconButton, TextField } from "@mui/material"; +import MuiDialog from "@/components/MuiDialog"; +import { fDate, fDateTimesecond } from '@/utils/formatTime'; +import { ContentCopy, WhatsApp, Instagram, Facebook, Telegram } from "@mui/icons-material"; + +type DialogConfirmationType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + requestLog: any; + shareLink: boolean; +}; + +export default function DialogSendWa({ + requestLog, + setOpenDialog, + openDialog, + shareLink = false, +}: DialogConfirmationType) { + const data = { + provider: requestLog?.provider || "LOG", + memberId: requestLog?.member_id || "-", + policyNumber: requestLog?.policy_number || "-", + name: requestLog?.name || "-", + submissionDate: requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : "-", + claimMethod: requestLog?.claim_method || "-", + serviceType: requestLog?.service_type || "-", + linkApproval: requestLog?.url_approval || "https://example.com/approval-link", + }; + + const getContent = () => ( + + Are you sure want to send this request ? + + + + + Member ID + + + + {data.memberId} + + + + + Policy Number + + + + {data.policyNumber} + + + + + Name + + + + {data.name} + + + + + Submission Date + + + + {data.submissionDate} + + + + + Claim Method + + + + {data.claimMethod} + + + + + Service Type + + + + {data.serviceType} + + + + {shareLink ? ( + <> + Share this link only with authorized parties! + {/* + + + + + + + + + + + + + */} + + or copy link + + + + + + ): null } + + ); + + const getAction = () => { + if (shareLink) { + return ( + + + + ); + } + + const handleSend = () => { + const message = `*Request Approval* + Yth. Bapak/Ibu, Nama Penerima + Mohon persetujuan atas data berikut: + + Provider: *${data.provider}* + Member ID: ${data.memberId} + Nama: ${data.name} + Policy Number: ${data.policyNumber} + Submission Date: ${data.submissionDate} + Claim Method: ${data.claimMethod} + Service Type: ${data.serviceType} + + Silakan klik link berikut untuk approval: + ${data.linkApproval}`; + + const encodedMessage = encodeURIComponent(message); + const waUrl = `https://wa.me/6283807417196?text=${encodedMessage}`; + window.open(waUrl, "_blank"); + }; + + return ( + + + + + ); + }; + + return ( + + ); +} diff --git a/frontend/hospital-portal/src/sections/dashboard/Components/DialogUploadFileFinalLog.tsx b/frontend/hospital-portal/src/sections/dashboard/Components/DialogUploadFileFinalLog.tsx new file mode 100644 index 00000000..d755d0fc --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Components/DialogUploadFileFinalLog.tsx @@ -0,0 +1,322 @@ +import { styled } from '@mui/material/styles'; +import Iconify from '@/components/Iconify'; +import { fCurrency } from '@/utils/formatNumber'; +import { LoadingButton } from '@mui/lab'; +import { Avatar, Button, Divider, LinearProgress, linearProgressClasses, ButtonBase, Box } 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, useContext, useEffect } from 'react'; +import { makeFormData } from '@/utils/jsonToFormData'; +import { format } from 'date-fns'; +// import { LanguageContext } from '@/contexts/LanguageContext'; +import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import TextField from '@mui/material/TextField'; +import MuiDialog from '@/components/MuiDialog'; + +type DialogUploadType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + id: number|undefined; +} +export default function DialogUploadFileFinalLog({ id, openDialog, setOpenDialog }: DialogUploadType) { + // ---------------------------------------------------------------------- + // Files Diagnosa + + const fileDiagnosaInput = useRef(null); + const [fileDiagnosas, setFileDiagnosas] = useState([]); + + const handleDiagnosaInputChange = (event:any) => { + if (event.target.files[0]) { + setFileDiagnosas([...fileDiagnosas, ...event.target.files]); + } else { + console.log('NO FILE'); + } + }; + const removeDiagnosaFiles = (filesState:any, index:any) => { + setFileDiagnosas( + filesState.filter((file:any, fileIndex:any) => { + return fileIndex != index; + }) + ); + }; + + // ---------------------------------------------------------------------- + // Files Result Kondisi + + const fileKondisiInput = useRef(null); + const [fileKondisis, setFileKondisis] = useState([]); + + const handleKondisiInputChange = (event:any) => { + if (event.target.files[0]) { + setFileKondisis([...fileKondisis, ...event.target.files]); + } else { + console.log('NO FILE'); + } + }; + const removeKondisiFiles = (filesState:any, index:any) => { + setFileKondisis( + filesState.filter((file:any, fileIndex:any) => { + return fileIndex != index; + }) + ); + }; + + // ---------------------------------------------------------------------- + // Files Result Hasil Penunjang + + const fileHasilPenunjangInput = useRef(null); + const [fileHasilPenunjangs, setFileHasilPenunjangs] = useState([]); + + const handleResultInputChange = (event:any) => { + if (event.target.files[0]) { + setFileHasilPenunjangs([...fileHasilPenunjangs, ...event.target.files]); + } else { + console.log('NO FILE'); + } + }; + const removeFiles = (filesState:any, index:any) => { + setFileHasilPenunjangs( + filesState.filter((file:any, fileIndex:any) => { + return fileIndex != index; + }) + ); + }; + + // -------------------------------------------------------------- + // Submit Form + const [submitLoading, setSubmitLoading] = useState(false); + function submitRequestFinalLog() { + setSubmitLoading(true); + const formData = makeFormData({ + request_logs_id: id, + result_files: fileHasilPenunjangs, + diagnosa_files: fileDiagnosas, + kondisi_files: fileKondisis, + }); + axios + .post(`/customer-service/request/${id}/add_file`, formData) + .then((response) => { + enqueueSnackbar('Berhasil membuat data', { variant: 'success' }); + setOpenDialog(false); + window.location.reload() + }) + .catch(({ response }) => { + enqueueSnackbar('Something Went Wrong', { variant: 'error' }); + }) + .then(() => { + setSubmitLoading(false); + }); + } + + const getContent = () => ( + + } + spacing={4} + sx={{ marginY: 2, marginBottom: 6 }} + > + {/* -------------------------------Upload Dokumen Kondisi------------------------------- */} + + + File Billing + + {/* Hasil Lab, */} + } + spacing={1} + sx={{ marginY: 2 }} + > + {fileKondisis && + fileKondisis.map((file:any, index:any) => ( + + {file.name} + { + removeKondisiFiles(fileKondisis, index); + }} + > + + ))} + + fileKondisiInput.current?.click()}> + + + + Upload + + + + + + + {/* -------------------------------Upload Dokumen Diagnosa------------------------------- */} + + + File Diagnosa + + {/* Hasil Lab, */} + } + spacing={1} + sx={{ marginY: 2 }} + > + {fileDiagnosas && + fileDiagnosas.map((file:any, index:any) => ( + + {file.name} + { + removeDiagnosaFiles(fileDiagnosas, index); + }} + > + + ))} + {/* + Nama File .pdf + + */} + + {/* { JSON.stringify(filesResult) } */} + fileDiagnosaInput.current?.click()}> + + + + Upload + + + + + + + {/* -------------------------------Upload Dokumen Hasil Penunjang------------------------------- */} + + + File Hasil Penunjang Medis + + {/* Hasil Lab, */} + } + spacing={1} + sx={{ marginY: 2 }} + > + {fileHasilPenunjangs && + fileHasilPenunjangs.map((file:any, index:any) => ( + + {file.name} + { + removeFiles(fileHasilPenunjangs, index); + }} + > + + ))} + {/* + Nama File .pdf + + */} + + {/* { JSON.stringify(filesResult) } */} + fileHasilPenunjangInput.current?.click()}> + + + + Upload + + + + + + + { + submitRequestFinalLog(); + }} + loading={submitLoading} + > + Upload File + + + ) + return ( + + ); +} diff --git a/frontend/hospital-portal/src/sections/dashboard/DetailRequestFinalLog.tsx b/frontend/hospital-portal/src/sections/dashboard/DetailRequestFinalLog.tsx new file mode 100644 index 00000000..5075a24d --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/DetailRequestFinalLog.tsx @@ -0,0 +1,1337 @@ +import * as Yup from 'yup'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { NumericFormat } from "react-number-format"; +import { + Container, + Grid, + Stack, + Typography, + Card, + Dialog, + TableRow, + Tab, + TableCell, + Collapse, + AccordionSummary, + AccordionDetails, + IconButton, + Divider, + ButtonBase + } from '@mui/material'; +// components +import Page from '@/components/Page'; +import Iconify from '@/components/Iconify'; +import { FormProvider, RHFDatepicker, RHFSelect, RHFTextField } from '@/components/hook-form'; +import RHFTextFieldMoney from '@/components/hook-form/v2/RHFTextFieldMoney'; +import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +// utils +import useSettings from '@/hooks/useSettings'; +import { useFieldArray, useForm } from 'react-hook-form'; +// react +import { useNavigate, useParams, useLocation } from 'react-router-dom'; +import { useEffect, useState, useRef, useMemo } from 'react'; +import axios from '@/utils/axios'; +import { enqueueSnackbar } from 'notistack'; +// pages +import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; +import { DetailFinalLogType } from './Model/Types' +import { fDate, fDateTimesecond } from '@/utils/formatTime'; +import { Button, Autocomplete, FormHelperText} from '@mui/material'; +import DialogConfirmation from './Components/DialogConfirmation'; +import Label from '@/components/Label'; +import { Box } from '@mui/system'; +import { Accordion } from '@mui/material'; +import { Delete, EditOutlined, ExpandMore } from '@mui/icons-material'; +import {BenefitData } from './Model/Types' +import AddIcon from '@mui/icons-material/Add'; +import { LoadingButton } from '@mui/lab'; +import { makeFormData } from '@/utils/jsonToFormData'; +import TextField from '@mui/material/TextField'; +import { fPostFormat } from '@/utils/formatTime'; + + + + +// Import Card Detail Final LOG +import CardService from './Components/CardService'; + +// Import Dialog +import DialogBenefit from './Components/DialogBenefit'; +import DialogMedicine from './Components/DialogMedicine'; +import DialogDeleteBenfit from './Components/DialogDeleteBenefit'; +import DialogEditBenefit from './Components/DialogEditBenefit'; +import DialogDeleteMedicine from './Components/DialogDeleteMedicine' + +import MoreMenu from '@/components/MoreMenu'; +import { MenuItem } from '@mui/material'; +import { fNumber } from '@/utils/formatNumber'; +import palette from '@/theme/palette'; +import DialogEditFinalLOG from './Components/DialogEditFinalLOG'; +import DialogDeleteFileLog from './Components/DialogDeleteFileLog'; +import DialogUploadFileFinalLog from './Components/DialogUploadFileFinalLog'; +import DialogSendWa from './Components/DialogSendWa'; +import { format,parse } from 'date-fns'; +import HeaderBreadcrumbs from '@/components/HeaderBreadcrumbs'; + + + +// ---------------------------------------------------------------------- + +export default function DetailRequestFinalLog() { + const { state } = useLocation() + + const { + Log_id, + full_name, + no_polis, + submission_date, + service_code, + member_id, + specialities_id, + dppj, + } = state || {} + + + + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); + + const navigate = useNavigate(); + const { themeStretch } = useSettings(); + const [requestLog, setRequestLog] = useState(); + const [isReversal, setIsReversal] = useState(false); + const [submitLoading, setSubmitLoading] = useState(false); + + const defaultValues: any = {nominal : 0}; + const validationSchema = Yup.object().shape({nominal: Yup.number().typeError('Nominal harus berupa angka').required('Nominal harus diisi')}) + + const methods = useForm({ + resolver: yupResolver(validationSchema), + defaultValues + }); + + const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods; + + const onSubmit = async (data: any) => { + setSubmitLoading(true); + const formData = makeFormData({ + request_logs_id: id, + approval_files: fileApprovals, + nominal: data.nominal, + }); + axios + .post(`/customer-service/request/${id}/approval_files`, formData) + .then((response) => { + enqueueSnackbar('Berhasil membuat data', { variant: 'success' }); + + window.location.reload() + }) + .catch(({ response }) => { + enqueueSnackbar('Something Went Wrong', { variant: 'error' }); + }) + .then(() => { + setSubmitLoading(false); + }); + } + + const updateApproval = async () => { + setSubmitLoading(true); + axios + .put(`/customer-service/request/${id}`, { + status_approval: 'approved', + }) + .then((response) => { + enqueueSnackbar('Berhasil Approve', { variant: 'success' }); + window.location.reload(); + }) + .catch(({ response }) => { + enqueueSnackbar(response?.data?.message || 'Something Went Wrong', { variant: 'error' }); + }) + .finally(() => { + setSubmitLoading(false); + }); + }; + + + const updateDecline = async () => { + setSubmitLoading(true); + axios + .put(`/customer-service/request/${id}`, { + status_approval: 'declined', + }) + .then((response) => { + enqueueSnackbar('Berhasil Approve', { variant: 'success' }); + window.location.reload(); + }) + .catch(({ response }) => { + enqueueSnackbar(response?.data?.message || 'Something Went Wrong', { variant: 'error' }); + }) + .finally(() => { + setSubmitLoading(false); + }); + } + + const { id, approval } = useParams(); + + useEffect(() => { + axios + .get('customer-service/request/'+id) + .then((response) => { + setRequestLog(response.data.data) + setIsReversal(response.data.data.is_reversal) + }) + .catch((error) => { + console.error(error); + }) + }, [id]); + + const style1 = { + color: '#919EAB', + width: '30%' + } + const style3 = { + color: '#919EAB', + width: '35%' + } + const style2 = { + width: '70%' + } + const marginBottom1 = { + marginBottom: 1, + } + const marginBottom2 = { + marginBottom: 2, + } + + const [openDialogSubmit, setOpenDialogSubmit] = useState(false); + const [openDialogEditDetail, setDialogDEditDetail] = useState(false); + const [openDialogBenefit, setDialogBenefit] = useState(false); + const [openDialogMedicine, setDialogMedicine] = useState(false); + const [openDialogSendWa, setDialogSendWa] = useState(false); + const [shareLink, setShareLink] = useState(false); + + // Handel Delete Detail Benefit + const [idBenefitData, setIdBenefitData] = useState(); + const [openDialogDeleteBenefit, setDialogDeleteBenefit] = useState(false) + + const [idMedicineData, setIdMedicineData] = useState(); + const [openDialogDeleteMedicine, setDialogDeleteMedicine] = useState(false) + + const [approve, setApprove] = useState('') + + // Handle Edit Detail Benefit + const [openDialogEditBenefit, setDialogEditBenefit] = useState(false) + const [BenefitConfigurationData, setBenefitConfigurationData] = useState(); + + // Buat total data + const totalAmountIncurred = (requestLog?.benefit_data || []).reduce((accumulator, item) => { + return accumulator + (item.amount_incurred || 0); + }, 0); + const totalAmountApprove = (requestLog?.benefit_data || []).reduce((accumulator, item) => { + return accumulator + (item.amount_approved || 0); + }, 0); + const totalAmountNotApprove = (requestLog?.benefit_data || []).reduce((accumulator, item) => { + return accumulator + (item.amount_not_approved || 0); + }, 0); + const totalExcessPaid = (requestLog?.benefit_data || []).reduce((accumulator, item) => { + return accumulator + (item.excess_paid || 0); + }, 0); + + const total = { + totalAmountIncurred : totalAmountIncurred, + totalAmountApproved : totalAmountApprove, + totalAmountNotApproved : totalAmountNotApprove, + totalExcessPaid : totalExcessPaid, + totalLimit : requestLog?.member_usage_benefit, + benefit : requestLog?.benefit, + } + // Handle Delete File LOG + const [pathFile, setPathFile] = useState('') + const [dialogDeleteFIleLog, setDialogDeleteFileLog] = useState(false) + + // Handle Upload File LOG + const [dialogUploadFileLog, setDialogUploadFileLog] = useState(false) + + + const fileDiagnosaInput = useRef(null); + const [fileApprovals, setFileApproval] = useState([]); + const handleDiagnosaInputChange = (event:any) => { + if (event.target.files[0]) { + setFileApproval([...fileApprovals, ...event.target.files]); + } else { + console.log('NO FILE'); + } + }; + const removeApprovalFiles = (filesState:any, index:any) => { + setFileApproval( + filesState.filter((file:any, fileIndex:any) => { + return fileIndex != index; + }) + ); + }; + + + //dari hospital portal + + const [dischargeDate, setDischargeDate] = useState(null) + useEffect(() => { + if (requestLog?.discharge_date) { + setDischargeDate( + parse( + requestLog.discharge_date, + 'yyyy-MM-dd HH:mm:ss', + new Date() + ) + ) + } + }, [requestLog?.discharge_date]) + + const [serviceOptions, setServiceOptions] = useState< + { value: string; label: string }[] + >([]) + + const [specialisOptions, setSpecialisOptions] = useState< + { value: number; label: string }[] + >([]) + + useEffect(() => { + if (!requestLog?.id && !Log_id) return + + axios + .get('service-member/' + (requestLog?.id ?? Log_id)) + .then((res) => setServiceOptions(res.data)) + .catch(console.error) + + axios + .get('specialis') + .then((res) => setSpecialisOptions(res.data)) + .catch(console.error) + }, [requestLog?.id, Log_id]) + + + const [serviceCode, setServiceCode] = useState('') + const [idSpecialities, setIdSpecialities] = useState(null) + const [inputDppj, setInputDppj] = useState('') + useEffect(() => { + if (!requestLog) return + setServiceCode(requestLog.service_code ?? '') + setIdSpecialities(requestLog.specialitiesID ?? null) + setInputDppj(requestLog.dppj ?? '') + }, [requestLog]) + const selectedService = useMemo( + () => + serviceOptions.find( + (o) => String(o.value) === String(serviceCode) + ) || null, + [serviceOptions, serviceCode] + ) + + const selectedSpecialis = useMemo( + () => + specialisOptions.find( + (o) => Number(o.value) === Number(idSpecialities) + ) || null, + [specialisOptions, idSpecialities] + ) + + + function submitRequestFinalLog() { + if(dischargeDate == '') + { + enqueueSnackbar('Tanggal Keluar', { variant: 'warning' }); + return false; + } + //cek spesialis + if(!idSpecialities) + { + enqueueSnackbar('Spesialis', { variant: 'warning' }); + return false; + } + //cek dpjp + if(!inputDppj) + { + enqueueSnackbar('DPPJ', { variant: 'warning' }); + return false; + } + setSubmitLoading(true); + const formData = makeFormData({ + request_logs_id: Log_id, + // result_files: fileHasilPenunjangs, + // diagnosa_files: fileDiagnosas, + // kondisi_files: fileKondisis, + discharge_date: fPostFormat(dischargeDate, 'yyyy-MM-dd HH:mm:ss'), + service_code: serviceCode, + spescialis_id: idSpecialities, + dppj: inputDppj, + }); + axios + .post('/request-final-log', formData) + .then((response) => { + enqueueSnackbar(response.data.meta.message ?? 'Berhasil membuat data', { variant: 'success' }); + // handleSubmitSuccess(); + // onClose({ someData: 'example data' }, getData); + window.location.reload(); + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.meta.message ?? 'Something Went Wrong', { variant: 'error' }); + }) + .then(() => { + setSubmitLoading(false); + }); + } + //end dari hospital portal + + return ( + + + + + navigate(-1)}> + + + + + {requestLog?.code ?? ''} + + + + + + {/* Detail */} + + + + + + Detail + + + {requestLog?.status_final_log != 'requested' ? ( + + + { + setDialogDEditDetail(true) + }}> + + Edit + + + } + /> + ) : null } + + + + Invoice Number + {requestLog?.invoice_no ? requestLog?.invoice_no : '-'} + + + Billing Number + {requestLog?.billing_no ? requestLog?.billing_no : '-'} + + + Provider + {requestLog?.provider} + + + Member ID + {requestLog?.member_id} + + + Type Of Member + {requestLog?.type_of_member} + + + Policy Number + {requestLog?.policy_number} + + + Name + {requestLog?.name} + + + Date Of Birth + {requestLog?.date_of_birth ? fDate(requestLog?.date_of_birth) : '-'} + + + Marital Status + {requestLog?.marital_status} + + + Submission Date + {requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'} + + + Admission Date + {requestLog?.admission_date ? fDateTimesecond(requestLog?.admission_date) : '-'} + + + + Discharge Date + {requestLog?.discharge_date ? fDateTimesecond(requestLog?.discharge_date) : '-'} + + + + No KTP + {requestLog?.no_identitas ? requestLog?.no_identitas : '-'} + + + Keterangan + {requestLog?.keterangan ? requestLog?.keterangan : '-'} + + + Hak Kamar Pasien + {requestLog?.hak_kamar_pasien ? requestLog?.hak_kamar_pasien : '-'} + + + Penempatan Kamar + {requestLog?.penempatan_kamar ? requestLog?.penempatan_kamar : '-'} + + + Spesialis + {requestLog?.specialities_id ? requestLog?.specialities_id : '-'} + + + DPPJ + {requestLog?.dppj ? requestLog?.dppj : '-'} + + + Catatan + {requestLog?.catatan ? requestLog?.catatan : '-'} + + + Diagnosis + + {requestLog?.diagnosis?.length > 0 ? ( +
    + {requestLog.diagnosis.map((diagnosisItem, index) => ( +
  • {diagnosisItem.value} - {diagnosisItem.label}
  • + // Replace 'name' with the property you want to display + ))} +
+ ) : ( +

No diagnosis available.

+ )} +
+
+ {/* */} +
+
+ + {/* Service */} + + + + + + {/* Exclusion */} + + {/* + */} + + + {/* Hospital Care */} + {/* + + + History of Hospital Care + + + + + + */} + + + + {/* Kolom Tanggal Discharge */} + + Tanggal Keluar + + { + setDischargeDate(newValue); + }} + inputFormat="dd-MM-yyyy HH:mm" + renderInput={(params) => } + /> + + + + {/* Kolom Service Type */} + + Tipe Service + o.label} + isOptionEqualToValue={(o, v) => o.value === v.value} + onChange={(_, v) => setServiceCode(v?.value ?? '')} + renderInput={(params) => ( + + )} + /> + + + + + + + {/* Specialist */} + + + Spesialis + o.label} + isOptionEqualToValue={(o, v) => o.value === v.value} + onChange={(_, v) => setIdSpecialities(v?.value ?? null)} + renderInput={(params) => ( + + )} + /> + + + + + + + + + DPPJ + { + setInputDppj(event.target.value); + }} + fullWidth + /> + + + { + submitRequestFinalLog(); + }} + loading={submitLoading} + > + Simpan + + + + + {/* Surat persetujuan Tindakan */} + + + + + Tindakan Persetujuan + + + + {!isReversal && ( + + + + + Upload Tindakan Persetujuan + + + {fileApprovals?.map((file: any, index: number) => ( + + + {file.name} + + removeApprovalFiles(fileApprovals, index)} + /> + + ))} + + fileDiagnosaInput.current?.click()} + > + + + + Upload Tindakan Persetujuan + + + + + + + + {/* + Simpan + */} + + {approval ? ( + <> + + {/* GRUP TOMBOL DI KANAN */} + {/* {requestLog?.status_approval !== 'approved' && ( + + + + + + )} */} + + ) : ( + <> + {/* TOMBOL SIMPAN DI KIRI */} + + Simpan + + + {/* Ini adalah spacer untuk mendorong tombol berikutnya ke kanan */} + + + {/* GRUP TOMBOL DI KANAN */} + + + + {/* */} + + + )} + + + + + + )} + + {/* FILE YANG SUDAH TERUPLOAD */} + {requestLog?.files + ?.filter((document) => document.type === 'approval') + ?.map((documentType, index) => ( + + + + {documentType.original_name || '-'} + + + + {!isReversal && ( + { + setDialogDeleteFileLog(true); + setPathFile(documentType.path); + }} + size="small" + > + + + )} + + ))} + + {/* DIALOG */} + + + + + + + + {/* Benefit */} + + + + Benefit + { + !isReversal ? ( + + ) : null + } + + + {requestLog?.benefit_data?.map((item, index) => ( + + + + + + + {item.benefit?.description} + + + { + !isReversal ? ( + + + { + setDialogEditBenefit(true) + setIdBenefitData(item.id) + setBenefitConfigurationData(item) + }} + > + + Edit + + { + setIdBenefitData(item.id) + setDialogDeleteBenefit(true) + }} + > + + Delete + + + } /> + + ) : null + } + + + + + + + {/* Amount Incurred */} + + + + + Amount Incurred + + + + + {fNumber(item.amount_incurred)} + + + + + + {/* Amount Approved */} + + + + + Amount Approved + + + + + {fNumber(item.amount_approved)} + + + + + + {/* Amount Not Approved */} + + + + + Amount Not Approved + + + + + {fNumber(item.amount_not_approved)} + + + + + + {/* Excess Paid* */} + + + + + Excess Paid* + + + + + {fNumber(item.excess_paid)} + + + + + + {/* Keterangan* */} + + + + + Keterangan* + + + + + {item.keterangan} + + + + + + + + + + + + ))} +
+
+ {requestLog?.benefit_data && requestLog.benefit_data.length > 0 ? ( + + + + + + + Total Benefit + + + + + + + + + + {/* Amount Incurred */} + + + + + Amount Incurred + + + + + {fNumber(totalAmountIncurred)} + + + + + + {/* Amount Approved */} + + + + + Amount Approved + + + + + {fNumber(totalAmountApprove)} + + + + + + {/* Amount Not Approved */} + + + + + Amount Not Approved + + + + + {fNumber(totalAmountNotApprove)} + + + + + + {/* Excess Paid* */} + + + + + Excess Paid + + + + + {fNumber(totalExcessPaid)} + + + + + + + + + + + ) + : ( + null + )} +
+ + {/* PR Buat pindahin ke componen */} + {/* + + */} + + {/* Dialog Edit */} + + + {/* Dialog Delete */} + + + {/* Dialog Edit Detai; */} + + + + + +
+ + {/* Medicine */} + {/* + + + Medicine + + + + {requestLog?.medicine.map((item, index) => ( + + {item.medicine} + Rp. {fNumber(item.price)} + { + setIdMedicineData(item.id) + setDialogDeleteMedicine(true) + }}> + + + + + ))} + + + + + */} + + {/* File */} + + + + + Files + + { !isReversal ? ( + + + + ) : null } + + + {requestLog?.files + ?.filter((document) => document.type !== 'approval') + ?.map((documentType, index) => ( + + + + {documentType.original_name ? documentType.original_name : '-'} + + + { !isReversal ? ( + + { + setDialogDeleteFileLog(true) + setPathFile(documentType.path) + }} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}> + + + + ) : null } + + + ))} + + + + + + + + {requestLog?.status == 'approved' ? ( + + + <> +
+ {/* */} +
+
+ + +
+ + +
+
+ ) : null} + +
+
+
+ ); +} diff --git a/frontend/hospital-portal/src/sections/dashboard/Model/Functions.tsx b/frontend/hospital-portal/src/sections/dashboard/Model/Functions.tsx new file mode 100644 index 00000000..753fca55 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Model/Functions.tsx @@ -0,0 +1,190 @@ +import axios from '@/utils/axios'; +import { enqueueSnackbar } from 'notistack'; +import { MedicineType, MemberListType } from './Types'; +import { BenefitConfigurationListType } from './Types'; +import { makeFormData } from '@/utils/jsonToFormData'; + +/** + * Listing Member + */ +export const getMemberList = async ( page: number, keyword: string ): Promise => { + const response = await axios.get(`/claim-requests/list-member?page=${page}&keyword=${keyword}`) + .then((res) =>{ + return res.data.data.member_list; + }) + .catch((res) => { + enqueueSnackbar("server error !", { + variant: 'error', + }); + + return []; + }); + + return response; +}; + +/** + * Add Claim Request + */ +export const addClaimRequest = async ( data: MemberListType[] ): Promise => { + // Mapping + const formData = new FormData(); + + data.map((row, index) => { + formData.append(`member_id[${index}]`, row.id.toString()); + formData.append(`service_code[${index}]`, row.patien_type??''); + + if (row.file_kondisi != undefined) { + row.file_kondisi.forEach((file, file_index) => { + console.log(file); + + formData.append(`file_kondisi[member_${row.id}][${file_index}]`, file); + }); + } + + if (row.file_diagnosa != undefined) { + row.file_diagnosa.forEach((file, file_index) => { + console.log(file); + + formData.append(`file_diagnosa[member_${row.id}][${file_index}]`, file); + }); + } + + if (row.file_penunjang != undefined) { + row.file_penunjang.forEach((file, file_index) => { + console.log(file); + + formData.append(`file_penunjang[member_${row.id}][${file_index}]`, file); + }); + } + }) + + // Axios + const response = await axios.post(`/claim-requests`, formData) + .then((res) =>{ + enqueueSnackbar("Berhasil membuat data !", { + variant: 'success', + }); + + return true; + }) + .catch((res) => { + enqueueSnackbar("server error !", { + variant: 'error', + }); + + return false; + }); + + return response; +}; + +/** + * Add Benefit + */ + +export const postAddBenefit = async (data: BenefitConfigurationListType):Promise => { + const response = await axios.post(`customer-service/request/insert-benefit`, { + ...data + }) + .then((res) =>{ + enqueueSnackbar(res.data.message, { + variant: 'success', + }); + + return true; + }) + .catch((res) => { + if (res.response.status == 400) { + let arr_message = res.response.data.message; + + // for (const key in arr_message) { + enqueueSnackbar(arr_message, { + variant: 'warning', + }); + // } + } + else { + enqueueSnackbar("server error !", { + variant: 'error', + }); + } + + return false; + }); + + return response; +} + +/** + * Edit Benefit + */ +export const postEditBenefit = async (id:number|undefined, data: BenefitConfigurationListType):Promise => { + const response = await axios.put(`customer-service/request/benefit_data/${id}`, { + ...data + }) + .then((res) =>{ + enqueueSnackbar(res.data.message, { + variant: 'success', + }); + + return true; + }) + .catch((res) => { + if (res.response.status == 400) { + let arr_message = res.response.data.message; + + // for (const key in arr_message) { + enqueueSnackbar(arr_message, { + variant: 'warning', + }); + // } + } + else { + enqueueSnackbar("server error !", { + variant: 'error', + }); + } + + return false; + }); + + return response; +} + +/** + * Add Medicine + */ + +export const postAddMedince = async (data: MedicineType):Promise => { + const response = await axios.post(`customer-service/request/medicine-data`, { + ...data + }) + .then((res) =>{ + enqueueSnackbar(res.data.message, { + variant: 'success', + }); + + return true; + }) + .catch((res) => { + if (res.response.status == 400) { + let arr_message = res.response.data.message; + + // for (const key in arr_message) { + enqueueSnackbar(arr_message, { + variant: 'warning', + }); + // } + } + else { + enqueueSnackbar("server error !", { + variant: 'error', + }); + } + + return false; + }); + + return response; +} diff --git a/frontend/hospital-portal/src/sections/dashboard/Model/Types.ts b/frontend/hospital-portal/src/sections/dashboard/Model/Types.ts new file mode 100644 index 00000000..cb9081c1 --- /dev/null +++ b/frontend/hospital-portal/src/sections/dashboard/Model/Types.ts @@ -0,0 +1,175 @@ +import { Member } from "@/@types/member" + +/** + * Search Type + */ +export type SearchType = { + keyword: string, +} + +/** + * Member List + */ +export type FinalLogType = { + id : number, + code : string, + member : Member, + member_name : string, + submission_date_fgl : string, + submission_date : string, + admission_date : string, + service_name : string, + payment_type_name : string, + status_final_log : string, + status_approval : string, + nominal : number, + provider : string, + status : string, + files_by_type : files_by_type, + specialities_id : number, + dppj: string +} + + +export type DetailFinalLogType = { + id : number, + code : string, + member_id : string, + invoice_no : string, + billing_no : string, + provider : string, + policy_number : string, + name : string|any, + date_of_birth : string, + gender : string, + marital_status : string, + admission_date : string, + submission_date : string, + approved_final_log_at : string, + service_type : string, + claim_method : string, + status : string, + status_final_log : string, + status_approval : string, + no_identitas : string, + keterangan : string, + hak_kamar_pasien : string, + penempatan_kamar : string, + catatan : string, + discharge_date : string, + reason : string, + type_of_member : string, + diagnosis : Diagnosis[], + benefit : Benefit[], + benefit_data : BenefitData[], + config_service : ConfigService, + exclusion : Exclusion[], + medicine : Medicine[], + files : file[], + member_usage_benefit : number, + corporate_id : number + nominal : number +} + +export type Diagnosis = { + id : number, + value : string, + label : string +} + +export type BenefitData = { + amount_incurred : number, + amount_approved : number, + amount_not_approved : number, + excess_paid : number, + keterangan : string, + benefit : Benefit, + request_log_id : number, + benefit_name : string, + description : string, + id : number, +} + +export type BenefitConfigurationListType = { + request_log_id: number|undefined, + benefit_name: string, + benefit_id: number, + benefit: { + description: string + }, + amount_incurred: number, + amount_approved: number, + amount_not_approved: number, + excess_paid: number, + keterangan: string, + description: string, + reason: { + value:string, + label:string + } | null +} + +export type Benefit = { + id: number, + code: string, + description: string +} + +export type files_by_type = { + final_log_diagnosis : file[], + final_log_kondisi : file[], + final_log_result : file[], +} + +export type file = { + original_name: string, + name: string, + path: string, + url: string, +} + +export type ConfigService = { + gp_external_doctor_online: string, + gp_external_doctor_offline: string, + gp_internal_doctor_online: string, + gp_internal_doctor_offline: string, + sp_external_doctor_online: string, + sp_external_doctor_offline: string, + sp_internal_doctor_online: string, + sp_internal_doctor_offline: string, + vitamins: string, + delivery_fee: string, + general_practitioner_fee: string, + specialist_practitioner_fee: string +} + +export type Exclusion = { + exclusionable: { + code: string, + name: string + }, + rules: Rule[] +} + +export type Rule = { + id: number, + exclusion_id: number, + name: string, + values: string, + +} + +export type MedicineType = { + medicine: Medicine[], +} + +export type Medicine = { + id: number, + medicine_name: string, + medicine_price: number, + medicine: string, + price: number, + request_log_id: number|undefined, +} + + diff --git a/frontend/hospital-portal/src/sections/dashboard/TableListReqLog.tsx b/frontend/hospital-portal/src/sections/dashboard/TableListReqLog.tsx index 4e6a79ac..4e11d427 100644 --- a/frontend/hospital-portal/src/sections/dashboard/TableListReqLog.tsx +++ b/frontend/hospital-portal/src/sections/dashboard/TableListReqLog.tsx @@ -325,12 +325,12 @@ export default function TableList() { isSort: false, }, ]; - + function handleSearchMember(noPolis:any, birthDate:any) { setLoadingClaim(false) - axios.post('/search-member', { - no_polis: noPolis, + axios.post('/search-member', { + no_polis: noPolis, birth_date: birthDate ? fPostFormat(birthDate, 'yyyy-MM-dd') : null, type: 'view' }) @@ -346,13 +346,13 @@ export default function TableList() { }); } - + function handleRequestFinalLog( - id:any, - full_name:any, - no_polis:any, - submission_date:any, + id:any, + full_name:any, + no_polis:any, + submission_date:any, service_code:any, member_id:any, specialities_id:any, @@ -438,20 +438,42 @@ export default function TableList() { Download LOG - ):''} + ):''} {obj.final_log === 0 && obj.status === 'approved' ? ( - handleRequestFinalLog( - obj.id, - obj.full_name, - obj.no_polis, - obj.submission_date, - obj.service_code, - obj.member_id, - obj.specialities_id, - obj.dppj, - ) }> - - Request Final LOG + + //OLD + // handleRequestFinalLog( + // obj.id, + // obj.full_name, + // obj.no_polis, + // obj.submission_date, + // obj.service_code, + // obj.member_id, + // obj.specialities_id, + // obj.dppj, + // ) }> + // + // Request Final LOG + // + //NEW + + navigate('/detail-request-final-log/'+obj.id, { + state: { + Log_id: obj.id, + full_name: obj.full_name, + no_polis: obj.no_polis, + submission_date: obj.submission_date, + service_code: obj.service_code, + member_id: obj.member_id, + specialities_id: obj.specialities_id, + dppj: obj.dppj, + }, + }) + } + > + + Request Final LOG ):''} @@ -484,7 +506,7 @@ export default function TableList() { - + // const [noPolis, setNoPolis] = useState('AW001-01'); //const [birthDate, setBirthDate] = useState('1991-01-10'); @@ -527,7 +549,7 @@ export default function TableList() { DialogMember(currentMember, () => setOpenDialogBenefit(false)) } maxWidth="sm" - /> + /> } maxWidth="sm" - /> + /> ); } \ No newline at end of file diff --git a/frontend/hospital-portal/src/utils/formatTime.ts b/frontend/hospital-portal/src/utils/formatTime.ts index fea72e17..f98c8782 100644 --- a/frontend/hospital-portal/src/utils/formatTime.ts +++ b/frontend/hospital-portal/src/utils/formatTime.ts @@ -68,6 +68,20 @@ export function fPostFormat(date: Date | string | number, dateFormat = 'yyyy-MM- return format(new Date(date), dateFormat); } +export function fDateTimesecond(date: Date | string | number) { + return format(new Date(date), 'dd MMM yyyy HH:mm:ss'); + } + + export function toTitleCase(str: string | null) { + return str.replace(/\w\S*/g, function(txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + } + + export function fDateOnly(date: Date | string | number) { + return format(new Date(date), 'yyyy-MM-dd'); + } + // export function fDateString(date) { // const dateObj = parseISO(date); // const formattedDate = format(dateObj, 'dd MMMM yyyy');