From 6a4aaff62897e5df6eaa64038cb9850ad63cfe46 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 4 Sep 2025 08:37:21 +0700 Subject: [PATCH 1/9] frontend approval --- .../Transformers/RequestLogShowResource.php | 2 +- .../Components/DialogEditFinalLOG.tsx | 2 +- .../pages/CustomerService/FinalLog/Detail.tsx | 183 +++++++++++++++++- 3 files changed, 184 insertions(+), 3 deletions(-) diff --git a/Modules/Internal/Transformers/RequestLogShowResource.php b/Modules/Internal/Transformers/RequestLogShowResource.php index 9f2a1143..647de78d 100755 --- a/Modules/Internal/Transformers/RequestLogShowResource.php +++ b/Modules/Internal/Transformers/RequestLogShowResource.php @@ -33,7 +33,7 @@ class RequestLogShowResource extends JsonResource $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(); diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx index c81f524e..6d371ee0 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx @@ -159,7 +159,7 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo // Add more options as needed ]; - console.log(formData.type_of_member) +// console.log(formData.type_of_member) const getContent = () => ( diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx index 17b46e1e..6c5eeb16 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx @@ -1,3 +1,6 @@ +import * as Yup from 'yup'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { NumericFormat } from "react-number-format"; import { Container, Grid, @@ -12,11 +15,17 @@ import { 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'; // 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'; @@ -33,6 +42,7 @@ import { Accordion } from '@mui/material'; import { Delete, EditOutlined, ExpandMore } from '@mui/icons-material'; import {BenefitData } from '../FinalLog/Model/Types' import AddIcon from '@mui/icons-material/Add'; +import { LoadingButton } from '@mui/lab'; // Import Card Detail Final LOG import CardDetail from '../Components/CardDetail'; @@ -70,6 +80,19 @@ export default function Detail() { const [requestLog, setRequestLog] = useState(); const [isReversal, setIsReversal] = 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) => { + alert('Nominal: ' + data.nominal); + } const { id } = useParams(); @@ -146,9 +169,27 @@ 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 [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; + }) + ); + }; return ( @@ -323,6 +364,146 @@ export default function Detail() { */} + {/* Surat persetujuan Tindakan */} + + + + + Tindakan Persetujuan + + + + {!isReversal && ( + + + + + Upload Tindakan Persetujuan + + + {fileDiagnosas?.map((file: any, index: number) => ( + + + {file.name} + + removeDiagnosaFiles(fileDiagnosas, index)} + /> + + ))} + + fileDiagnosaInput.current?.click()} + > + + + + Upload Tindakan Persetujuan + + + + + + + + + Simpan + + + + + )} + + {/* FILE YANG SUDAH TERUPLOAD */} + {requestLog?.files?.map((documentType, index) => ( + + + + {documentType.original_name || '-'} + + + + {!isReversal && ( + { + setDialogDeleteFileLog(true); + setPathFile(documentType.path); + }} + size="small" + > + + + )} + + ))} + + {/* DIALOG */} + + + + + + + {/* Benefit */} From 3c9066edc611910c57bfea21617eab45dad28f2e Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 11 Sep 2025 09:47:42 +0700 Subject: [PATCH 2/9] backend dan penyesian upload file dinamis approval notifikasi --- .../Controllers/Api/RequestLogController.php | 28 +++ Modules/Internal/Routes/api.php | 1 + .../Transformers/RequestLogShowResource.php | 1 + app/Models/File.php | 54 ++++- app/Models/RequestLog.php | 3 +- ...d_columns_nominal_to_table_request_log.php | 32 +++ .../FinalLog/Components/DialogSendWa.tsx | 185 ++++++++++++++++++ .../pages/CustomerService/FinalLog/Detail.tsx | 94 +++++++-- .../CustomerService/FinalLog/Model/Types.tsx | 1 + 9 files changed, 375 insertions(+), 24 deletions(-) create mode 100644 database/migrations/2025_09_10_103219_add_columns_nominal_to_table_request_log.php create mode 100755 frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogSendWa.tsx diff --git a/Modules/Internal/Http/Controllers/Api/RequestLogController.php b/Modules/Internal/Http/Controllers/Api/RequestLogController.php index ba9ab91f..4058a280 100755 --- a/Modules/Internal/Http/Controllers/Api/RequestLogController.php +++ b/Modules/Internal/Http/Controllers/Api/RequestLogController.php @@ -1224,6 +1224,34 @@ class RequestLogController extends Controller ]); } } + return Helper::responseJson(data: $request->toArray(), message: 'File Success Uploaded'); + } + + public function approvalFiles(Request $request, $id) + { + Helper::setCustomPHPIniSettings(); + $requestLog = RequestLog::findOrFail($id); + $nominal = $request->nominal; + if($nominal){ + $requestLog->nominal = $nominal; + $requestLog->save(); + } + if ($request->hasFile('approval_files')) { + foreach ($request->approval_files as $file) { + $fileData = File::storeFile('approval', $id, $file); + $requestLog->files()->updateOrCreate([ + 'type' => 'approval', + 'name' => $fileData['name'], + 'original_name' => $file->getClientOriginalName(), + 'extension' => $file->getClientOriginalExtension(), + 'source' => env('FILESYSTEM_DISK'), + 'path' => $fileData['path'], + 'created_by' => auth()->user()->id, + 'updated_by' => auth()->user()->id, + 'reason' => $request->reason, + ]); + } + } return Helper::responseJson(data: $request->toArray(), message: 'File Success Uploaded'); } diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 0445b0d2..6709a385 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -323,6 +323,7 @@ Route::prefix('internal')->group(function () { Route::post('customer-service/request/exportFiledInvoice', [RequestLogController::class, 'exportFiledInvoice']); Route::get('customer-service/request/data', [RequestLogController::class, 'generateDataRequestLogExcel']); Route::post('customer-service/request/{id}/add_file', [RequestLogController::class, 'requestFiles']); + Route::post('customer-service/request/{id}/approval_files', [RequestLogController::class, 'approvalFiles']); Route::post('customer-service/request/{id}/delete_file', [RequestLogController::class, 'deleteFiles']); Route::post('customer-service/request/final-log', [RequestLogController::class, 'updateFinalLog']); diff --git a/Modules/Internal/Transformers/RequestLogShowResource.php b/Modules/Internal/Transformers/RequestLogShowResource.php index 647de78d..9349c037 100755 --- a/Modules/Internal/Transformers/RequestLogShowResource.php +++ b/Modules/Internal/Transformers/RequestLogShowResource.php @@ -174,6 +174,7 @@ class RequestLogShowResource extends JsonResource 'keterangan' => $requestLog['keterangan'], 'hak_kamar_pasien' => $requestLog['hak_kamar_pasien'], 'penempatan_kamar' => $requestLog['penempatan_kamar'], + 'nominal' => $requestLog['nominal'], 'catatan' => $requestLog['catatan'], 'reason' => $requestLog['reason'], 'diagnosis' => $icd, diff --git a/app/Models/File.php b/app/Models/File.php index a6adacf9..793dd4d3 100755 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -111,28 +111,62 @@ class File extends Model // return $path; // } + // public static function storeFile($type, $id, $file) + // { + // // Pastikan directory tidak punya trailing slash + // $directory = rtrim(self::getDirectory($type), '/'); + + // // Buat nama file yang unik dan aman + // $originalName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME); + // $extension = $file->getClientOriginalExtension(); + // $safeName = Str::slug($originalName); + // $uniqueName = $safeName . '-' . uniqid() . '.' . $extension; + + // // Upload file ke disk 's3' dengan visibility 'public' + // $path = Storage::disk('s3')->putFileAs( + // $directory, + // $file, + // $uniqueName, + // 'public' + // ); + + // // Kembalikan path dan nama unik agar bisa digunakan di controller + // return [ + // 'path' => $directory . '/' . $uniqueName, // hasil konsisten + // 'name' => $uniqueName, + // ]; + // } + public static function storeFile($type, $id, $file) { - // Pastikan directory tidak punya trailing slash + // 1. Ambil nama disk dari konfigurasi default + // Nilainya akan 'public', 'local', atau 's3' tergantung .env Anda + $disk = config('filesystems.default'); + $directory = rtrim(self::getDirectory($type), '/'); - // Buat nama file yang unik dan aman + // Buat nama file yang unik dan aman (kode Anda sudah bagus) $originalName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME); $extension = $file->getClientOriginalExtension(); $safeName = Str::slug($originalName); $uniqueName = $safeName . '-' . uniqid() . '.' . $extension; - // Upload file ke disk 's3' dengan visibility 'public' - $path = Storage::disk('s3')->putFileAs( - $directory, - $file, - $uniqueName, - 'public' + // 2. Gunakan disk yang sudah dinamis + $path = Storage::disk($disk)->putFileAs( + $directory, + $file, + $uniqueName ); - // Kembalikan path dan nama unik agar bisa digunakan di controller + // 3. (Sangat Direkomendasikan) Tambahkan penanganan error + if ($path === false) { + Log::error("Gagal menyimpan file ke disk '{$disk}' pada path: {$directory}/{$uniqueName}"); + return false; // Kembalikan false jika upload gagal + } + + // 4. Kembalikan path asli dari hasil upload untuk konsistensi return [ - 'path' => $directory . '/' . $uniqueName, // hasil konsisten + 'path' => $path, // Gunakan $path yang dikembalikan oleh Storage 'name' => $uniqueName, ]; } diff --git a/app/Models/RequestLog.php b/app/Models/RequestLog.php index bd180522..0ae461e9 100755 --- a/app/Models/RequestLog.php +++ b/app/Models/RequestLog.php @@ -53,7 +53,8 @@ class RequestLog extends Model 'created_final_by', 'specialities_id', 'dppj', - 'type_of_member' + 'type_of_member', + 'nominal', ]; protected $hidden = [ diff --git a/database/migrations/2025_09_10_103219_add_columns_nominal_to_table_request_log.php b/database/migrations/2025_09_10_103219_add_columns_nominal_to_table_request_log.php new file mode 100644 index 00000000..f2294eab --- /dev/null +++ b/database/migrations/2025_09_10_103219_add_columns_nominal_to_table_request_log.php @@ -0,0 +1,32 @@ +integer('nominal')->default(0)->after('total_cob'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('table_request_log', function (Blueprint $table) { + $table->dropColumn('nominal'); + }); + } +}; diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogSendWa.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogSendWa.tsx new file mode 100755 index 00000000..ddd123ab --- /dev/null +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/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/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx index 6c5eeb16..fb45bb08 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx @@ -30,6 +30,7 @@ import { useFieldArray, useForm } from 'react-hook-form'; 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'; @@ -43,6 +44,7 @@ import { Delete, EditOutlined, ExpandMore } from '@mui/icons-material'; import {BenefitData } from '../FinalLog/Model/Types' import AddIcon from '@mui/icons-material/Add'; import { LoadingButton } from '@mui/lab'; +import { makeFormData } from '@/utils/jsonToFormData'; // Import Card Detail Final LOG import CardDetail from '../Components/CardDetail'; @@ -67,6 +69,8 @@ import CardFile from '../Components/CardFile'; import DialogEditFinalLOG from './Components/DialogEditFinalLOG'; import DialogDeleteFileLog from './Components/DialogDeleteFileLog'; import DialogUploadFileFinalLog from './Components/DialogUploadFileFinalLog'; +import DialogSendWa from './Components/DialogSendWa'; +import { set } from 'nprogress'; // ---------------------------------------------------------------------- @@ -79,6 +83,7 @@ export default function Detail() { 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')}) @@ -91,7 +96,25 @@ export default function Detail() { const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods; const onSubmit = async (data: any) => { - alert('Nominal: ' + data.nominal); + 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 { id } = useParams(); @@ -130,6 +153,8 @@ export default function Detail() { 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(); @@ -175,16 +200,16 @@ export default function Detail() { const fileDiagnosaInput = useRef(null); - const [fileDiagnosas, setFileDiagnosas] = useState([]); + const [fileApprovals, setFileApproval] = useState([]); const handleDiagnosaInputChange = (event:any) => { if (event.target.files[0]) { - setFileDiagnosas([...fileDiagnosas, ...event.target.files]); + setFileApproval([...fileApprovals, ...event.target.files]); } else { console.log('NO FILE'); } }; - const removeDiagnosaFiles = (filesState:any, index:any) => { - setFileDiagnosas( + const removeApprovalFiles = (filesState:any, index:any) => { + setFileApproval( filesState.filter((file:any, fileIndex:any) => { return fileIndex != index; }) @@ -381,7 +406,7 @@ export default function Detail() { Upload Tindakan Persetujuan - {fileDiagnosas?.map((file: any, index: number) => ( + {fileApprovals?.map((file: any, index: number) => ( removeDiagnosaFiles(fileDiagnosas, index)} + onClick={() => removeApprovalFiles(fileApprovals, index)} /> ))} @@ -438,29 +463,60 @@ export default function Detail() { label="Nominal" required placeholder="Nominal" + value={requestLog?.nominal || 0} /> - Simpan - + */} + + {/* TOMBOL SIMPAN DI KIRI */} + + Simpan + + + {/* Ini adalah spacer untuk mendorong tombol berikutnya ke kanan */} + + + {/* GRUP TOMBOL DI KANAN */} + + + + + - + + )} {/* FILE YANG SUDAH TERUPLOAD */} - {requestLog?.files?.map((documentType, index) => ( + {requestLog?.files + ?.filter((document) => document.type === 'approval') + ?.map((documentType, index) => ( + + + + {/* Medicine */} @@ -843,7 +909,9 @@ export default function Detail() { ) : null } - {requestLog?.files?.map((documentType, index) => ( + {requestLog?.files + ?.filter((document) => document.type !== 'approval') + ?.map((documentType, index) => ( Date: Thu, 11 Sep 2025 10:03:01 +0700 Subject: [PATCH 3/9] penyesuaian url --- Modules/Internal/Transformers/RequestLogShowResource.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/Internal/Transformers/RequestLogShowResource.php b/Modules/Internal/Transformers/RequestLogShowResource.php index 9349c037..4b9432d5 100755 --- a/Modules/Internal/Transformers/RequestLogShowResource.php +++ b/Modules/Internal/Transformers/RequestLogShowResource.php @@ -178,6 +178,7 @@ class RequestLogShowResource extends JsonResource 'catatan' => $requestLog['catatan'], 'reason' => $requestLog['reason'], 'diagnosis' => $icd, + 'url_approval' => env('LMS_WEB_URL') . '/custormer-service/final-log/detail/'.$requestLog['id'], 'is_reversal' => $isReversal, // untuk penjagaan, jika true tidak bisa di edit/hapus lagi From c65a95ce09578441d70b464e243f3112de0c7b8c Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Fri, 12 Sep 2025 09:29:31 +0700 Subject: [PATCH 4/9] list dan detail approval pov apporoval --- database/seeders/NavigationSeeder.php | 5 + database/seeders/PermissionTableSeeder.php | 1 + .../layouts/dashboard/navbar/NavConfig.tsx | 1 + .../ApprovalMonitoring/Index.tsx | 30 + .../ApprovalMonitoring/List.tsx | 636 ++++++++++++++++++ .../pages/CustomerService/FinalLog/Detail.tsx | 79 ++- frontend/dashboard/src/routes/index.tsx | 9 + 7 files changed, 746 insertions(+), 15 deletions(-) create mode 100755 frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/Index.tsx create mode 100755 frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/List.tsx diff --git a/database/seeders/NavigationSeeder.php b/database/seeders/NavigationSeeder.php index 5a208800..19809bd1 100755 --- a/database/seeders/NavigationSeeder.php +++ b/database/seeders/NavigationSeeder.php @@ -137,6 +137,11 @@ class NavigationSeeder extends Seeder 'path' => '/case_management/inpatient_monitoring', 'permission' => 'final-log-list' ], + [ + 'title' => 'Approval Inpatient', + 'path' => '/case_management/approval_inpatient_monitoring', + 'permission' => 'approval-log-list' + ], ], 'permission' => null ], diff --git a/database/seeders/PermissionTableSeeder.php b/database/seeders/PermissionTableSeeder.php index 0013f925..70578c48 100755 --- a/database/seeders/PermissionTableSeeder.php +++ b/database/seeders/PermissionTableSeeder.php @@ -76,6 +76,7 @@ class PermissionTableSeeder extends Seeder 'user-access-list', 'report-katalog-dokter', 'invoice-payment-list', + 'approval-inpatient-monitoring', ] ], ####################### CLIENT PORTAL ######################### diff --git a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx index dee7accc..5add5536 100755 --- a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx @@ -85,6 +85,7 @@ const navConfig = [ { title: 'Daily Monitoring', path: '/case_management/daily_monitoring' }, // { title: 'Laboratorium Result', path: '/case_management/laboratorium_result' }, { title: 'Inpatient Monitoring', path: '/case_management/inpatient_monitoring' }, + { title: 'Approval Monitoring', path: '/case_management/inpatient_monitoring' }, ], }, { diff --git a/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/Index.tsx b/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/Index.tsx new file mode 100755 index 00000000..a0f1083d --- /dev/null +++ b/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/Index.tsx @@ -0,0 +1,30 @@ +import { Card, Stack } from "@mui/material"; +import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; +import Page from "../../../components/Page"; +import List from "./List"; + + + +export default function Claims() { + + const pageTitle = 'Approval Monitoring'; + return ( + + + + + {/* */} + + {/* */} + + ); +} diff --git a/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/List.tsx b/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/List.tsx new file mode 100755 index 00000000..864145af --- /dev/null +++ b/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/List.tsx @@ -0,0 +1,636 @@ +// @mui +import { + Box, + Button, + Card, + Collapse, + IconButton, + MenuItem, + Table, + TableBody, + TableCell, + TableRow, + TextField, + Typography, + Stack, + Menu, + ButtonGroup, + FormControl, + Select, + Link, + Chip, + TableHead, + InputLabel, + Grid, +} from '@mui/material'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import AddIcon from '@mui/icons-material/Add'; +import UploadIcon from '@mui/icons-material/Upload'; +import CancelIcon from '@mui/icons-material/Cancel'; + +import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +// hooks +import React, { ChangeEvent, useEffect, useRef, useState } from 'react'; +import { Navigate, useNavigate, useSearchParams } from 'react-router-dom'; +import useSettings from '@/hooks/useSettings'; +// components +import axios from '../../../utils/axios'; +import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../../@types/paginated-data'; +import DataTable from '../../../components/LaravelTable'; +import { fCurrency } from '../../../utils/formatNumber'; +import EditRoundedIcon from '@mui/icons-material/EditRounded'; +import { LoadingButton } from '@mui/lab'; +import { enqueueSnackbar } from 'notistack'; +import { Divider } from '@mui/material'; +import Iconify from '@/components/Iconify'; +import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim'; +import { fDateTimesecond } from '@/utils/formatTime'; +import { capitalizeFirstLetter } from '@/utils/formatString'; +import Label from '@/components/Label'; +import TableMoreMenu from '@/components/table/TableMoreMenu'; +import { Import } from '@/@types/claims'; + +import { FinalLogType } from '../../CustomerService/FinalLog/Model/Types'; +import DialogDeleteFinalLOG from '@/pages/CustomerService/FinalLog/Components/DialogDeleteFinalLOG'; +import { Delete } from '@mui/icons-material'; +import useAuth from '@/hooks/useAuth'; +// import LoadingButton from '@/theme/overrides/LoadingButton'; + +export default function List() { + const { themeColorPresets } = useSettings(); + const [searchParams, setSearchParams] = useSearchParams(); + const [importResult, setImportResult] = useState(null); + const { user } = useAuth(); + + const navigate = useNavigate() + + const fileOptions = { + kondisi: 'Dokumen Billing', + diagnosa: 'Dokumen Diagnosa', + result: 'Dokumen Penduk Medis', + none: 'Belum ada Dokumen' + }; + + function SearchInput(props: any) { + // SEARCH + const searchInput = useRef(null); + const [searchText, setSearchText] = useState(''); + + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + props.onSearch({ search: searchText }); // Trigger to Parent + }; + + useEffect(() => { + // Trigger First Search + setSearchText(searchParams.get('search') ?? ''); + }, []); + + return ( +
+ + + ); + } + + function ImportForm(props: any) { + // IMPORT + // Create Button Menu + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + const importForm = useRef(null); + const [currentImportFileName, setCurrentImportFileName] = useState(null); + const [importLoading, setImportLoading] = useState(false); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + const handleImportButton = () => { + if (importForm?.current) { + handleClose(); + importForm.current ? importForm.current.click() : console.log('No File selected'); + } else { + alert('No file selected'); + } + }; + + const handleCancelImportButton = () => { + importForm.current.value = ''; + importForm.current.dispatchEvent(new Event('change', { bubbles: true })); + }; + + const handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name); + } else { + setCurrentImportFileName(null); + } + }; + + const handleUpload = () => { + if (importForm.current?.files.length) { + const formData = new FormData(); + formData.append('file', importForm.current?.files[0]); + + setImportLoading(true); + axios + .post(`claim-requests/import`, formData) + .then((response) => { + handleCancelImportButton(); + loadDataTableData(); + setImportResult(response.data); + // alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows'); + setImportLoading(false); + }) + .catch((response) => { + enqueueSnackbar( + 'Looks like something went wrong. Please check your data and try again. ' + + response.message, + { variant: 'error' } + ); + setImportLoading(false); + }); + } else { + enqueueSnackbar('No File Selected', { variant: 'warning' }); + } + }; + + const handleGetTemplate = (type :string) => { + axios.get('corporates/import-document-example/' + type) + .then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + }) + } + + const handleGetData = (type :string) => { + axios.get(`corporates/${corporate_id}/data-plan-benefit`) + .then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + }) + } + + return ( +
+ + {!currentImportFileName && ( + + + + + + + File + + + + + + + + Import + {handleGetTemplate('claim-request')}}>Download Template + {handleGetData('data-plan-benefit')}}>Download Claim Request + + {/* */} + + )} + + {currentImportFileName && ( + + + + + + + } + sx={{ p: 1.8 }} + onClick={handleUpload} + loading={importLoading} + > + Upload + + + )} + {importResult && ( + + + Last Import Result Report :{' '} + + {importResult.result_file?.name ?? '-'} + + + + )} +
+ ); + } + + // Dummy Default Data + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableData, setDataTableData] = useState( + LaravelPaginatedDataDefault + ); + + const loadDataTableData = async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get('/customer-service/request?final_log=1&service_code=IP', { params: filter }); + // console.log(response.data); + setDataTableLoading(false); + + setDataTableData(response.data); + }; + + const applyFilter = async (searchFilter: { search: string }) => { + await loadDataTableData(searchFilter); + setSearchParams(searchFilter); + }; + + const handlePageChange = (event: ChangeEvent, value: number): void => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); + loadDataTableData(filter); + setSearchParams(filter); + }; + + // Handel Delete Final LOG + const [idFinalLog, setidFinalLog] = useState(); + const [openDialogDeleteFinalLog, setDialogDeleteFinalLog] = useState(false) + + useEffect(() => { + loadDataTableData(); + }, []); + + const headStyle = { + fontWeight: 'bold', + }; + + // Called on every row to map the data to the columns + function createData(data: FinalLogType) { + return { + ...data, + }; + } + + { + /* ------------------ TABLE ROW ------------------ */ + } + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + const [loadingApprove, setLoadingApprove] = React.useState(false); + return ( + + *': { borderBottom: 'unset' } }}> + {/* + setOpen(!open)}> + {open ? : } + + */ } + {/* + { + // handleShowClaim(row); + // }} + > + {row.id} + + */} + {row.code} + {row.provider} + {row.member_name} + + {row.service_name} + {row.payment_type_name} + + {row.files_by_type?.final_log_diagnosis?.length > 0 && ( + <> + +
+ + )} + + {row.files_by_type?.final_log_kondisi?.length > 0 && ( + <> + +
+ + )} + + {row.files_by_type?.final_log_result?.length > 0 && ( + + )} +
+ + { row.status_final_log == "requested" ? + () : + row.status_final_log == "declined" ? + () + : + () + } + + + + {/* navigate(`/claim-requests/edit/${row.id}`)}> + + Edit + */} + navigate ('/custormer-service/final-log/detail/'+row.id+'/'+user.id)}> + + Detail + + { + setidFinalLog(row.id) + setDialogDeleteFinalLog(true) + }} + > + + Delete + + + } /> + + {/* + + { + handleShowClaim(row); + }} + > + + + */} +
+ {/* COLLAPSIBLE ROW */} + + + + + } + spacing={1} + sx={{ marginY: 2 }} + > + + Berkas Hasil Penunjang + {/* {row.files_by_type?.claim_kondisi && + row.files_by_type?.claim_kondisi.map((file, index) => ( + + -{' '} + + {file.name} + + + ))} */} + + {row.files_by_type?.claim_kondisi && ( + <> + - Kondisi + {row.files_by_type?.claim_kondisi.map((file, index) => ( + + + + {file.name} + + + ))} + + )} + + {row.files_by_type?.claim_diagnosis && ( + <> + - Diagnosa + {row.files_by_type?.claim_diagnosis.map((file, index) => ( + + + + {file.name} + + + ))} + + )} + + {row.files_by_type?.claim_result && ( + <> + - Hasil + {row.files_by_type?.claim_result.map((file, index) => ( + + + + {file.name} + + + ))} + + )} + {(!row.files_by_type?.claim_result && !row.files_by_type?.claim_diagnosis && !row.files_by_type?.claim_kondisi)&& Tidak ada berkas} + + + + + + +
+ ); + } + { + /* ------------------ END TABLE ROW ------------------ */ + } + + function TableContent() { + return ( + + {/* ------------------ TABLE HEADER ------------------ */} + + + {/* */} + {/* + ID Request LOG + */} + + Code + + + Provider + + + Name + + + Date of Admission + + + Service Type + + + Claim Method + + + File Upload + + + Status + + + + + {/* ------------------ END TABLE HEADER ------------------ */} + + {/* ------------------ TABLE ROW ------------------ */} + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length === 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} + {/* ------------------ END TABLE ROW ------------------ */} +
+ ); + } + return ( + + + + + + + } + /> + + + {/* Dialog Delete */} + + + + ); +} diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx index fb45bb08..88724ef4 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx @@ -117,7 +117,7 @@ export default function Detail() { }); } - const { id } = useParams(); + const { id, approval } = useParams(); useEffect(() => { axios @@ -474,14 +474,46 @@ export default function Detail() { > Simpan */} - + + {approval ? ( + <> + + {/* GRUP TOMBOL DI KANAN */} + + + + + + + ) : ( + <> {/* TOMBOL SIMPAN DI KIRI */} Simpan @@ -489,19 +521,36 @@ export default function Detail() { {/* Ini adalah spacer untuk mendorong tombol berikutnya ke kanan */} - {/* GRUP TOMBOL DI KANAN */} - - - - - + {/* GRUP TOMBOL DI KANAN */} + + + + + + + )} + diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index a44879f0..9082b104 100755 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -269,6 +269,10 @@ export default function Router() { path: 'inpatient_monitoring', // Inpatient Monitoring element: }, + { + path: 'approval_inpatient_monitoring', // Approval Monitoring + element: + }, ] }, { @@ -555,6 +559,10 @@ export default function Router() { { path: 'custormer-service/final-log/detail/:id', element: , + }, + { + path: 'custormer-service/final-log/detail/:id/:approval', + element: , }, { path: 'e-prescription/live-chat', @@ -714,6 +722,7 @@ const DetailLabResultForm = Loadable(lazy(() => import('../pages/CaseManage const DetailLabResultList = Loadable(lazy(() => import('../pages/CaseManagement/LaboratoriumResult/Components/DetailLabResultList'))) // Inpatient Monitoring const InpatientMonitoring = Loadable(lazy(() => import('../pages/CaseManagement/InpatientMonitoring/Index'))) +const ApprovalMonitoring = Loadable(lazy(() => import('../pages/CaseManagement/ApprovalMonitoring/Index'))) /** From acfc8d1288089632dcfaeacab7b4f743ed526e33 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Fri, 12 Sep 2025 11:28:15 +0700 Subject: [PATCH 5/9] update approval di list approval --- .../Controllers/Api/RequestLogController.php | 5 + .../Transformers/RequestLogResource.php | 2 + .../Transformers/RequestLogShowResource.php | 3 +- app/Models/RequestLog.php | 2 + ..._columns_nominal_to_table_request_log.php} | 4 + .../ApprovalMonitoring/List.tsx | 73 ++++++++- .../pages/CustomerService/FinalLog/Detail.tsx | 143 +++++++++++------- .../CustomerService/FinalLog/Model/Types.tsx | 4 +- 8 files changed, 174 insertions(+), 62 deletions(-) rename database/migrations/{2025_09_10_103219_add_columns_nominal_to_table_request_log.php => 2025_09_10_103221_add_columns_nominal_to_table_request_log.php} (70%) diff --git a/Modules/Internal/Http/Controllers/Api/RequestLogController.php b/Modules/Internal/Http/Controllers/Api/RequestLogController.php index 4058a280..d66b3255 100755 --- a/Modules/Internal/Http/Controllers/Api/RequestLogController.php +++ b/Modules/Internal/Http/Controllers/Api/RequestLogController.php @@ -346,6 +346,11 @@ class RequestLogController extends Controller $requestLog->approved_at = Carbon::now(); } + if ($request->status_approval){ + $requestLog->status_approval = $request->status_approval; + $requestLog->approval_nominal_by = auth()->user()->id; + } + $requestLog->save(); // update nirc member diff --git a/Modules/Internal/Transformers/RequestLogResource.php b/Modules/Internal/Transformers/RequestLogResource.php index 60a4f0ac..4ecc2d10 100755 --- a/Modules/Internal/Transformers/RequestLogResource.php +++ b/Modules/Internal/Transformers/RequestLogResource.php @@ -31,6 +31,8 @@ class RequestLogResource extends JsonResource 'status' => $this->status ?? 'unknown', 'provider' => $provider ? $provider->name : '-', 'status_final_log' => $this->status_final_log ?? 'unknown', + 'nominal' => $this->nominal ?? 'unknown', + 'status_approval' => $this->status_approval ?? 'requested', 'service_name' => $this->service ? $this->service->name : '', 'payment_type' => $this->payment_type, 'payment_type_name' => $this->payment_type_name, diff --git a/Modules/Internal/Transformers/RequestLogShowResource.php b/Modules/Internal/Transformers/RequestLogShowResource.php index 4b9432d5..54a67090 100755 --- a/Modules/Internal/Transformers/RequestLogShowResource.php +++ b/Modules/Internal/Transformers/RequestLogShowResource.php @@ -175,10 +175,11 @@ class RequestLogShowResource extends JsonResource 'hak_kamar_pasien' => $requestLog['hak_kamar_pasien'], 'penempatan_kamar' => $requestLog['penempatan_kamar'], 'nominal' => $requestLog['nominal'], + 'status_approval' => $requestLog['status_approval'], 'catatan' => $requestLog['catatan'], 'reason' => $requestLog['reason'], 'diagnosis' => $icd, - 'url_approval' => env('LMS_WEB_URL') . '/custormer-service/final-log/detail/'.$requestLog['id'], + 'url_approval' => env('LMS_WEB_URL') . '/custormer-service/final-log/detail/'.$requestLog['id'] . '/' . auth()->user()->id, 'is_reversal' => $isReversal, // untuk penjagaan, jika true tidak bisa di edit/hapus lagi diff --git a/app/Models/RequestLog.php b/app/Models/RequestLog.php index 0ae461e9..d415be4f 100755 --- a/app/Models/RequestLog.php +++ b/app/Models/RequestLog.php @@ -33,6 +33,7 @@ class RequestLog extends Model 'final_log', 'status', 'status_final_log', + 'status_approval', 'source', 'claim_id', 'organization_id', @@ -55,6 +56,7 @@ class RequestLog extends Model 'dppj', 'type_of_member', 'nominal', + 'approval_nominal_by', ]; protected $hidden = [ diff --git a/database/migrations/2025_09_10_103219_add_columns_nominal_to_table_request_log.php b/database/migrations/2025_09_10_103221_add_columns_nominal_to_table_request_log.php similarity index 70% rename from database/migrations/2025_09_10_103219_add_columns_nominal_to_table_request_log.php rename to database/migrations/2025_09_10_103221_add_columns_nominal_to_table_request_log.php index f2294eab..d5094d82 100644 --- a/database/migrations/2025_09_10_103219_add_columns_nominal_to_table_request_log.php +++ b/database/migrations/2025_09_10_103221_add_columns_nominal_to_table_request_log.php @@ -15,6 +15,8 @@ return new class extends Migration { Schema::table('request_logs', function (Blueprint $table) { $table->integer('nominal')->default(0)->after('total_cob'); + $table->string('status_approval')->nullable()->after('status_final_log'); + $table->integer('approval_nominal_by')->nullable()->after('status_approval'); }); } @@ -27,6 +29,8 @@ return new class extends Migration { Schema::table('table_request_log', function (Blueprint $table) { $table->dropColumn('nominal'); + $table->dropColumn('status_approval'); + $table->dropColumn('approval_nominal_by'); }); } }; diff --git a/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/List.tsx b/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/List.tsx index 864145af..216603a7 100755 --- a/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/List.tsx +++ b/frontend/dashboard/src/pages/CaseManagement/ApprovalMonitoring/List.tsx @@ -367,6 +367,39 @@ export default function List() { }; } + const updateApproval = async (id:any) => { + 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(() => { + }); + }; + + + const updateDecline = async (id:any) => { + 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(() => { + }); + } + { /* ------------------ TABLE ROW ------------------ */ } @@ -423,14 +456,38 @@ export default function List() { )} - { row.status_final_log == "requested" ? - () : - row.status_final_log == "declined" ? - () + { row.status_approval == "requested" ? + () : + row.status_approval == "declined" ? + () : - () + () } + {fCurrency(row.nominal)} + + {row.status_approval !== "approved" && ( + + + + + + )} + @@ -575,6 +632,12 @@ export default function List() { Status + + Nominal + + + Action + diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx index 88724ef4..e3e27566 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx @@ -117,6 +117,43 @@ export default function Detail() { }); } + 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(() => { @@ -457,13 +494,14 @@ export default function Detail() { /> - {/* Simpan */} - + {approval ? ( <> - - {/* GRUP TOMBOL DI KANAN */} - - + + {/* GRUP TOMBOL DI KANAN */} + {requestLog?.status_approval !== 'approved' && ( + + - - + + + )} - ) : ( + ) : ( <> - {/* TOMBOL SIMPAN DI KIRI */} - + > Simpan - + - {/* Ini adalah spacer untuk mendorong tombol berikutnya ke kanan */} - + {/* Ini adalah spacer untuk mendorong tombol berikutnya ke kanan */} + - {/* GRUP TOMBOL DI KANAN */} - + {/* GRUP TOMBOL DI KANAN */} + - + - )} + )} @@ -1009,8 +1043,7 @@ export default function Detail() { variant="outlined" sx={{ color: '#FF4842', borderColor: '#FF4842' }} onClick={() => { - setOpenDialogSubmit(true); - setApprove('declined'); + }} > Decline diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx index 6c1d2a16..cb9081c1 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx @@ -21,6 +21,8 @@ export type FinalLogType = { 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, @@ -43,12 +45,12 @@ export type DetailFinalLogType = { marital_status : string, admission_date : string, submission_date : string, - admission_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, From b27682f5b32a74464f453d0678f7df0d9f49fbf3 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Mon, 15 Sep 2025 15:13:13 +0700 Subject: [PATCH 6/9] update --- ...25_09_10_103221_add_columns_nominal_to_table_request_log.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2025_09_10_103221_add_columns_nominal_to_table_request_log.php b/database/migrations/2025_09_10_103221_add_columns_nominal_to_table_request_log.php index d5094d82..4ee89307 100644 --- a/database/migrations/2025_09_10_103221_add_columns_nominal_to_table_request_log.php +++ b/database/migrations/2025_09_10_103221_add_columns_nominal_to_table_request_log.php @@ -27,7 +27,7 @@ return new class extends Migration */ public function down() { - Schema::table('table_request_log', function (Blueprint $table) { + Schema::table('request_logs', function (Blueprint $table) { $table->dropColumn('nominal'); $table->dropColumn('status_approval'); $table->dropColumn('approval_nominal_by'); From a29c39086a3069248d8217321b1795206d0d6d25 Mon Sep 17 00:00:00 2001 From: Fadila Date: Mon, 22 Sep 2025 09:04:00 +0700 Subject: [PATCH 7/9] set cron job update no sjp --- app/Console/Commands/UpdateNoSjp.php | 48 ++++++++++++++++++++++++++++ app/Console/Kernel.php | 1 + 2 files changed, 49 insertions(+) create mode 100644 app/Console/Commands/UpdateNoSjp.php diff --git a/app/Console/Commands/UpdateNoSjp.php b/app/Console/Commands/UpdateNoSjp.php new file mode 100644 index 00000000..50c690bc --- /dev/null +++ b/app/Console/Commands/UpdateNoSjp.php @@ -0,0 +1,48 @@ +info('Checking Data ...'); + $client = new Client([ 'base_uri' => 'https://api.linksehat.dev', 'headers' => ['Content-Type' => 'application/json'], 'timeout' => 10, ]); + + $liveChats = Livechat::query() + ->join('tx_livechat_summary', 'tx_livechat_summary.nIDLiveChat', '=', 'tx_livechat.nID') + ->whereNull('tx_livechat.sNoSpj') + ->whereDate('tx_livechat.dCreateOn', Carbon::today()) + ->select('tx_livechat.nID', 'tx_livechat_summary.nID as summary_id') + ->get(); + + foreach ($liveChats as $row) { + try { + $prescriptionData = [ + 'nIDLivechats' => [$row->nID] + ]; + + $response = $client->post('/api/rujukan-dan-sjp', [ + 'json' => $prescriptionData, + ]); + + $body = json_decode($response->getBody()->getContents(), true); + + $this->info('Jumlah Livechat dikirim: ' . $liveChats->count()); + $this->info("Nomor SJP berhasil dibuat untuk Livechat {$row->nID}"); + + } catch (\Exception $e) { + $this->error("Gagal generate Nomor SJP Livechat {$row->nID}: " . $e->getMessage()); + } + } + + $this->info('Proses selesai.'); + + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 5489a453..46ff531f 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -17,6 +17,7 @@ class Kernel extends ConsoleKernel { // $schedule->command('inspire')->hourly(); $schedule->command('log:auto-update-status')->dailyAt('01:00'); + $schedule->command('update:no-sjp')->everyThirtyMinutes(); } /** From 42ea2e108de1850b08bf21490ac4a54df4354f49 Mon Sep 17 00:00:00 2001 From: Fadila Date: Mon, 22 Sep 2025 09:19:52 +0700 Subject: [PATCH 8/9] tambah table log update np sjp --- app/Console/Commands/UpdateNoSjp.php | 18 ++++++++-- app/Models/LivechatUpdateLog.php | 18 ++++++++++ ...1254_create_livechat_update_logs_table.php | 35 +++++++++++++++++++ database/seeders/LivechatUpdateLogSeeder.php | 19 ++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 app/Models/LivechatUpdateLog.php create mode 100644 database/migrations/2025_09_22_091254_create_livechat_update_logs_table.php create mode 100644 database/seeders/LivechatUpdateLogSeeder.php diff --git a/app/Console/Commands/UpdateNoSjp.php b/app/Console/Commands/UpdateNoSjp.php index 50c690bc..366ddb0c 100644 --- a/app/Console/Commands/UpdateNoSjp.php +++ b/app/Console/Commands/UpdateNoSjp.php @@ -1,6 +1,7 @@ getBody()->getContents(), true); - - $this->info('Jumlah Livechat dikirim: ' . $liveChats->count()); + + LivechatUpdateLog::create([ + 'nIDLivechat' => $row->nID, + 'nIDSummary' => $row->summary_id, + 'status' => 'success', + 'response' => json_encode($body) + ]); + $this->info("Nomor SJP berhasil dibuat untuk Livechat {$row->nID}"); } catch (\Exception $e) { + LivechatUpdateLog::create([ + 'nIDLivechat' => $row->nID, + 'nIDSummary' => $row->summary_id, + 'status' => 'fail', + 'response' => $e->getMessage() + ]); + $this->error("Gagal generate Nomor SJP Livechat {$row->nID}: " . $e->getMessage()); } } diff --git a/app/Models/LivechatUpdateLog.php b/app/Models/LivechatUpdateLog.php new file mode 100644 index 00000000..fb34d6fd --- /dev/null +++ b/app/Models/LivechatUpdateLog.php @@ -0,0 +1,18 @@ +id(); + $table->unsignedBigInteger('nIDLivechat'); + $table->unsignedBigInteger('nIDSummary')->nullable(); + $table->string('status'); + $table->text('response')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('livechat_update_logs'); + } +}; diff --git a/database/seeders/LivechatUpdateLogSeeder.php b/database/seeders/LivechatUpdateLogSeeder.php new file mode 100644 index 00000000..73a886df --- /dev/null +++ b/database/seeders/LivechatUpdateLogSeeder.php @@ -0,0 +1,19 @@ + Date: Mon, 22 Sep 2025 16:02:58 +0700 Subject: [PATCH 9/9] update --- database/seeders/PermissionTableSeeder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/seeders/PermissionTableSeeder.php b/database/seeders/PermissionTableSeeder.php index 70578c48..7256f115 100755 --- a/database/seeders/PermissionTableSeeder.php +++ b/database/seeders/PermissionTableSeeder.php @@ -76,7 +76,7 @@ class PermissionTableSeeder extends Seeder 'user-access-list', 'report-katalog-dokter', 'invoice-payment-list', - 'approval-inpatient-monitoring', + 'approval-log-list', ] ], ####################### CLIENT PORTAL #########################