Merge remote-tracking branch 'origin/staging' into origin/production

This commit is contained in:
Server D3 Linksehat
2025-09-22 16:07:43 +07:00
22 changed files with 1530 additions and 18 deletions

View File

@@ -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
@@ -1224,6 +1229,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');
}

View File

@@ -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']);

View File

@@ -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,

View File

@@ -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();
@@ -174,9 +174,12 @@ class RequestLogShowResource extends JsonResource
'keterangan' => $requestLog['keterangan'],
'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'] . '/' . auth()->user()->id,
'is_reversal' => $isReversal, // untuk penjagaan, jika true tidak bisa di edit/hapus lagi

View File

@@ -0,0 +1,62 @@
<?php
namespace App\Console\Commands;
use App\Models\LivechatUpdateLog;
use App\Models\OLDLMS\Livechat;
use Carbon\Carbon;
use Illuminate\Console\Command;
use GuzzleHttp\Client;
class UpdateNoSJP extends Command {
protected $signature = 'update:no-sjp';
protected $description = 'Cek data Livechat yang sNoSpj kosong lalu generate SJP lewat API';
public function handle() {
$this->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);
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());
}
}
$this->info('Proses selesai.');
return Command::SUCCESS;
}
}

View File

@@ -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();
}
/**

View File

@@ -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,
];
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class LivechatUpdateLog extends Model
{
use HasFactory;
protected $fillable = [
'nIDLivechat',
'nIDSummary',
'status',
'response'
];
}

View File

@@ -33,6 +33,7 @@ class RequestLog extends Model
'final_log',
'status',
'status_final_log',
'status_approval',
'source',
'claim_id',
'organization_id',
@@ -53,7 +54,9 @@ class RequestLog extends Model
'created_final_by',
'specialities_id',
'dppj',
'type_of_member'
'type_of_member',
'nominal',
'approval_nominal_by',
];
protected $hidden = [

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
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');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('request_logs', function (Blueprint $table) {
$table->dropColumn('nominal');
$table->dropColumn('status_approval');
$table->dropColumn('approval_nominal_by');
});
}
};

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('livechat_update_logs', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,19 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class LivechatUpdateLogSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
}
}

View File

@@ -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
],

View File

@@ -76,6 +76,7 @@ class PermissionTableSeeder extends Seeder
'user-access-list',
'report-katalog-dokter',
'invoice-payment-list',
'approval-log-list',
]
],
####################### CLIENT PORTAL #########################

View File

@@ -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' },
],
},
{

View File

@@ -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 (
<Page title={ pageTitle } sx={{ mx: 2}}>
<HeaderBreadcrumbs
heading={ pageTitle }
links={[
{ name: 'Dashboard', href: '/dashboard' },
{
name: 'Approval Monitoring',
href: '/approval_inpatient_monitoring',
},
]}
/>
{/* <Stack> */}
<List />
{/* </Stack> */}
</Page>
);
}

View File

