From 5dc04d01e24ab2f9c08c8cc1a856b510541cc41b Mon Sep 17 00:00:00 2001 From: Iqbal Date: Mon, 11 May 2026 09:10:13 +0700 Subject: [PATCH 1/4] Add Primayan Medicare report with LOG codes and aggregated amounts --- .../Api/PrimayanMedicareController.php | 465 ++++++++++++++++++ Modules/Internal/Routes/api.php | 5 + 2 files changed, 470 insertions(+) create mode 100644 Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php diff --git a/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php b/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php new file mode 100644 index 00000000..21be051b --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php @@ -0,0 +1,465 @@ +has('per_page') ? $request->input('per_page') : 10; + + $corporate = Corporate::where('code', 'PRAY')->first(); + + if (!$corporate) { + return response()->json([ + 'message' => 'Corporate Primayan Medicare (PRAY) not found', + 'data' => [] + ], 404); + } + + // Subquery to aggregate amounts from request_log_benefits + $subQueryAmount = DB::table('request_log_benefits') + ->select('request_log_id', + DB::raw('SUM(amount_incurred) as total_incurred'), + DB::raw('SUM(amount_approved) as total_approved'), + DB::raw('SUM(excess_paid) as total_excess')) + ->whereNull('deleted_at') + ->groupBy('request_log_id'); + + $query = DB::table('claim_requests') + ->join('members', 'claim_requests.member_id', '=', 'members.id') + ->join('corporate_employees', 'corporate_employees.member_id', '=', 'members.id') + ->leftJoin('organizations', 'claim_requests.organization_id', '=', 'organizations.id') + ->leftJoin('claims', 'claim_requests.claim_id', '=', 'claims.id') + ->leftJoin('request_logs', 'claim_requests.request_log_id', '=', 'request_logs.id') + ->leftJoinSub($subQueryAmount, 'log_amounts', function ($join) { + $join->on('request_logs.id', '=', 'log_amounts.request_log_id'); + }) + ->leftJoin('plans', 'claims.plan_id', '=', 'plans.id') + ->leftJoin('benefits', 'claims.benefit_id', '=', 'benefits.id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->whereNull('corporate_employees.deleted_at') + ->whereNull('claim_requests.deleted_at') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('members.name', 'like', "%" . $search . "%") + ->orWhere('members.member_id', 'like', "%" . $search . "%") + ->orWhere('claim_requests.code', 'like', "%" . $search . "%") + ->orWhere('corporate_employees.nik', 'like', "%" . $search . "%"); + }); + }) + ->when($request->input('start_date'), function ($query, $start_date) { + $query->where('claim_requests.created_at', '>=', $start_date); + }) + ->when($request->input('end_date'), function ($query, $end_date) { + $query->where('claim_requests.created_at', '<=', $end_date . ' 23:59:59'); + }) + ->when($request->input('status'), function ($query, $status) { + if ($status !== 'all') { + $query->where('claim_requests.status', $status); + } + }) + ->when($request->input('provider_id'), function ($query, $provider_id) { + if ($provider_id !== 'all') { + $query->where('claim_requests.organization_id', $provider_id); + } + }) + ->select( + 'claim_requests.id', + 'claim_requests.code as request_code', + 'claim_requests.status', + 'claim_requests.created_at as request_date', + 'claim_requests.service_code', + 'claims.code as claim_code', + 'request_logs.code as log_code', + DB::raw('COALESCE(log_amounts.total_incurred, claims.amount_incurred, 0) as amount_incurred'), + DB::raw('COALESCE(log_amounts.total_approved, claims.amount_approved, 0) as amount_approved'), + DB::raw('COALESCE(claims.amount_not_approved, 0) as amount_not_approved'), + DB::raw('COALESCE(log_amounts.total_excess, claims.excess_paid, 0) as excess_paid'), + 'claims.benefit_code', + 'claims.benefit_desc', + 'members.name as member_name', + 'members.member_id as member_number', + 'members.birth_date', + 'members.gender', + 'corporate_employees.nik', + 'corporate_employees.branch_code', + 'organizations.name as provider_name', + 'plans.code as plan_code', + 'benefits.code as benefit_code_ref' + ) + ->orderBy('claim_requests.created_at', 'desc'); + + $results = $query->paginate($limit)->appends($request->all()); + + // Get Providers for filter + $providers = DB::table('claim_requests') + ->join('members', 'claim_requests.member_id', '=', 'members.id') + ->join('corporate_employees', 'corporate_employees.member_id', '=', 'members.id') + ->join('organizations', 'claim_requests.organization_id', '=', 'organizations.id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->whereNull('claim_requests.deleted_at') + ->select('organizations.id', 'organizations.name') + ->groupBy('organizations.id', 'organizations.name') + ->get(); + + return Helper::responseJson([ + 'results' => Helper::paginateResources($results), + 'providers' => $providers, + ]); + } + + /** + * Export transactions to Excel + */ + public function export(Request $request) + { + $corporate = Corporate::where('code', 'PRAY')->first(); + + if (!$corporate) { + return response()->json([ + 'message' => 'Corporate Primayan Medicare (PRAY) not found', + ], 404); + } + + $exportType = $request->input('export_type', 'list'); + + if ($exportType === 'summary') { + return $this->exportSummary($corporate); + } + + // Subquery to aggregate amounts from request_log_benefits + $subQueryAmount = DB::table('request_log_benefits') + ->select('request_log_id', + DB::raw('SUM(amount_incurred) as total_incurred'), + DB::raw('SUM(amount_approved) as total_approved'), + DB::raw('SUM(excess_paid) as total_excess')) + ->whereNull('deleted_at') + ->groupBy('request_log_id'); + + $results = DB::table('claim_requests') + ->join('members', 'claim_requests.member_id', '=', 'members.id') + ->join('corporate_employees', 'corporate_employees.member_id', '=', 'members.id') + ->leftJoin('organizations', 'claim_requests.organization_id', '=', 'organizations.id') + ->leftJoin('claims', 'claim_requests.claim_id', '=', 'claims.id') + ->leftJoin('request_logs', 'claim_requests.request_log_id', '=', 'request_logs.id') + ->leftJoinSub($subQueryAmount, 'log_amounts', function ($join) { + $join->on('request_logs.id', '=', 'log_amounts.request_log_id'); + }) + ->leftJoin('plans', 'claims.plan_id', '=', 'plans.id') + ->leftJoin('benefits', 'claims.benefit_id', '=', 'benefits.id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->whereNull('corporate_employees.deleted_at') + ->whereNull('claim_requests.deleted_at') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('members.name', 'like', "%" . $search . "%") + ->orWhere('members.member_id', 'like', "%" . $search . "%") + ->orWhere('claim_requests.code', 'like', "%" . $search . "%") + ->orWhere('corporate_employees.nik', 'like', "%" . $search . "%"); + }); + }) + ->when($request->input('start_date'), function ($query, $start_date) { + $query->where('claim_requests.created_at', '>=', $start_date); + }) + ->when($request->input('end_date'), function ($query, $end_date) { + $query->where('claim_requests.created_at', '<=', $end_date . ' 23:59:59'); + }) + ->when($request->input('status'), function ($query, $status) { + if ($status !== 'all') { + $query->where('claim_requests.status', $status); + } + }) + ->when($request->input('provider_id'), function ($query, $provider_id) { + if ($provider_id !== 'all') { + $query->where('claim_requests.organization_id', $provider_id); + } + }) + ->select( + 'claim_requests.id', + 'claim_requests.code as request_code', + 'claim_requests.status', + 'claim_requests.created_at as request_date', + 'claim_requests.service_code', + 'claims.code as claim_code', + 'request_logs.code as log_code', + DB::raw('COALESCE(log_amounts.total_incurred, claims.amount_incurred, 0) as amount_incurred'), + DB::raw('COALESCE(log_amounts.total_approved, claims.amount_approved, 0) as amount_approved'), + DB::raw('COALESCE(claims.amount_not_approved, 0) as amount_not_approved'), + DB::raw('COALESCE(log_amounts.total_excess, claims.excess_paid, 0) as excess_paid'), + 'claims.benefit_code', + 'claims.benefit_desc', + 'members.name as member_name', + 'members.member_id as member_number', + 'members.birth_date', + 'members.gender', + 'corporate_employees.nik', + 'corporate_employees.branch_code', + 'organizations.name as provider_name', + 'plans.code as plan_code', + 'benefits.code as benefit_code_ref' + ) + ->orderBy('claim_requests.created_at', 'desc') + ->get(); + + $fileName = 'PrimayanMedicare-List-' . now()->format('Y-m-d_H-i-s') . '.xlsx'; + $filePath = storage_path('app/public/temp/' . $fileName); + + if (!is_dir(storage_path('app/public/temp'))) { + mkdir(storage_path('app/public/temp'), 0755, true); + } + + $writer = WriterEntityFactory::createXLSXWriter(); + $writer->openToFile($filePath); + + $serviceCodeMap = [ + 'OP' => 'Outpatient', + 'IP' => 'Inpatient', + 'DE' => 'Dental', + 'MA' => 'Maternal', + 'GL' => 'Optical', + 'MCU' => 'Medical Check Up', + 'MEDIVAC' => 'Medical Emergency Evacuation', + ]; + + $headers = [ + 'No', + 'Code Claim / Code LOG', + 'Member Name', + 'Member Number', + 'NIK', + 'Birth Date', + 'Gender', + 'Plan', + 'Benefit', + 'Service', + 'Provider', + 'Request Date', + 'Amount Incurred', + 'Amount Approved', + 'Amount Not Approved', + 'Excess Paid', + 'Status', + ]; + $headerRow = WriterEntityFactory::createRowFromArray($headers); + $writer->addRow($headerRow); + + $no = 1; + foreach ($results as $row) { + $serviceCode = $row->service_code ?? '-'; + $serviceName = $serviceCodeMap[$serviceCode] ?? $serviceCode; + + $rowData = [ + $no++, + ($row->claim_code && $row->log_code) + ? $row->claim_code . ' / ' . $row->log_code + : ($row->claim_code ?? $row->log_code ?? $row->request_code ?? '-'), + $row->member_name ?? '-', + $row->member_number ?? '-', + $row->nik ?? '-', + $row->birth_date ?? '-', + $row->gender ?? '-', + $row->plan_code ?? '-', + $row->benefit_name ?? $row->benefit_desc ?? '-', + $serviceName, + $row->provider_name ?? '-', + $row->request_date ?? '-', + $row->amount_incurred ?? 0, + $row->amount_approved ?? 0, + $row->amount_not_approved ?? 0, + $row->excess_paid ?? 0, + $row->status ?? '-', + ]; + $dataRow = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($dataRow); + } + + $writer->close(); + + return Helper::responseJson([ + 'file_name' => 'Primayan Medicare List ' . now()->format('Y-m-d H:i:s'), + 'file_url' => Storage::disk('public')->url('temp/' . $fileName), + ]); + } + + private function exportSummary($corporate) + { + // Get all branch codes + $branches = DB::table('corporate_employees') + ->where('corporate_id', $corporate->id) + ->whereNull('deleted_at') + ->select('branch_code') + ->groupBy('branch_code') + ->get(); + + $fileName = 'PrimayanMedicare-Summary-' . now()->format('Y-m-d_H-i-s') . '.xlsx'; + $filePath = storage_path('app/public/temp/' . $fileName); + + if (!is_dir(storage_path('app/public/temp'))) { + mkdir(storage_path('app/public/temp'), 0755, true); + } + + $codeRSMap = [ + 'AI' => 'ABI', 'BB' => 'PHBB', 'BT' => 'PHBT', 'BU' => 'PHBU', + 'BW' => 'PHBW', 'CI' => 'PHPC', 'CO' => 'FABS', 'DE' => 'PHDE', + 'EV' => 'PHEV', 'HE' => 'PHHE', 'KG' => 'PHKG', 'KI' => 'KMI', + 'KR' => 'PHKA', 'KV' => 'KAVA', 'LM' => 'LMS', 'LY' => 'LYNAS', + 'MK' => 'PHMA', 'MS' => 'EYEQU', 'PF' => 'FMC', 'PS' => 'PHPK', + 'PY' => 'PHBP', 'RA' => 'PHRA', 'SB' => 'PHSB', 'SF' => 'SFI', + 'SG' => 'PHSM', 'SM' => 'SIM', 'SO' => 'PHSW', 'SS' => 'SAS', + 'TG' => 'PHTA', 'UP' => 'UKRIDA', 'WS' => 'WEST' + ]; + + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + + // Header Date Range (Row 1) + $dateRange = now()->startOfMonth()->format('j F') . ' - ' . now()->format('j F Y'); + $sheet->setCellValue('A1', strtoupper($dateRange)); + $sheet->mergeCells('A1:G1'); + $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + + // Table Headers (Row 3) + $headers = ['Code RS', 'Branch Code', 'Total Benefit', 'Deposit 10%', 'Pemakaian Benefit Obat & Rujukan', 'Pemakaian Telekonsultasi', 'Sisa Deposit']; + $sheet->fromArray($headers, null, 'A3'); + $sheet->getStyle('A3:G3')->applyFromArray([ + 'font' => [ + 'bold' => true, + ], + 'fill' => [ + 'fillType' => Fill::FILL_SOLID, + 'startColor' => [ + 'rgb' => 'C6E0B4' + ] + ], + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + ] + ] + ]); + + $row = 4; + foreach ($branches as $branch) { + $branchCode = $branch->branch_code ?? '-'; + $codeRS = $codeRSMap[$branchCode] ?? '-'; + + $totalDeposit = DB::table('corporate_employees') + ->join('member_plans', 'corporate_employees.member_id', '=', 'member_plans.member_id') + ->join('plans', 'member_plans.plan_id', '=', 'plans.id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->where(function($query) use ($branchCode) { + if ($branchCode === '-') { + $query->whereNull('corporate_employees.branch_code')->orWhere('corporate_employees.branch_code', ''); + } else { + $query->where('corporate_employees.branch_code', $branchCode); + } + }) + ->whereNull('corporate_employees.deleted_at') + ->whereNull('member_plans.deleted_at') + ->sum(DB::raw('CAST(plans.limit_rules as UNSIGNED)')); + + $deposit10 = $totalDeposit * 0.1; + + $usageMedicines = DB::table('request_log_medicines') + ->join('request_logs', 'request_log_medicines.request_log_id', '=', 'request_logs.id') + ->join('corporate_employees', 'request_logs.member_id', '=', 'corporate_employees.member_id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->where(function($query) use ($branchCode) { + if ($branchCode === '-') { + $query->whereNull('corporate_employees.branch_code')->orWhere('corporate_employees.branch_code', ''); + } else { + $query->where('corporate_employees.branch_code', $branchCode); + } + }) + ->whereIn('request_logs.log_type', ['prescription', 'reference']) + ->sum('request_log_medicines.price'); + + $consultationCount = DB::table('request_logs') + ->join('corporate_employees', 'request_logs.member_id', '=', 'corporate_employees.member_id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->where(function($query) use ($branchCode) { + if ($branchCode === '-') { + $query->whereNull('corporate_employees.branch_code') + ->orWhere('corporate_employees.branch_code', ''); + } else { + $query->where('corporate_employees.branch_code', $branchCode); + } + }) + ->where('request_logs.log_type', 'consultation') + ->count(); + + $usageConsultation = $consultationCount * 11100; + $remainingDeposit = $deposit10 - $usageMedicines - $usageConsultation; + + $sheet->setCellValue('A' . $row, $codeRS); + $sheet->setCellValue('B' . $row, $branchCode); + $sheet->setCellValue('C' . $row, $totalDeposit); + $sheet->setCellValue('D' . $row, $deposit10); + $sheet->setCellValue('E' . $row, $usageMedicines); + $sheet->setCellValue('F' . $row, $usageConsultation); + $sheet->setCellValue('G' . $row, $remainingDeposit); + + // Number format with thousand separator + $sheet->getStyle('C' . $row . ':G' . $row)->getNumberFormat()->setFormatCode('#,##0'); + + $row++; + } + + $lastRow = $row - 1; + foreach (range('A', 'G') as $col) { + $sheet->getColumnDimension($col)->setAutoSize(true); + } + + $sheet->getStyle('A3:G' . $lastRow)->applyFromArray([ + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + 'color' => ['rgb' => '000000'] + ] + ] + ]); + + $sheet->freezePane('A4'); + + $sheet->setCellValue('E1', '=SUM(E4:E' . $lastRow . ')'); + $sheet->setCellValue('F1', '=SUM(F4:F' . $lastRow . ')'); + $sheet->getStyle('E1:F1') + ->getNumberFormat() + ->setFormatCode('#,##0'); + $sheet->getStyle('E1:F1')->applyFromArray([ + 'font' => [ + 'bold' => true, + 'size' => 12, + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + ] + ]); + + $writer = new Xlsx($spreadsheet); + $writer->save($filePath); + + return Helper::responseJson([ + 'file_name' => $fileName, + 'file_url' => Storage::disk('public')->url('temp/' . $fileName), + ]); + } +} diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 48d8f406..509c540e 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -62,6 +62,7 @@ use Modules\Primaya\Http\Controllers\Api\MasterController; // Report use Modules\Internal\Http\Controllers\Api\ReportLogController; +use Modules\Internal\Http\Controllers\Api\PrimayanMedicareController; /* @@ -112,6 +113,10 @@ Route::prefix('internal')->group(function () { Route::get('linksehat/rujukan', [RujukanController::class, 'index']); Route::get('linksehat/rujukan/generate-excel', [RujukanController::class, 'generateExcel']); + // Report Primayan Medicare + Route::get('primayan-medicare', [PrimayanMedicareController::class, 'index']); + Route::get('primayan-medicare/export', [PrimayanMedicareController::class, 'export']); + Route::post('logout', [AuthController::class, 'logout'])->name('logout'); Route::get('/user', function (Request $request) { return $request->user(); From 21a909aacb009ae437ae93bee6409517e4d4e3f9 Mon Sep 17 00:00:00 2001 From: Iqbal Date: Mon, 11 May 2026 09:10:33 +0700 Subject: [PATCH 2/4] Add Primayan Medicare report frontend and navigation --- database/seeders/NavigationSeeder.php | 151 +++++-- database/seeders/PermissionTableSeeder.php | 1 + .../layouts/dashboard/navbar/NavConfig.tsx | 1 + .../pages/Report/PrimayanMedicare/Index.tsx | 32 ++ .../pages/Report/PrimayanMedicare/List.tsx | 402 ++++++++++++++++++ frontend/dashboard/src/routes/index.tsx | 6 + 6 files changed, 547 insertions(+), 46 deletions(-) create mode 100644 frontend/dashboard/src/pages/Report/PrimayanMedicare/Index.tsx create mode 100644 frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx diff --git a/database/seeders/NavigationSeeder.php b/database/seeders/NavigationSeeder.php index 55cb5d42..6f7e7b42 100644 --- a/database/seeders/NavigationSeeder.php +++ b/database/seeders/NavigationSeeder.php @@ -16,9 +16,10 @@ class NavigationSeeder extends Seeder public function run() { $menuItems = [ - // DOCTORS & HOSPITALS + // Dashboard [ 'title' => 'Dashboard', + 'urutan' => 1, 'children' => [ [ 'title' => 'Dashboard', @@ -31,6 +32,7 @@ class NavigationSeeder extends Seeder // DOCTORS & HOSPITALS [ 'title' => 'DOCTORS & HOSPITALS', + 'urutan' => 2, 'children' => [ [ 'title' => 'Doctors', @@ -48,6 +50,7 @@ class NavigationSeeder extends Seeder // PHARMACY & DELIVERY MANAGEMENT [ 'title' => 'PHARMACY & DELIVERY MANAGEMENT', + 'urutan' => 3, 'children' => [ [ 'title' => 'Drug', @@ -70,6 +73,7 @@ class NavigationSeeder extends Seeder // STATION BENEFIT & MEMBERSHIP [ 'title' => 'STATION BENEFIT & MEMBERSHIP', + 'urutan' => 4, 'openWhen' => ['/corporates', '/formularium', '/diagnosis', '/hospitals'], 'children' => [ [ @@ -77,7 +81,6 @@ class NavigationSeeder extends Seeder 'path' => '/corporates', 'permission' => 'corporate-list', ], - // ['title' => 'Corporate Create', 'path' => '/corporates/create'], [ 'title' => 'Formularium', 'path' => '/master/formularium-template-v2', @@ -96,28 +99,33 @@ class NavigationSeeder extends Seeder ], 'permission' => null ], - // CLAIM REQUEST + // MASTER [ - 'title' => 'CLAIM REQUEST', - 'path' => '/claim-requests', + 'title' => 'MASTER', + 'urutan' => 5, 'children' => [ [ - 'title' => 'CLAIM REQUEST', - 'path' => '/claim-requests', - 'permission' => 'claim-request-list' + 'title' => 'Diagnosis', + 'path' => '/master/diagnosis', + 'permission' => 'diagnosis-list' ], ], 'permission' => null ], - // CLAIM MANAGEMENT + // CUSTOMER SERVICES [ - 'title' => 'CLAIM MANAGEMENT', - 'path' => '/claims', + 'title' => 'CUSTOMER SERVICES', + 'urutan' => 6, 'children' => [ [ - 'title' => 'CLAIM MANAGEMENT', - 'path' => '/claims', - 'permission' => 'claim-management-list' + 'title' => 'Request', + 'path' => '/custormer-service/request', + 'permission' => 'request-log-list' + ], + [ + 'title' => 'Final LOG', + 'path' => '/custormer-service/final-log', + 'permission' => 'final-log-list' ], ], 'permission' => null @@ -125,13 +133,13 @@ class NavigationSeeder extends Seeder // CASE MANAGEMENT [ 'title' => 'CASE MANAGEMENT', + 'urutan' => 7, 'children' => [ [ 'title' => 'Daily Monitoring', 'path' => '/case_management/daily_monitoring', 'permission' => 'daily-monitoring-list' ], - // ['title' => 'Laboratorium Result', 'path' => '/case_management/laboratorium_result'], [ 'title' => 'Inpatient Monitoring', 'path' => '/case_management/inpatient_monitoring', @@ -145,9 +153,56 @@ class NavigationSeeder extends Seeder ], 'permission' => null ], - // Invoice + // CLAIM REQUEST + [ + 'title' => 'CLAIM REQUEST', + 'urutan' => 8, + 'path' => '/claim-requests', + 'children' => [ + [ + 'title' => 'CLAIM REQUEST', + 'path' => '/claim-requests', + 'permission' => 'claim-request-list' + ], + ], + 'permission' => null + ], + // CLAIM MANAGEMENT + [ + 'title' => 'CLAIM MANAGEMENT', + 'urutan' => 9, + 'path' => '/claims', + 'children' => [ + [ + 'title' => 'CLAIM MANAGEMENT', + 'path' => '/claims', + 'permission' => 'claim-management-list' + ], + ], + 'permission' => null + ], + // USER MANAGEMENT + [ + 'title' => 'USER MANAGEMENT', + 'urutan' => 10, + 'children' => [ + [ + 'title' => 'User Role', + 'path' => '/user-role', + 'permission' => 'user-role-list' + ], + [ + 'title' => 'User Access', + 'path' => '/user-access', + 'permission' => 'user-access-list' + ], + ], + 'permission' => null + ], + // INVOICE [ 'title' => 'INVOICE', + 'urutan' => 11, 'children' => [ [ 'title' => 'Invoice', @@ -157,79 +212,78 @@ class NavigationSeeder extends Seeder ], 'permission' => null ], - // CUSTOMER SERVICES - [ - 'title' => 'CUSTOMER SERVICES', - 'children' => [ - [ - 'title' => 'Request', - 'path' => '/custormer-service/request', - 'permission' => 'request-log-list' - ], - // ['title' => 'Membership', 'path' => '/cs-membership'], - [ - 'title' => 'Final LOG', - 'path' => '/custormer-service/final-log', - 'permission' => 'final-log-list' - ], - ], - 'permission' => null - ], // REPORT [ 'title' => 'REPORT', + 'urutan' => 12, 'children' => [ [ 'title' => 'Files Provider', 'path' => 'report/files-provider', - 'permission' => 'report-files-provider-list' + 'permission' => 'report-files-provider-list', + 'urutan' => 1, ], [ 'title' => 'Letter of Guarantee', 'path' => '/report/logs', - 'permission' => 'report-log-list' + 'permission' => 'report-log-list', + 'urutan' => 2, ], [ 'title' => 'Appointment', 'path' => '/report/appointments', - 'permission' => 'report-appointment-list' + 'permission' => 'report-appointment-list', + 'urutan' => 3, ], [ 'title' => 'Live Chat', 'path' => '/report/live-chat', - 'permission' => 'report-livechat-list' + 'permission' => 'report-livechat-list', + 'urutan' => 4, ], [ 'title' => 'Linksehat Payment', 'path' => '/report/linksehat-payments', - 'permission' => 'report-livechat-payment' + 'permission' => 'report-livechat-payment', + 'urutan' => 5, ], [ 'title' => 'Prescription', 'path' => '/report/prescription', - 'permission' => 'report-prescription' + 'permission' => 'report-prescription', + 'urutan' => 6, ], [ 'title' => 'Doctor Rating', 'path' => '/report/doctor-rating', - 'permission' => 'report-doctor-rating' + 'permission' => 'report-doctor-rating', + 'urutan' => 7, ], [ 'title' => 'Doctor Online', 'path' => '/report/doctor-online', - 'permission' => 'report-doctor-online' + 'permission' => 'report-doctor-online', + 'urutan' => 8, ], [ 'title' => 'Katalog Dokter', 'path' => '/report/katalog-dokter', - 'permission' => 'report-katalog-dokter' - ] + 'permission' => 'report-katalog-dokter', + 'urutan' => 9, + ], + [ + 'title' => 'Primayan Medicare', + 'path' => '/report/primayan-medicare', + 'permission' => 'report-primayan-medicare', + 'urutan' => 10, + ], ], 'permission' => null ], // MASTER [ 'title' => 'MASTER', + 'urutan' => 11, 'children' => [ [ 'title' => 'Diagnosis', @@ -242,6 +296,7 @@ class NavigationSeeder extends Seeder // USER MANAGEMENT [ 'title' => 'USER MANAGEMENT', + 'urutan' => 9, 'children' => [ [ 'title' => 'User Role', @@ -259,12 +314,14 @@ class NavigationSeeder extends Seeder // LINKING TOOLS [ 'title' => 'LINKING TOOLS', + 'urutan' => 13, 'path' => '/linking', 'permission' => 'linkking-list' ], // E-PRESCRIPTION [ 'title' => 'E-PRESCRIPTION', + 'urutan' => 14, 'path' => '/e-prescription/live-chat', 'permission' => 'prescription-list' ], @@ -370,7 +427,8 @@ class NavigationSeeder extends Seeder 'title' => $menuItemData['title'], 'path' => $menuItemData['path'] ?? null, 'icon' => $menuItemData['icon'] ?? null, - 'permission' => $menuItemData['permission'] ?? null + 'permission' => $menuItemData['permission'] ?? null, + 'urutan' => $menuItemData['urutan'] ?? null ]); if (isset($menuItemData['children'])) { @@ -384,7 +442,8 @@ class NavigationSeeder extends Seeder 'path' => $childData['path'] ?? null, 'icon' => $childData['icon'] ?? null, 'parent_id' => $menuItem->id, - 'permission' => $childData['permission'] ?? null + 'permission' => $childData['permission'] ?? null, + 'urutan' => $childData['urutan'] ?? null ]); } } diff --git a/database/seeders/PermissionTableSeeder.php b/database/seeders/PermissionTableSeeder.php index 75da78fd..4f317935 100644 --- a/database/seeders/PermissionTableSeeder.php +++ b/database/seeders/PermissionTableSeeder.php @@ -70,6 +70,7 @@ class PermissionTableSeeder extends Seeder 'report-livechat-list', 'report-livechat-payment', 'report-doctor-rating', + 'report-primayan-medicare', 'report-doctor-online', 'report-prescription', 'user-role-list', diff --git a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx index 5add5536..1dae1e2f 100644 --- a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx @@ -106,6 +106,7 @@ const navConfig = [ { title: 'Linksehat Payment', path: '/report/linksehat-payments' }, // { title: 'Prescription', path: '/report/prescription' }, { title: 'Doctor Rating', path: '/report/doctor-rating' }, + { title: 'Primayan Medicare', path: '/report/primayan-medicare' }, ], }, diff --git a/frontend/dashboard/src/pages/Report/PrimayanMedicare/Index.tsx b/frontend/dashboard/src/pages/Report/PrimayanMedicare/Index.tsx new file mode 100644 index 00000000..c888da30 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/PrimayanMedicare/Index.tsx @@ -0,0 +1,32 @@ +import { Container } from '@mui/material'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import Page from '../../../components/Page'; +import useSettings from '../../../hooks/useSettings'; +import List from './List'; + +export default function PrimayanMedicare() { + const { themeStretch } = useSettings(); + + const pageTitle = 'Primayan Medicare'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx b/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx new file mode 100644 index 00000000..de95bd07 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx @@ -0,0 +1,402 @@ +import { + Card, + Grid, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableRow, + TextField, + Stack, + Chip, + FormControl, + InputLabel, + Select, + MenuItem, + Menu, +} from '@mui/material'; + +import { useSearchParams } from 'react-router-dom'; +import React, { ChangeEvent, useEffect, useRef, useState } from 'react'; +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import BasePagination from '../../../components/BasePagination'; +import { fNumber } from '@/utils/formatNumber'; +import { fDateOnly } from '@/utils/formatTime'; +import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import { LoadingButton } from '@mui/lab'; +import UploadIcon from '@mui/icons-material/Upload'; + +// ---------------------------------------------------------------------- + +const statusColors: Record = { + draft: 'default', + requested: 'info', + received: 'primary', + approved: 'success', + postpone: 'warning', + paid: 'success', + declined: 'error', +}; + +export default function List() { + const [searchParams, setSearchParams] = useSearchParams(); + const [providers, setProviders] = useState([]); + + function Filter() { + const searchInput = useRef(null); + const [searchText, setSearchText] = useState(''); + const [exportLoading, setExportLoading] = useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const exportMenuOpen = Boolean(anchorEl); + + const handleExportClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleExportClose = () => { + setAnchorEl(null); + }; + + const handleExport = (exportType: string) => { + const parameters = Object.fromEntries([...searchParams.entries()]); + setExportLoading(true); + axios + .get('/primayan-medicare/export', { + params: { ...parameters, export_type: exportType }, + }) + .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(); + handleExportClose(); + setExportLoading(false); + }) + .catch(() => { + setExportLoading(false); + }); + }; + + const handleSearchChange = (event: any) => { + setSearchText(event.target.value ?? ''); + }; + + useEffect(() => { + setSearchText(searchParams.get('search') ?? ''); + }, []); + + return ( +
+ + + { + if (event.key === 'Enter') { + event.preventDefault(); + const filter = Object.fromEntries([ + ...searchParams.entries(), + ['search', searchText], + ['page', '1'], + ]); + setSearchParams(filter); + loadDataTableData(filter); + } + }} + label="Search" + value={searchText} + InputProps={{ + placeholder: 'Nama, Member ID', + }} + /> + + + + + Status + + + + + + + Provider + + + + + + + { + try { + if (value && !!Date.parse(value)) { + const date = fDateOnly(value); + let entries = [...searchParams.entries(), ['start_date', date ?? '']]; + if (!searchParams.get('end_date')) { + entries = [...entries, ['end_date', date ?? '']]; + } + const filter = Object.fromEntries([...entries, ['page', '1']]); + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => } + /> + + + + + + { + try { + if (value && !!Date.parse(value)) { + const date = fDateOnly(value); + let entries = [...searchParams.entries(), ['end_date', date ?? '']]; + if (!searchParams.get('start_date')) { + entries = [...entries, ['start_date', date ?? '']]; + } + const filter = Object.fromEntries([...entries, ['page', '1']]); + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => } + /> + + + + + } + sx={{ p: 1.8 }} + onClick={handleExportClick} + loading={exportLoading} + > + Export + + + handleExport('list')}>Download List Excel + handleExport('summary')}>Download Summary Excel + + + +
+ ); + } + + function FilterForm() { + return ( + + + + + + ); + } + + function Row(props: { row: any }) { + const { row } = props; + + const serviceCodeMap: Record = { + 'OP': 'Outpatient', + 'IP': 'Inpatient', + 'DE': 'Dental', + 'MA': 'Maternal', + 'GL': 'Optical', + 'MCU': 'Medical Check Up', + 'MEDIVAC': 'Medical Emergency Evacuation', + }; + + const serviceName = serviceCodeMap[row.service_code] || row.service_code || '-'; + + return ( + + + {(row.claim_code || row.request_code) && row.log_code + ? `${row.claim_code ?? row.request_code} / ${row.log_code}` + : row.claim_code ?? row.request_code ?? row.log_code ?? '-'} + + {row.member_name ?? '-'} + {row.member_number ?? '-'} + {serviceName} + {row.provider_name ?? '-'} + {row.request_date ?? '-'} + {fNumber(parseInt(row.amount_incurred ?? 0))} + {fNumber(parseInt(row.amount_approved ?? 0))} + {fNumber(parseInt(row.excess_paid ?? 0))} + + + + + ); + } + + const headStyle = { + fontWeight: 'bold', + }; + + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableData, setDataTableData] = useState({ + current_page: 1, + data: [], + path: '', + first_page_url: '', + last_page: 1, + last_page_url: '', + next_page_url: '', + prev_page_url: '', + per_page: 10, + from: 0, + to: 0, + total: 0, + }); + + const loadDataTableData = async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + try { + const response = await axios.get('/primayan-medicare', { + params: filter, + }); + setDataTableData(response.data.data.results); + setProviders(response.data.data.providers); + } catch (error) { + console.error('Failed to load Primayan Medicare data:', error); + } + setDataTableLoading(false); + }; + + const handlePageChange = (event: ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); + loadDataTableData(filter); + setSearchParams(filter); + }; + + useEffect(() => { + loadDataTableData(); + }, []); + + return ( + + + + + + + + + Code Claim / Code LOG + Nama Member + Member ID + Service + Provider + Tanggal Request + Amount Incurred + Amount Approved + Excess Paid + Status + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length === 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row, index) => ( + + ))} + + )} +
+
+ + +
+
+ ); +} diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index 9082b104..fa0b42e6 100644 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -471,6 +471,10 @@ export default function Router() { path: 'report/linksehat-payments', element: , }, + { + path: 'report/primayan-medicare', + element: , + }, { path: 'report/phr', @@ -770,6 +774,8 @@ const EPrescriptionShow = Loadable(lazy(() => import('../pages/EPrescription/Liv const LinksehatPayment = Loadable(lazy(() => import('../pages/Report/LinksehatPayments/Index'))); +const PrimayanMedicare = Loadable(lazy(() => import('../pages/Report/PrimayanMedicare/Index'))); + const RiwayatMedisPeserta = Loadable(lazy(() => import('../pages/Report/RiwayatMedisPeserta/Index'))); const RujukanPasien = Loadable(lazy(() => import('../pages/Report/Rujukan/Index'))); const Prescription = Loadable(lazy(() => import('../pages/Report/Prescription/Index'))); From d96e439b2a96291993e4df32d954984299fddf4f Mon Sep 17 00:00:00 2001 From: Iqbal Date: Mon, 11 May 2026 11:36:13 +0700 Subject: [PATCH 3/4] Update Primayan Medicare report search and layout --- .../Api/PrimayanMedicareController.php | 87 ++++++++++++------- .../pages/Report/PrimayanMedicare/List.tsx | 17 ++-- 2 files changed, 66 insertions(+), 38 deletions(-) diff --git a/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php b/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php index 21be051b..79895c7f 100644 --- a/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php +++ b/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php @@ -7,11 +7,9 @@ use App\Models\Corporate; use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\DB; -use Box\Spout\Writer\Common\Creator\WriterEntityFactory; use Illuminate\Support\Facades\Storage; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; -use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Fill; @@ -62,6 +60,8 @@ class PrimayanMedicareController extends Controller $query->orWhere('members.name', 'like', "%" . $search . "%") ->orWhere('members.member_id', 'like', "%" . $search . "%") ->orWhere('claim_requests.code', 'like', "%" . $search . "%") + ->orWhere('request_logs.code', 'like', "%" . $search . "%") + ->orWhere('claims.code', 'like', "%" . $search . "%") ->orWhere('corporate_employees.nik', 'like', "%" . $search . "%"); }); }) @@ -173,6 +173,8 @@ class PrimayanMedicareController extends Controller $query->orWhere('members.name', 'like', "%" . $search . "%") ->orWhere('members.member_id', 'like', "%" . $search . "%") ->orWhere('claim_requests.code', 'like', "%" . $search . "%") + ->orWhere('request_logs.code', 'like', "%" . $search . "%") + ->orWhere('claims.code', 'like', "%" . $search . "%") ->orWhere('corporate_employees.nik', 'like', "%" . $search . "%"); }); }) @@ -226,8 +228,8 @@ class PrimayanMedicareController extends Controller mkdir(storage_path('app/public/temp'), 0755, true); } - $writer = WriterEntityFactory::createXLSXWriter(); - $writer->openToFile($filePath); + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); $serviceCodeMap = [ 'OP' => 'Outpatient', @@ -258,40 +260,55 @@ class PrimayanMedicareController extends Controller 'Excess Paid', 'Status', ]; - $headerRow = WriterEntityFactory::createRowFromArray($headers); - $writer->addRow($headerRow); + $sheet->fromArray($headers, null, 'A1'); + $sheet->getStyle('A1:Q1')->applyFromArray([ + 'font' => [ + 'bold' => true, + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + ] + ]); + + $rowIndex = 2; $no = 1; - foreach ($results as $row) { - $serviceCode = $row->service_code ?? '-'; + foreach ($results as $item) { + $serviceCode = $item->service_code ?? '-'; $serviceName = $serviceCodeMap[$serviceCode] ?? $serviceCode; $rowData = [ $no++, - ($row->claim_code && $row->log_code) - ? $row->claim_code . ' / ' . $row->log_code - : ($row->claim_code ?? $row->log_code ?? $row->request_code ?? '-'), - $row->member_name ?? '-', - $row->member_number ?? '-', - $row->nik ?? '-', - $row->birth_date ?? '-', - $row->gender ?? '-', - $row->plan_code ?? '-', - $row->benefit_name ?? $row->benefit_desc ?? '-', + ($item->claim_code && $item->log_code) + ? $item->claim_code . ' / ' . $item->log_code + : ($item->claim_code ?? $item->log_code ?? $item->request_code ?? '-'), + $item->member_name ?? '-', + $item->member_number ?? '-', + $item->nik ?? '-', + $item->birth_date ?? '-', + $item->gender ?? '-', + $item->plan_code ?? '-', + $item->benefit_name ?? $item->benefit_desc ?? '-', $serviceName, - $row->provider_name ?? '-', - $row->request_date ?? '-', - $row->amount_incurred ?? 0, - $row->amount_approved ?? 0, - $row->amount_not_approved ?? 0, - $row->excess_paid ?? 0, - $row->status ?? '-', + $item->provider_name ?? '-', + $item->request_date ?? '-', + $item->amount_incurred ?? 0, + $item->amount_approved ?? 0, + $item->amount_not_approved ?? 0, + $item->excess_paid ?? 0, + $item->status ?? '-', ]; - $dataRow = WriterEntityFactory::createRowFromArray($rowData); - $writer->addRow($dataRow); + + $sheet->fromArray($rowData, null, 'A' . $rowIndex); + $rowIndex++; } - $writer->close(); + foreach (range('A', 'Q') as $col) { + $sheet->getColumnDimension($col)->setAutoSize(true); + } + + $writer = new Xlsx($spreadsheet); + $writer->save($filePath); return Helper::responseJson([ 'file_name' => 'Primayan Medicare List ' . now()->format('Y-m-d H:i:s'), @@ -333,8 +350,15 @@ class PrimayanMedicareController extends Controller // Header Date Range (Row 1) $dateRange = now()->startOfMonth()->format('j F') . ' - ' . now()->format('j F Y'); $sheet->setCellValue('A1', strtoupper($dateRange)); - $sheet->mergeCells('A1:G1'); - $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + $sheet->mergeCells('A1:B1'); + $sheet->getStyle('A1')->applyFromArray([ + 'font' => [ + 'bold' => true, + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + ] + ]); // Table Headers (Row 3) $headers = ['Code RS', 'Branch Code', 'Total Benefit', 'Deposit 10%', 'Pemakaian Benefit Obat & Rujukan', 'Pemakaian Telekonsultasi', 'Sisa Deposit']; @@ -353,6 +377,9 @@ class PrimayanMedicareController extends Controller 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, ] + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, ] ]); diff --git a/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx b/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx index de95bd07..586551b1 100644 --- a/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx +++ b/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx @@ -90,8 +90,8 @@ export default function List() { return (
- - + + - + Status - + - + - + } - sx={{ p: 1.8 }} + fullWidth + sx={{ height: '56px' }} onClick={handleExportClick} loading={exportLoading} > From 80204a7abbedfd647cbacf188a86d66340a1874e Mon Sep 17 00:00:00 2001 From: Iqbal Date: Mon, 11 May 2026 15:01:44 +0700 Subject: [PATCH 4/4] feat: add Indonesian log type mapping and color chips to report --- .../Api/PrimayanMedicareController.php | 28 +++++++++---- .../pages/Report/PrimayanMedicare/List.tsx | 42 ++++++++++++------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php b/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php index 79895c7f..89048047 100644 --- a/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php +++ b/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php @@ -87,6 +87,7 @@ class PrimayanMedicareController extends Controller 'claim_requests.status', 'claim_requests.created_at as request_date', 'claim_requests.service_code', + 'request_logs.log_type', 'claims.code as claim_code', 'request_logs.code as log_code', DB::raw('COALESCE(log_amounts.total_incurred, claims.amount_incurred, 0) as amount_incurred'), @@ -200,6 +201,7 @@ class PrimayanMedicareController extends Controller 'claim_requests.status', 'claim_requests.created_at as request_date', 'claim_requests.service_code', + 'request_logs.log_type', 'claims.code as claim_code', 'request_logs.code as log_code', DB::raw('COALESCE(log_amounts.total_incurred, claims.amount_incurred, 0) as amount_incurred'), @@ -252,6 +254,7 @@ class PrimayanMedicareController extends Controller 'Plan', 'Benefit', 'Service', + 'Tipe LOG', 'Provider', 'Request Date', 'Amount Incurred', @@ -262,7 +265,7 @@ class PrimayanMedicareController extends Controller ]; $sheet->fromArray($headers, null, 'A1'); - $sheet->getStyle('A1:Q1')->applyFromArray([ + $sheet->getStyle('A1:R1')->applyFromArray([ 'font' => [ 'bold' => true, ], @@ -273,9 +276,15 @@ class PrimayanMedicareController extends Controller $rowIndex = 2; $no = 1; + + $logTypeMap = [ + 'reference' => 'Rujukan', + 'prescription' => 'Resep', + 'consultation' => 'Konsultasi', + ]; + foreach ($results as $item) { - $serviceCode = $item->service_code ?? '-'; - $serviceName = $serviceCodeMap[$serviceCode] ?? $serviceCode; + $logTypeName = $logTypeMap[$item->log_type] ?? $item->log_type ?? '-'; $rowData = [ $no++, @@ -289,13 +298,14 @@ class PrimayanMedicareController extends Controller $item->gender ?? '-', $item->plan_code ?? '-', $item->benefit_name ?? $item->benefit_desc ?? '-', - $serviceName, + $item->service_code ?? '-', + $logTypeName, $item->provider_name ?? '-', $item->request_date ?? '-', - $item->amount_incurred ?? 0, - $item->amount_approved ?? 0, - $item->amount_not_approved ?? 0, - $item->excess_paid ?? 0, + (int)($item->amount_incurred ?? 0), + (int)($item->amount_approved ?? 0), + (int)($item->amount_not_approved ?? 0), + (int)($item->excess_paid ?? 0), $item->status ?? '-', ]; @@ -303,7 +313,7 @@ class PrimayanMedicareController extends Controller $rowIndex++; } - foreach (range('A', 'Q') as $col) { + foreach (range('A', 'R') as $col) { $sheet->getColumnDimension($col)->setAutoSize(true); } diff --git a/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx b/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx index 586551b1..c9123d47 100644 --- a/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx +++ b/frontend/dashboard/src/pages/Report/PrimayanMedicare/List.tsx @@ -41,6 +41,18 @@ const statusColors: Record = { + reference: 'Rujukan', + prescription: 'Resep', + consultation: 'Konsultasi', +}; + +const logTypeColors: Record = { + reference: 'secondary', + prescription: 'warning', + consultation: 'info', +}; + export default function List() { const [searchParams, setSearchParams] = useSearchParams(); const [providers, setProviders] = useState([]); @@ -266,18 +278,6 @@ export default function List() { function Row(props: { row: any }) { const { row } = props; - const serviceCodeMap: Record = { - 'OP': 'Outpatient', - 'IP': 'Inpatient', - 'DE': 'Dental', - 'MA': 'Maternal', - 'GL': 'Optical', - 'MCU': 'Medical Check Up', - 'MEDIVAC': 'Medical Emergency Evacuation', - }; - - const serviceName = serviceCodeMap[row.service_code] || row.service_code || '-'; - return ( @@ -287,7 +287,18 @@ export default function List() { {row.member_name ?? '-'} {row.member_number ?? '-'} - {serviceName} + {row.service_code ?? '-'} + + {row.log_type ? ( + + ) : ( + '-' + )} + {row.provider_name ?? '-'} {row.request_date ?? '-'} {fNumber(parseInt(row.amount_incurred ?? 0))} @@ -362,6 +373,7 @@ export default function List() { Nama Member Member ID Service + Tipe LOG Provider Tanggal Request Amount Incurred @@ -373,7 +385,7 @@ export default function List() { {dataTableIsLoading ? ( - + Loading @@ -381,7 +393,7 @@ export default function List() { ) : dataTableData.data.length === 0 ? ( - + No Data