diff --git a/Modules/HospitalPortal/Http/Controllers/Api/RequestLogController.php b/Modules/HospitalPortal/Http/Controllers/Api/RequestLogController.php index b7a07ebe..44c9e01e 100644 --- a/Modules/HospitalPortal/Http/Controllers/Api/RequestLogController.php +++ b/Modules/HospitalPortal/Http/Controllers/Api/RequestLogController.php @@ -13,6 +13,11 @@ use App\Models\File; use Dompdf\Dompdf; use Dompdf\Options; use Illuminate\Support\Facades\View; +use App\Models\Member; +use App\Models\RequestLog; +use App\Models\Organization; +use App\Services\ClaimRequestService; +use App\Models\ClaimRequest; class RequestLogController extends Controller { @@ -20,6 +25,7 @@ class RequestLogController extends Controller * Display a listing of the resource. * @return Renderable */ + private static $code_prefix = 'CLAIM'; public function requestLog(Request $request) { $data = [ @@ -820,4 +826,110 @@ class RequestLogController extends Controller return response($pdf->output(), 200, $headers); } + + public function submitClaims(Request $request) + { + $data = [ + 'selectedRows' => $request->selectedRows, + ]; + $validator = Validator::make($request->all(), [ + 'selectedRows' => 'required' + ], [ + 'selectedRows.required' => trans('Validation.required',['attribute' => 'Request Logs ID']), + ]); + if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); + } + else + { + foreach ($request->selectedRows as $request_logs_id) { + $data_req_logs = DB::table('request_logs') + ->where('id', '=', $request_logs_id) + ->select('id', 'member_id', 'service_code') + ->first(); + + $check_claim_requests = DB::table('claim_requests') + ->where('claim_requests.request_log_id', '=', $request_logs_id) + ->first(); + if(!$check_claim_requests) { + try { + DB::beginTransaction(); + $code = $this->getNextCode($request_logs_id); + $member = Member::find($data_req_logs->member_id); + $requestLogData = RequestLog::where('id',$request_logs_id)->first(); + $organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code'); + $provideCode = $organization ? $organization->code : ''; + + $newClaimRequest = ClaimRequestService::storeClaimRequest( + row: [], + code: $code, + member: $member, + paymentType: 'cashless', + serviceCode: $data_req_logs->service_code, + requestLogID: $request_logs_id, + organization_code: $provideCode, + ); + // Log History + $newClaimRequest->histories()->create([ + 'title' => 'New Claim Requested', + 'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})", + 'type' => 'info', + 'system_origin' => 'hospital-portal' + ]); + + // Claim Log + DB::table('claim_logs') + ->insert([ + 'claim_request_id' => $newClaimRequest->id, + 'status' => 'requested', + 'date' => date('Y-m-d H:i:s'), + 'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})", + 'system_origin' => 'hospital-portal', + 'created_by' => auth()->user()->id, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at'=> date('Y-m-d H:i:s'), + ]); + DB::commit(); + } catch (\Exception $e) { + DB::rollback(); + return ApiResponse::apiResponse("Error", $data, $e->getMessage(), 500); + } + } + } + return ApiResponse::apiResponse('Success', $data, trans('Message.success'), 200); + } + } + + public static function getNextCode($request_log_id = 0) + { + $last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, ".", -1) AS SIGNED)) as max_numeric_code')) + ->whereRaw('SUBSTRING_INDEX(code, ".", -1) REGEXP "^[0-9]+$"') + ->value('max_numeric_code'); + // $next_number = 1; + if ($last_numeric_code) { + // // Jika ada kode sebelumnya, pecah kode dan tambahkan 1 ke angka terakhir + // $parts = explode('-', $last_code); + // $last_number = (int) end($parts); + $next_number = $last_numeric_code + 1; + } else { + $next_number = 1; + } + return self::makeCode($next_number, $request_log_id); + } + + public static function makeCode($next_number, $request_log_id) + { + // Pastikan $next_number adalah integer positif + $next_number = max(1, (int) $next_number); + $requestLogData = RequestLog::where('id', $request_log_id)->first(); + $organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code'); + $provideCode = $organization ? $organization->code : ''; + $member = Member::with('currentCorporate')->where(['id' => $requestLogData->member_id])->first(); + $sparator = '.'; + $date = date('ymd'); + // Menghasilkan kode dengan format yang diinginkan + return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 5, '0', STR_PAD_LEFT); + return self::$code_prefix . '.' . str_pad($next_number, 6, '0', STR_PAD_LEFT); + } } diff --git a/Modules/HospitalPortal/Routes/api.php b/Modules/HospitalPortal/Routes/api.php index 17fdec66..3b73f731 100644 --- a/Modules/HospitalPortal/Routes/api.php +++ b/Modules/HospitalPortal/Routes/api.php @@ -56,6 +56,7 @@ Route::prefix('v1')->group(function() { Route::post('request-final-log', 'requestFinalLog'); Route::get('download-log/{request_log_id}', 'downlodLog'); Route::get('download-final-log/{request_log_id}', 'downlodFinalLog'); + Route::post('submit-claims', 'submitClaims'); }); //Notification Route::controller(NotificationController::class)->group(function() { diff --git a/frontend/hospital-portal/public/lang/en-US.json b/frontend/hospital-portal/public/lang/en-US.json index 8791f8a0..64b5efee 100644 --- a/frontend/hospital-portal/public/lang/en-US.json +++ b/frontend/hospital-portal/public/lang/en-US.json @@ -50,5 +50,12 @@ "txtNew" : "New", "txtBeforeThat" : "Before that", "txtDischargeDate" : "Discharge Date", - "txtPatner" : "Patner" + "txtPatner" : "Patner", + "txtSelected": "Selected", + "txtConfirmation": "Confirmation", + "txtReason": "Reason Decline", + "txtCancel": "Cancel", + "txtDecline": "Decline", + "txtApprove": "Approve", + "txtDialogConfirmation": "Are you sure you want to proceed with this action?" } diff --git a/frontend/hospital-portal/public/lang/id-ID.json b/frontend/hospital-portal/public/lang/id-ID.json index 6f85bbb5..96077fe4 100644 --- a/frontend/hospital-portal/public/lang/id-ID.json +++ b/frontend/hospital-portal/public/lang/id-ID.json @@ -50,5 +50,12 @@ "txtNew" : "Baru", "txtBeforeThat" : "Sebelum", "txtDischargeDate" : "Tanggal Keluar", - "txtPatner" : "Rekanan" + "txtPatner" : "Rekanan", + "txtSelected": "Terpilih", + "txtConfirmation": "Konfirmasi", + "txtReason": "Alasan Penolakan", + "txtCancel": "Batal", + "txtDecline": "Tolak", + "txtApprove": "Terima", + "txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?" } diff --git a/frontend/hospital-portal/src/components/Table.tsx b/frontend/hospital-portal/src/components/Table.tsx index a60bc862..6de87395 100644 --- a/frontend/hospital-portal/src/components/Table.tsx +++ b/frontend/hospital-portal/src/components/Table.tsx @@ -13,6 +13,7 @@ import { TableSortLabel, Box, Card, + Checkbox, Grid, FormControl, InputLabel, @@ -43,6 +44,8 @@ import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from ' import { InputAdornment } from '@mui/material'; import GetAppIcon from '@mui/icons-material/GetApp'; import { LanguageContext } from '@/contexts/LanguageContext'; +import CancelIcon from '@mui/icons-material/Cancel'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; /* --------------------------------- styled --------------------------------- */ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ @@ -71,6 +74,7 @@ export default function Table({ filterEndDate, searchs, exportReport, + selected, }: TableListProps) { /* ------------------------------- handle sort ------------------------------ */ const handleRequestSort = async (event: React.MouseEvent, property: string) => { @@ -98,34 +102,73 @@ export default function Table({ return ( - {headCells && - headCells.map((headCell, index) => ( - - {headCell.isSort ? ( - - {headCell.label} - {orders?.orderBy === headCell.id ? ( - - {orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'} - - ) : null} - - ) : ( - headCell.label - )} + {selected.useSelected && selected.selectedRows.length > 0 ? ( + <> + + + + + + + {selected.selectedRows.length > 0 ? selected.selectedRows.length : '0'}   {localeData.txtSelected} + + + + + + {selected.useDecline ? ( + + ):''} + + + + - ))} + + ):( + <> + {selected.useSelected ? ( + + + + ):''} + {headCells && + headCells.map((headCell, index) => ( + + {headCell.isSort ? ( + + {headCell.label} + {orders?.orderBy === headCell.id ? ( + + {orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + ) : ( + headCell.label + )} + + ))} + + + )} + + ); @@ -162,7 +205,6 @@ export default function Table({ params.setAppliedParams(parameters); }; /* -------------------------------------------------------------------------- */ - return ( // @@ -349,6 +391,20 @@ export default function Table({ ) : rows && rows.length >= 1 ? ( rows.map((row, rowIndex) => ( + {!selected.useSelected ? ( + '' + ): (selected.useSelected && row.check_status === 'approved' && !row.check_claim ? ( + + selected.handleCheckboxChange(row.id)} + /> + + ):( + + + + ))} {headCells && //@ts-ignore headCells.map((head, headIndex) => ( diff --git a/frontend/hospital-portal/src/sections/claim/TableList.tsx b/frontend/hospital-portal/src/sections/claim/TableList.tsx index 61badfaf..dcd4994e 100644 --- a/frontend/hospital-portal/src/sections/claim/TableList.tsx +++ b/frontend/hospital-portal/src/sections/claim/TableList.tsx @@ -118,6 +118,58 @@ export default function TableList() { /* -------------------------------------------------------------------------- */ + // ----------------------------------------- handle selected --------------------- + const [selectAll, setSelectAll] = useState(false); + const [selectedRows, setSelectedRows] = useState([]); + const [dataTableData, setDataTableData] = useState(); + const handleSelectAll = () => { + setSelectAll(!selectAll); + if (!selectAll) { + const requestedIds = dataTableData?.data + .filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim) + .map((row: { id: any }) => row.id); + setSelectedRows(requestedIds); + } else { + setSelectedRows([]); + } + }; + + const handleCheckboxChange = (id: any) => { + setSelectedRows(prevSelectedRows => { + const isSelected = prevSelectedRows.includes(id); + if (isSelected) { + return prevSelectedRows.filter(rowId => rowId !== id); + } else { + return [...prevSelectedRows, id]; + } + }); + }; + + const [openDialogSubmit, setOpenDialogSubmit] = useState(false); + const handleCloseDialogSubmit = () => { + setOpenDialogSubmit(false); + } + const [valDialog, setValDialog] = useState(''); + const [reasonDecline, setReasonDecline] = useState(''); + const handleReasonDeclineChange = (event: { target: { value: SetStateAction; }; }) => { + setReasonDecline(event.target.value); +}; + + const selected = { + useSelected: false, + selectAll: selectAll, + handleSelectAll: handleSelectAll, + selectedRows: selectedRows, + handleCheckboxChange : handleCheckboxChange, + totRows: 9, + useDecline: false, + txtDecline: 'Decline', + useApprove:true, + txtApprove: 'Submit Claim', + setOpenDialogSubmit: setOpenDialogSubmit, + setValDialog: setValDialog + }; + /* ------------------------------ handle search ----------------------------- */ const [searchText, setSearchText] = useState(''); @@ -280,6 +332,7 @@ export default function TableList() { const response = await axios.get(`/get-claim-requests`, { params: { ...parameters, type: 'final-log' }, }); + setDataTableData(response.data); setData( response.data.data.map((obj: any) => ({ ...obj, @@ -357,6 +410,7 @@ export default function TableList() { params={params} searchs={searchs} filterStatus={filterStatus} + selected={selected} // filterStartDate={filterStartDate} // filterEndDate={filterEndDate} /> diff --git a/frontend/hospital-portal/src/sections/dashboard/TableListFinalLog.tsx b/frontend/hospital-portal/src/sections/dashboard/TableListFinalLog.tsx index 010662dc..027d8ff0 100644 --- a/frontend/hospital-portal/src/sections/dashboard/TableListFinalLog.tsx +++ b/frontend/hospital-portal/src/sections/dashboard/TableListFinalLog.tsx @@ -1,11 +1,11 @@ /* ---------------------------------- @mui ---------------------------------- */ -import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from '@mui/material'; +import { Stack, Button, Checkbox, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box, IconButton, TextField } from '@mui/material'; /* ---------------------------------- axios --------------------------------- */ // import axios from 'axios'; import axios from '../../utils/axios'; import { styled } from '@mui/material/styles'; /* ---------------------------------- react --------------------------------- */ -import { useContext, useEffect, useState } from 'react'; +import { SetStateAction, useContext, useEffect, useState } from 'react'; /* -------------------------------- component ------------------------------- */ import Iconify from '../../components/Iconify'; @@ -30,6 +30,8 @@ import MuiDialog from '@/components/MuiDialog'; import DialogMember from './DialogMember'; import DialogClaimSubmit from './DialogClaimSubmit'; import { fPostFormat } from '@/utils/formatTime'; +import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; export default function TableListFinalLog() { const navigate = useNavigate(); @@ -68,6 +70,7 @@ export default function TableListFinalLog() { return `${day}${month}${year}`; } + /* -------------------------------------------------------------------------- */ /* setting up for the table */ /* -------------------------------------------------------------------------- */ @@ -128,6 +131,85 @@ export default function TableListFinalLog() { /* -------------------------------------------------------------------------- */ + // ----------------------------------------- handle selected --------------------- + const [selectAll, setSelectAll] = useState(false); + const [selectedRows, setSelectedRows] = useState([]); + const [dataTableData, setDataTableData] = useState(); + const handleSelectAll = () => { + setSelectAll(!selectAll); + if (!selectAll) { + const requestedIds = dataTableData?.data + .filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim) + .map((row: { id: any }) => row.id); + setSelectedRows(requestedIds); + } else { + setSelectedRows([]); + } + }; + + const handleCheckboxChange = (id: any) => { + setSelectedRows(prevSelectedRows => { + const isSelected = prevSelectedRows.includes(id); + if (isSelected) { + return prevSelectedRows.filter(rowId => rowId !== id); + } else { + return [...prevSelectedRows, id]; + } + }); + }; + + const [openDialogSubmit, setOpenDialogSubmit] = useState(false); + const handleCloseDialogSubmit = () => { + setOpenDialogSubmit(false); + } + const [valDialog, setValDialog] = useState(''); + const [reasonDecline, setReasonDecline] = useState(''); + const handleReasonDeclineChange = (event: { target: { value: SetStateAction; }; }) => { + setReasonDecline(event.target.value); +}; + + const selected = { + useSelected: true, + selectAll: selectAll, + handleSelectAll: handleSelectAll, + selectedRows: selectedRows, + handleCheckboxChange : handleCheckboxChange, + totRows: 9, + useDecline: false, + txtDecline: 'Decline', + useApprove:true, + txtApprove: 'Submit Claim', + setOpenDialogSubmit: setOpenDialogSubmit, + setValDialog: setValDialog + }; + const handleSubmitData = () => { + //approve or decline + // if (!reasonDecline && valDialog == 'decline') { + // enqueueSnackbar('Mohon isi alasan', { variant: 'warning' }); + // return false; + // } + axios.post('/submit-claims', { + selectedRows: selectedRows + }) + .then((response) => { + if(response.data.meta.code === 200) + { + setOpenDialogSubmit(false); + enqueueSnackbar(response.data.meta.message, {variant : "success"}); + } + else{ + setOpenDialogSubmit(false); + enqueueSnackbar(response.data.meta.message, {variant : "error"}); + } + getData(); + setSelectedRows([]); + }) + .catch(({response}) => { + enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"}) + }) + .then(() => { + }); + }; /* ------------------------------ handle search ----------------------------- */ const [searchText, setSearchText] = useState(''); @@ -335,11 +417,14 @@ export default function TableListFinalLog() { params: { ...parameters, search:searchText, order: order, orderBy: orderBy, status:statusValue, type: 'final-log' }, }); + setDataTableData(response.data); setData( - response.data.data.map((obj: any) => ({ - ...obj, + response.data.data.map((obj: any) => ({ + ...obj, provider:formatTitleCase(obj.provider), full_name:formatTitleCase(obj.full_name), + check_status: obj.status, + check_claim: obj.check_claim, status: obj.status === 'requested' ? (