@@ -0,0 +1,699 @@
// @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<Import>(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<HTMLInputElement>(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 (
<form onSubmit={handleSearchSubmit} style={{ width: '100%' }}>
<TextField
id="search-input"
ref={searchInput}
label="Search"
variant="outlined"
fullWidth
onChange={handleSearchChange}
value={searchText}
placeholder='Search Code or Name...'
/>
</form>
);
}
function ImportForm(props: any) {
// IMPORT
// Create Button Menu
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const createMenu = Boolean(anchorEl);
const importForm = useRef<HTMLInputElement>(null);
const [currentImportFileName, setCurrentImportFileName] = useState(null);
const [importLoading, setImportLoading] = useState(false);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
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 (
<div>
<input
type="file"
id="file"
ref={importForm}
style={{ display: 'none' }}
onChange={handleImportChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
{!currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<Grid item md={9}>
<SearchInput onSearch={applyFilter} />
</Grid>
<Grid item md={2}>
<FormControl fullWidth>
<InputLabel>File</InputLabel>
<Select
value={searchParams.get('file') ?? 'semua'} // Pastikan menggunakan kunci 'file'
label="File"
onChange={(el) => {
const selectedValue = el.target.value;
const filter = Object.fromEntries(searchParams.entries());
if (selectedValue === 'semua') {
delete filter.file; // Menghapus filter 'file' jika memilih 'semua'
} else {
filter.file = selectedValue; // Menambahkan atau memperbarui filter 'file'
}
setSearchParams(filter); // Update state searchParams
loadDataTableData(filter); // Memuat data sesuai filter
}}
>
<MenuItem value={'semua'}>Semua</MenuItem>
{Object.entries(fileOptions).map((option, index) => (
<MenuItem value={option[0]} key={index}>
{option[1]}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item md={1}>
<Button
variant="outlined"
startIcon={<UploadIcon />}
sx={{ p: 1.8 }}
onClick={handleClick}
>
Import
</Button>
</Grid>
<Menu
id="import-button"
anchorEl={anchorEl}
open={createMenu}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
<MenuItem onClick={handleImportButton}>Import</MenuItem>
<MenuItem onClick={() => {handleGetTemplate('claim-request')}}>Download Template</MenuItem>
<MenuItem onClick={() => {handleGetData('data-plan-benefit')}}>Download Claim Request</MenuItem>
</Menu>
{/* <Button
variant="contained"
startIcon={<AddIcon />}
sx={{ p: 1.8 }}
onClick={() => {
navigate('/claim-requests/create');
}}
>
Create
</Button> */}
</Stack>
)}
{currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<ButtonGroup variant="outlined" aria-label="outlined button group" fullWidth>
<Button onClick={handleImportButton} fullWidth>
{currentImportFileName ?? 'No File Selected'}
</Button>
<Button
onClick={handleCancelImportButton}
size="small"
fullWidth={false}
sx={{ p: 1.8 }}
>
<CancelIcon color="error" />
</Button>
</ButtonGroup>
<LoadingButton
id="upload-button"
variant="outlined"
startIcon={<UploadIcon />}
sx={{ p: 1.8 }}
onClick={handleUpload}
loading={importLoading}
>
Upload
</LoadingButton>
</Stack>
)}
{importResult && (
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
<Box sx={{ color: 'text.secondary' }}>
Last Import Result Report :{' '}
<a href={importResult.result_file?.url ?? '#'}>
{importResult.result_file?.name ?? '-'}
</a>
</Box>
</Stack>
)}
</div>
);
}
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = useState(true);
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>(
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<number>();
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,
};
}
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 ------------------ */
}
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const [loadingApprove, setLoadingApprove] = React.useState(false);
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
{/* <TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell> */ }
{/* <TableCell align="left">
<Typography
// onClick={() => {
// handleShowClaim(row);
// }}
>
{row.id}
</Typography>
</TableCell> */}
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.provider}</TableCell>
<TableCell align="left">{row.member_name}</TableCell>
<TableCell align="left"><Label>{fDateTimesecond(row.admission_date)}</Label></TableCell>
<TableCell align="left">{row.service_name}</TableCell>
<TableCell align="left">{row.payment_type_name}</TableCell>
<TableCell align="left">
{row.files_by_type?.final_log_diagnosis?.length > 0 && (
<>
<Label variant='ghost' color='primary'>
{row.files_by_type.final_log_diagnosis.length} File Diagnosa
</Label>
<br />
</>
)}
{row.files_by_type?.final_log_kondisi?.length > 0 && (
<>
<Label variant='ghost' color='success'>
{row.files_by_type.final_log_kondisi.length} File Billing
</Label>
<br />
</>
)}
{row.files_by_type?.final_log_result?.length > 0 && (
<Label variant='ghost' color='warning'>
{row.files_by_type.final_log_result.length} File Pendukung Medis
</Label>
)}
</TableCell>
<TableCell align="left">
{ row.status_approval == "requested" ?
(<Label variant='ghost' color='primary'>{capitalizeFirstLetter(row.status_approval)}</Label>) :
row.status_approval == "declined" ?
(<Label color='error'> {capitalizeFirstLetter(row.status_approval)}</Label>)
:
(<Label color='success'> {capitalizeFirstLetter(row.status_approval)}</Label>)
}
</TableCell>
<TableCell align="left">{fCurrency(row.nominal)}</TableCell>
<TableCell align="left">
{row.status_approval !== "approved" && (
<Stack direction="row" spacing={1.5} mt={2}>
<Button
color="error"
variant="outlined"
size="small"
onClick={() => updateDecline(row.id)}
>
Decline
</Button>
<Button
variant="contained"
size="small"
color="primary"
onClick={() => updateApproval(row.id)}
>
Approve
</Button>
</Stack>
)}
</TableCell>
<TableCell align="right">
<TableMoreMenu actions={
<>
{/* <MenuItem onClick={() => navigate(`/claim-requests/edit/${row.id}`)}>
<EditOutlinedIcon />
Edit
</MenuItem> */}
<MenuItem onClick={() => navigate ('/custormer-service/final-log/detail/'+row.id+'/'+user.id)}>
<FindInPageOutlinedIcon />
Detail
</MenuItem>
<MenuItem onClick={() => {
setidFinalLog(row.id)
setDialogDeleteFinalLog(true)
}}
>
<Delete color='error'/>
Delete
</MenuItem>
</>
} />
</TableCell>
{/* <TableCell>
<IconButton
onClick={() => {
handleShowClaim(row);
}}
>
<Iconify icon="eva:eye-fill" />
</IconButton>
</TableCell> */}
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={99}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ borderBottom: 1 }}>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
<Box>
<Typography fontWeight={600}>Berkas Hasil Penunjang</Typography>
{/* {row.files_by_type?.claim_kondisi &&
row.files_by_type?.claim_kondisi.map((file, index) => (
<Stack direction="row" key={index}>
<Typography sx={{ marginRight: 2 }}>-</Typography>{' '}
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))} */}
{row.files_by_type?.claim_kondisi && (
<>
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Kondisi</Typography>
{row.files_by_type?.claim_kondisi.map((file, index) => (
<Stack direction="row" key={index}>
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
</>
)}
{row.files_by_type?.claim_diagnosis && (
<>
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Diagnosa</Typography>
{row.files_by_type?.claim_diagnosis.map((file, index) => (
<Stack direction="row" key={index}>
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
</>
)}
{row.files_by_type?.claim_result && (
<>
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Hasil</Typography>
{row.files_by_type?.claim_result.map((file, index) => (
<Stack direction="row" key={index}>
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
</>
)}
{(!row.files_by_type?.claim_result && !row.files_by_type?.claim_diagnosis && !row.files_by_type?.claim_kondisi)&& <Typography>Tidak ada berkas</Typography>}
</Box>
</Stack>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
{
/* ------------------ END TABLE ROW ------------------ */
}
function TableContent() {
return (
<Table aria-label="collapsible table">
{/* ------------------ TABLE HEADER ------------------ */}
<TableHead>
<TableRow>
{/* <TableCell style={headStyle} align="left" /> */}
{/* <TableCell style={headStyle} align="left">
ID Request LOG
</TableCell> */}
<TableCell style={headStyle} align="left">
Code
</TableCell>
<TableCell style={headStyle} align="left">
Provider
</TableCell>
<TableCell style={headStyle} align="left">
Name
</TableCell>
<TableCell style={headStyle} align="left">
Date of Admission
</TableCell>
<TableCell style={headStyle} align="left">
Service Type
</TableCell>
<TableCell style={headStyle} align="left">
Claim Method
</TableCell>
<TableCell style={headStyle} align="left">
File Upload
</TableCell>
<TableCell style={headStyle} align="left">
Status
</TableCell>
<TableCell style={headStyle} align="left">
Nominal
</TableCell>
<TableCell style={headStyle} align="left">
Action
</TableCell>
<TableCell style={headStyle} align="right"></TableCell>
</TableRow>
</TableHead>
{/* ------------------ END TABLE HEADER ------------------ */}
{/* ------------------ TABLE ROW ------------------ */}
{dataTableIsLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
Loading
</TableCell>
</TableRow>
</TableBody>
) : dataTableData.data.length === 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
No Data
</TableCell>
</TableRow>
</TableBody>
) : (
<TableBody>
{dataTableData.data.map((row) => (
<Row key={row.id} row={row} />
))}
</TableBody>
)}
{/* ------------------ END TABLE ROW ------------------ */}
</Table>
);
}
return (
<Grid container>
<Grid item sm={12}>
<ImportForm />
</Grid>
<Grid item sm={12}>
<DataTable
isLoading={dataTableIsLoading}
lastRequest={0}
data={dataTableData}
handlePageChange={handlePageChange}
TableContent={<TableContent />}
/>
</Grid>
<Grid item sm={12}>
{/* Dialog Delete */}
<DialogDeleteFinalLOG
id={idFinalLog}
openDialog={openDialogDeleteFinalLog}
setOpenDialog={setDialogDeleteFinalLog}
/>
</Grid>
</Grid>
);
}

View File

@@ -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 = () => (
<Stack spacing={1} marginTop={2}>

View File

@@ -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 = () => (
<Stack spacing={2} sx={{ marginTop: 2, padding: 2 }}>
<Typography>Are you sure want to send this request ?</Typography>
<Paper variant="outlined" sx={{ p: 2 }}>
<Grid container spacing={1}>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Member ID
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.memberId}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Policy Number
</Typography>
</Grid>
<Grid item xs={7}>
<Typography fontWeight="bold">{data.policyNumber}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Name
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.name}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Submission Date
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.submissionDate}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Claim Method
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.claimMethod}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Service Type
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.serviceType}</Typography>
</Grid>
</Grid>
</Paper>
{shareLink ? (
<>
<Typography>Share this link only with authorized parties!</Typography>
{/* <Stack direction="row" spacing={2}>
<IconButton color="success">
<WhatsApp />
</IconButton>
<IconButton color="primary">
<Instagram />
</IconButton>
<IconButton color="primary">
<Telegram />
</IconButton>
<IconButton color="primary">
<Facebook />
</IconButton>
</Stack> */}
<Typography variant="body2">or copy link</Typography>
<Stack direction="row" spacing={1}>
<TextField
fullWidth
size="small"
value={data.linkApproval}
InputProps={{
readOnly: true,
}}
/>
<Button
variant="outlined"
onClick={() => navigator.clipboard.writeText(data.linkApproval)}
>
Copy
</Button>
</Stack>
</>
): null }
</Stack>
);
const getAction = () => {
if (shareLink) {
return (
<Stack direction="row" justifyContent="flex-end">
<Button variant="outlined" onClick={() => setOpenDialog(false)}>
Cancel
</Button>
</Stack>
);
}
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 (
<Stack direction="row" justifyContent="space-between" spacing={2}>
<Button variant="outlined" onClick={() => setOpenDialog(false)}>
Cancel
</Button>
<Button variant="contained" onClick={handleSend}>
Send
</Button>
</Stack>
);
};
return (
<MuiDialog
title={{ name: "Confirmation", variant: "h4" }}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
action={getAction()}
maxWidth="sm"
/>
);
}

View File

@@ -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,15 +15,22 @@ 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';
import axios from '../../../utils/axios';
import { enqueueSnackbar } from 'notistack';
// pages
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import { DetailFinalLogType } from './Model/Types';
@@ -33,6 +43,8 @@ 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 { makeFormData } from '@/utils/jsonToFormData';
// Import Card Detail Final LOG
import CardDetail from '../Components/CardDetail';
@@ -57,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';
// ----------------------------------------------------------------------
@@ -69,9 +83,78 @@ export default function Detail() {
const { themeStretch } = useSettings();
const [requestLog, setRequestLog] = useState<DetailFinalLogType>();
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<any>({
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 { id } = useParams();
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
@@ -107,6 +190,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<number>();
@@ -146,9 +231,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<HTMLInputElement>(null);
const [fileApprovals, setFileApproval] = useState<any>([]);
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;
})
);
};
return (
<Page title='Detail'>
@@ -323,6 +426,223 @@ export default function Detail() {
</DialogHospitalCare>
</Grid> */}
{/* Surat persetujuan Tindakan */}
<Grid item xs={12}>
<Card sx={{ p: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="flex-start" sx={{ mb: 3 }}>
<Typography variant="subtitle1" sx={{ color: '#19BBBB', fontWeight: 'bold' }}>
Tindakan Persetujuan
</Typography>
</Stack>
{!isReversal && (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={2}>
<Stack spacing={2} sx={{ mb: 3 }}>
<Typography variant="body1" fontWeight="bold">
Upload Tindakan Persetujuan
</Typography>
{fileApprovals?.map((file: any, index: number) => (
<Stack
key={index}
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Typography variant="body2" color="text.secondary">
{file.name}
</Typography>
<Iconify
icon="eva:trash-2-outline"
color="darkred"
sx={{ cursor: "pointer" }}
onClick={() => removeApprovalFiles(fileApprovals, index)}
/>
</Stack>
))}
<ButtonBase
sx={{
p: 2,
border: "2px dashed #F9FAFB",
bgcolor: "#F4F6F8",
borderRadius: 2,
width: "100%",
height: 60,
}}
onClick={() => fileDiagnosaInput.current?.click()}
>
<Box
display="flex"
alignItems="center"
justifyContent="center"
gap={1}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="2rem" />
<Typography variant="body2" fontWeight="bold">
Upload Tindakan Persetujuan
</Typography>
</Box>
<input
type="file"
ref={fileDiagnosaInput}
style={{ display: "none" }}
multiple
onChange={handleDiagnosaInputChange}
accept="application/pdf,image/*"
/>
</ButtonBase>
</Stack>
<RHFTextFieldMoney
id="nominal"
name="nominal"
label="Nominal"
required
placeholder="Nominal"
value={requestLog?.nominal || 0}
disabled={!!approval}
/>
{/* <LoadingButton
type="submit" // ✅ supaya ikut submit
variant="contained"
sx={{ marginTop: 2, p: 2, backgroundColor: "#19BBBB" }}
loading={false}
>
Simpan
</LoadingButton> */}
<Stack direction="row" spacing={2} sx={{ mt: 6 }}>
{approval ? (
<>
<Box sx={{ flexGrow: 1 }} />
{/* GRUP TOMBOL DI KANAN */}
{requestLog?.status_approval !== 'approved' && (
<Stack direction="row" spacing={1.5} mt={2}>
<Button
color="error"
variant="outlined"
size="small"
onClick={updateDecline}
>
Decline
</Button>
<Button
variant="contained"
size="small"
color="primary"
onClick={updateApproval}
>
Approve
</Button>
</Stack>
)}
</>
) : (
<>
{/* TOMBOL SIMPAN DI KIRI */}
<LoadingButton
type="submit"
variant="contained"
sx={{ p: 2, backgroundColor: "#19BBBB" }}
loading={false}
size="small"
>
Simpan
</LoadingButton>
{/* Ini adalah spacer untuk mendorong tombol berikutnya ke kanan */}
<Box sx={{ flexGrow: 1 }} />
{/* GRUP TOMBOL DI KANAN */}
<Stack direction="row" spacing={1.5} mt={2}>
<Button
variant="contained"
size="small"
sx={{ p: 2, backgroundColor: "#19BBBB" }}
onClick={() => {
setDialogSendWa(true);
setShareLink(false);
}}
>
Kirim (WA Chatbot)
</Button>
<Button
variant="contained"
size="small"
sx={{ p: 2, backgroundColor: "#19BBBB" }}
onClick={() => {
setDialogSendWa(true);
setShareLink(true);
}}
>
Share Link
</Button>
</Stack>
</>
)}
</Stack>
</Stack>
</FormProvider>
)}
{/* FILE YANG SUDAH TERUPLOAD */}
{requestLog?.files
?.filter((document) => document.type === 'approval')
?.map((documentType, index) => (
<Stack
key={index}
direction="row"
justifyContent="space-between"
alignItems="center"
sx={{ mt: 2 }}
>
<a
href={documentType.url}
target="_blank"
rel="noopener noreferrer"
style={{ textDecoration: 'none', color: '#19BBBB' }}
>
<Typography variant="body2">
{documentType.original_name || '-'}
</Typography>
</a>
{!isReversal && (
<IconButton
onClick={() => {
setDialogDeleteFileLog(true);
setPathFile(documentType.path);
}}
size="small"
>
<Delete color="error" fontSize="small" />
</IconButton>
)}
</Stack>
))}
{/* DIALOG */}
<DialogDeleteFileLog
id={requestLog?.id}
path={pathFile}
setOpenDialog={setDialogDeleteFileLog}
openDialog={dialogDeleteFIleLog}
/>
<DialogUploadFileFinalLog
id={requestLog?.id}
setOpenDialog={setDialogUploadFileLog}
openDialog={dialogUploadFileLog}
/>
</Card>
</Grid>
{/* Benefit */}
<Grid item xs={12} md={12}>
<Card sx={{padding:2}} >
@@ -597,6 +917,16 @@ export default function Detail() {
requestLog={requestLog}
openDialog={openDialogEditDetail}
/>
<DialogSendWa
requestLog={requestLog}
openDialog={openDialogSendWa}
setOpenDialog={setDialogSendWa}
shareLink={shareLink}
/>
</Grid>
{/* Medicine */}
@@ -662,7 +992,9 @@ export default function Detail() {
) : null }
</Stack>
{requestLog?.files?.map((documentType, index) => (
{requestLog?.files
?.filter((document) => document.type !== 'approval')
?.map((documentType, index) => (
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{marginBottom: 2}} key={index}>
<Stack direction="column" spacing={2} >
<a
@@ -711,8 +1043,7 @@ export default function Detail() {
variant="outlined"
sx={{ color: '#FF4842', borderColor: '#FF4842' }}
onClick={() => {
setOpenDialogSubmit(true);
setApprove('declined');
}}
>
Decline

View File

@@ -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,
@@ -48,6 +50,7 @@ export type DetailFinalLogType = {
claim_method : string,
status : string,
status_final_log : string,
status_approval : string,
no_identitas : string,
keterangan : string,
hak_kamar_pasien : string,
@@ -65,6 +68,7 @@ export type DetailFinalLogType = {
files : file[],
member_usage_benefit : number,
corporate_id : number
nominal : number
}
export type Diagnosis = {

View File

@@ -269,6 +269,10 @@ export default function Router() {
path: 'inpatient_monitoring', // Inpatient Monitoring
element: <InpatientMonitoring />
},
{
path: 'approval_inpatient_monitoring', // Approval Monitoring
element: <ApprovalMonitoring />
},
]
},
{
@@ -555,6 +559,10 @@ export default function Router() {
{
path: 'custormer-service/final-log/detail/:id',
element: <FinalLogDetail />,
},
{
path: 'custormer-service/final-log/detail/:id/:approval',
element: <FinalLogDetail />,
},
{
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')))
/**