From 39af7d169267f6fc794a8cfb8a0d3a84d0e67f0c Mon Sep 17 00:00:00 2001 From: arifal Date: Wed, 13 Aug 2025 17:51:30 +0700 Subject: [PATCH] partial update revision simplify feature --- src/App.tsx | 6 +- src/components/Layout.tsx | 30 +- src/components/Sidebar.tsx | 54 +- src/pages/BPJSCode.tsx | 1038 ++++++++---------------------- src/pages/BPJSSync.tsx | 92 ++- src/pages/CostRecommendation.tsx | 332 +++++++--- src/pages/Dashboard.tsx | 87 --- src/pages/MedicalRecord.tsx | 567 +++++++++++----- src/pages/MedicalRecordSync.tsx | 115 ++-- src/pages/Patients.tsx | 284 -------- src/pages/RoleManagement.tsx | 137 ++-- src/pages/UserManagement.tsx | 201 ++---- 12 files changed, 1209 insertions(+), 1734 deletions(-) delete mode 100644 src/pages/Patients.tsx diff --git a/src/App.tsx b/src/App.tsx index 215e3c0..cc0d0e1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,8 +6,8 @@ import { } from "react-router-dom"; import Login from "./pages/Login"; import Dashboard from "./pages/Dashboard"; -import Patients from "./pages/Patients"; import MedicalRecord from "./pages/MedicalRecord"; + import CostRecommendation from "./pages/CostRecommendation"; import BPJSSync from "./pages/BPJSSync"; import BPJSCode from "./pages/BPJSCode"; @@ -34,8 +34,8 @@ function App() { > } /> } /> - } /> - } /> + } /> + } /> } /> { setSidebarCollapsed(!sidebarCollapsed); @@ -15,6 +17,11 @@ export default function Layout() { setMobileMenuOpen(!mobileMenuOpen); }; + const handleLogout = () => { + localStorage.removeItem("isAuthenticated"); + navigate("/login"); + }; + return (
{/* Desktop Sidebar */} @@ -57,6 +64,27 @@ export default function Layout() { ClaimGuard Hospital Management
+
+
+
+ A +
+
+
+ Dr. Admin +
+
Administrator
+
+
+ +
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 8ab6c1e..a37d738 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,10 +1,9 @@ import { useState } from "react"; -import { Link, useLocation, useNavigate } from "react-router-dom"; +import { Link, useLocation } from "react-router-dom"; import { LayoutDashboard, Users, FileText, - LogOut, ChevronLeft, ChevronRight, UserCog, @@ -26,12 +25,6 @@ export default function Sidebar({ onToggleCollapse, }: SidebarProps) { const location = useLocation(); - const navigate = useNavigate(); - - const handleLogout = () => { - localStorage.removeItem("isAuthenticated"); - navigate("/login"); - }; const menuItems = [ { @@ -64,11 +57,10 @@ export default function Sidebar({ icon: Users, color: "text-orange-600", submenu: [ - { title: "Manajemen Pasien", icon: Users, path: "/patients" }, { title: "Medical Record Pasien", icon: FileText, - path: "/medical-record", + path: "/patients/medical-record", }, { title: "BPJS Code", icon: Shield, path: "/patients/bpjs-code" }, ], @@ -263,46 +255,8 @@ export default function Sidebar({ - {/* User Profile & Logout */} -
- {!isCollapsed ? ( -
-
-
- A -
-
-

Dr. Admin

-

Administrator

-
-
- - -
- ) : ( -
-
- A -
- -
- )} -
+ {/* Footer space */} +
); } diff --git a/src/pages/BPJSCode.tsx b/src/pages/BPJSCode.tsx index 3e137b6..f9b475d 100644 --- a/src/pages/BPJSCode.tsx +++ b/src/pages/BPJSCode.tsx @@ -1,60 +1,17 @@ import { useState } from "react"; import { - FileText, - Search, - Filter, - Download, - Eye, - Calendar, - Clock, Code, - Activity, - DollarSign, - TrendingUp, AlertCircle, - Building2, Wand2, ShieldCheck, CheckCircle2, ClipboardList, + Download, } from "lucide-react"; -interface DiagnoseCode { - id: string; - icdCode: string; - bpjsCode: string; - description: string; - category: string; - severity: "ringan" | "sedang" | "berat"; - bpjsRate: number; - usageCount: number; - lastUsed: string; - department: string; -} +// Halaman Assist Coding (tanpa tab ICD dan tanpa kolom departemen) -// removed legacy ICP-9 procedure model - -interface ICD9DiagnoseCode { - id: string; - icd9Code: string; - bpjsCode: string; - description: string; - category: string; - severity: "ringan" | "sedang" | "berat"; - bpjsRate: number; - usageCount: number; - lastUsed: string; - department: string; -} - -interface CodeUsageStats { - totalDiagnoses: number; - totalProcedures: number; - totalRevenue: number; - mostUsedDiagnose: string; - mostUsedProcedure: string; - averageClaimValue: number; -} +// removed unused CodeUsageStats summary interface interface AssistInput { clinicalNotes: string; @@ -85,51 +42,23 @@ interface AssistResult { } export default function BPJSCode() { - const [activeTab, setActiveTab] = useState<"diagnose" | "procedure" | "assist">( - "diagnose" - ); - const [searchTerm, setSearchTerm] = useState(""); - const [categoryFilter, setCategoryFilter] = useState("all"); - const [assistInput, setAssistInput] = useState({ clinicalNotes: "", labResults: "", procedures: "" }); + const [assistInput, setAssistInput] = useState({ + clinicalNotes: "", + labResults: "", + procedures: "", + }); const [assistLoading, setAssistLoading] = useState(false); const [assistError, setAssistError] = useState(null); const [assistResult, setAssistResult] = useState(null); - const formatDate = (dateString: string) => { - const date = new Date(dateString); - return date.toLocaleDateString("id-ID", { - day: "numeric", - month: "short", - year: "numeric", - hour: "2-digit", - minute: "2-digit", - }); - }; - - const formatCurrency = (amount: number) => { - return new Intl.NumberFormat("id-ID", { + const formatCurrency = (amount: number) => + new Intl.NumberFormat("id-ID", { style: "currency", currency: "IDR", minimumFractionDigits: 0, }).format(amount); - }; - const formatDateOnly = (dateString: string) => { - const date = new Date(dateString); - return date.toLocaleDateString("id-ID", { - day: "numeric", - month: "short", - year: "numeric", - }); - }; - - const formatTimeOnly = (dateString: string) => { - const date = new Date(dateString); - return date.toLocaleTimeString("id-ID", { - hour: "2-digit", - minute: "2-digit", - }); - }; + // (format tanggal untuk tabel dihapus karena halaman hanya Assist Coding) // Mock pipeline: preprocess -> LLM -> RAG -> INA-CBGs mapping const preprocessInput = (input: AssistInput): AssistInput => { @@ -141,43 +70,108 @@ export default function BPJSCode() { }; }; - const mockLLMRecommend = async (input: AssistInput): Promise => { - const text = `${input.clinicalNotes} ${input.labResults} ${input.procedures}`.toLowerCase(); + const mockLLMRecommend = async ( + input: AssistInput + ): Promise => { + const text = + `${input.clinicalNotes} ${input.labResults} ${input.procedures}`.toLowerCase(); const recs: RecommendedCode[] = []; - if (text.includes("hipertensi") || text.includes("bp 150/95") || text.includes("tekanan darah")) { - recs.push({ type: "ICD10", code: "I10", description: "Essential (primary) hypertension", confidence: 90, rationale: "Temuan tekanan darah tinggi/hipertensi pada catatan klinis." }); + if ( + text.includes("hipertensi") || + text.includes("bp 150/95") || + text.includes("tekanan darah") + ) { + recs.push({ + type: "ICD10", + code: "I10", + description: "Essential (primary) hypertension", + confidence: 90, + rationale: + "Temuan tekanan darah tinggi/hipertensi pada catatan klinis.", + }); } - if (text.includes("hba1c") || text.includes("diabetes") || text.includes("glukosa puasa")) { - recs.push({ type: "ICD10", code: "E11", description: "Type 2 diabetes mellitus", confidence: 86, rationale: "Hasil lab HbA1c/glukosa dan kata kunci diabetes terdeteksi." }); + if ( + text.includes("hba1c") || + text.includes("diabetes") || + text.includes("glukosa puasa") + ) { + recs.push({ + type: "ICD10", + code: "E11", + description: "Type 2 diabetes mellitus", + confidence: 86, + rationale: + "Hasil lab HbA1c/glukosa dan kata kunci diabetes terdeteksi.", + }); } - if (text.includes("pneumonia") || text.includes("infiltrat") || text.includes("nyeri dada batuk demam")) { - recs.push({ type: "ICD10", code: "J18.9", description: "Pneumonia, unspecified organism", confidence: 80, rationale: "Gambaran klinis/temuan imaging mendukung pneumonia." }); + if ( + text.includes("pneumonia") || + text.includes("infiltrat") || + text.includes("nyeri dada batuk demam") + ) { + recs.push({ + type: "ICD10", + code: "J18.9", + description: "Pneumonia, unspecified organism", + confidence: 80, + rationale: "Gambaran klinis/temuan imaging mendukung pneumonia.", + }); } - if (text.includes("endoskopi") || text.includes("kateterisasi") || text.includes("fiksasi")) { - recs.push({ type: "ICD9CM", code: "45.13", description: "Endoskopi lambung (contoh)", confidence: 72, rationale: "Prosedur terekstrak dari tindakan/operasi." }); + if ( + text.includes("endoskopi") || + text.includes("kateterisasi") || + text.includes("fiksasi") + ) { + recs.push({ + type: "ICD9CM", + code: "45.13", + description: "Endoskopi lambung (contoh)", + confidence: 72, + rationale: "Prosedur terekstrak dari tindakan/operasi.", + }); } if (recs.length === 0) { - recs.push({ type: "ICD10", code: "R69", description: "Illness, unspecified", confidence: 55, rationale: "Tidak ada sinyal kuat; butuh klarifikasi klinis." }); + recs.push({ + type: "ICD10", + code: "R69", + description: "Illness, unspecified", + confidence: 55, + rationale: "Tidak ada sinyal kuat; butuh klarifikasi klinis.", + }); } await new Promise((r) => setTimeout(r, 500)); return recs.sort((a, b) => b.confidence - a.confidence).slice(0, 6); }; - const mockRAGValidate = async (recs: RecommendedCode[]): Promise<{ validated: RecommendedCode[]; references: string[] }> => { + const mockRAGValidate = async ( + recs: RecommendedCode[] + ): Promise<{ validated: RecommendedCode[]; references: string[] }> => { // Anggap melakukan pencarian ke referensi nasional (Kemenkes/BPJS) const refs: string[] = recs.map((r) => `Ref:${r.type}:${r.code}`); await new Promise((r) => setTimeout(r, 300)); return { validated: recs, references: refs }; }; - const mockMapInaCbgs = async (recs: RecommendedCode[]): Promise => { + const mockMapInaCbgs = async ( + recs: RecommendedCode[] + ): Promise => { // Mapping sederhana contoh saja const hasPneumonia = recs.some((r) => r.code.startsWith("J18")); const hasDM = recs.some((r) => r.code.startsWith("E11")); const mapping: InaCbgsMapping | null = hasPneumonia - ? { group: "Respiratory", code: "E-4-13-II", description: "Pneumonia", estTariff: 3500000 } + ? { + group: "Respiratory", + code: "E-4-13-II", + description: "Pneumonia", + estTariff: 3500000, + } : hasDM - ? { group: "Endocrine", code: "E-1-10-I", description: "Diabetes Mellitus", estTariff: 2100000 } + ? { + group: "Endocrine", + code: "E-1-10-I", + description: "Diabetes Mellitus", + estTariff: 2100000, + } : null; await new Promise((r) => setTimeout(r, 300)); return mapping; @@ -192,200 +186,21 @@ export default function BPJSCode() { const llm = await mockLLMRecommend(pre); const rag = await mockRAGValidate(llm); const ina = await mockMapInaCbgs(rag.validated); - setAssistResult({ preprocessed: pre, recommendedCodes: rag.validated, inaCbgs: ina, references: rag.references }); + setAssistResult({ + preprocessed: pre, + recommendedCodes: rag.validated, + inaCbgs: ina, + references: rag.references, + }); } catch (e) { + console.error(e); setAssistError("Gagal menjalankan Assist Coding. Coba lagi."); } finally { setAssistLoading(false); } }; - const getSeverityColor = (severity: string) => { - switch (severity) { - case "ringan": - return "bg-green-100 text-green-800"; - case "sedang": - return "bg-yellow-100 text-yellow-800"; - case "berat": - return "bg-red-100 text-red-800"; - default: - return "bg-gray-100 text-gray-800"; - } - }; - - // removed legacy complexity color helper (ICP-9 procedures) - - // Sample diagnose codes data - const [diagnoseCodes] = useState([ - { - id: "1", - icdCode: "I10", - bpjsCode: "BPJS-I10-01", - description: "Hipertensi Esensial", - category: "Penyakit Kardiovaskular", - severity: "sedang", - bpjsRate: 850000, - usageCount: 145, - lastUsed: "2024-01-15T14:30:00Z", - department: "Poli Dalam", - }, - { - id: "2", - icdCode: "E11", - bpjsCode: "BPJS-E11-02", - description: "Diabetes Mellitus Tipe 2", - category: "Penyakit Endokrin", - severity: "sedang", - bpjsRate: 1200000, - usageCount: 98, - lastUsed: "2024-01-15T10:45:00Z", - department: "Poli Endokrin", - }, - { - id: "3", - icdCode: "J18.9", - bpjsCode: "BPJS-J18-03", - description: "Pneumonia", - category: "Penyakit Pernafasan", - severity: "berat", - bpjsRate: 2500000, - usageCount: 67, - lastUsed: "2024-01-14T16:20:00Z", - department: "Poli Paru", - }, - { - id: "4", - icdCode: "K29.1", - bpjsCode: "BPJS-K29-04", - description: "Gastritis Akut", - category: "Penyakit Pencernaan", - severity: "ringan", - bpjsRate: 650000, - usageCount: 112, - lastUsed: "2024-01-13T09:15:00Z", - department: "Poli Dalam", - }, - { - id: "5", - icdCode: "S52.5", - bpjsCode: "BPJS-S52-05", - description: "Fraktur Radius", - category: "Cedera dan Keracunan", - severity: "berat", - bpjsRate: 3200000, - usageCount: 34, - lastUsed: "2024-01-12T11:30:00Z", - department: "Ortopedi", - }, - ]); - - // Sample procedure codes data - const [icd9DiagnoseCodes] = useState([ - { - id: "1", - icd9Code: "250.00", - bpjsCode: "BPJS-250-00", - description: "Diabetes mellitus tanpa komplikasi", - category: "Penyakit Endokrin", - severity: "sedang", - bpjsRate: 950000, - usageCount: 132, - lastUsed: "2024-01-15T13:20:00Z", - department: "Poli Endokrin", - }, - { - id: "2", - icd9Code: "486", - bpjsCode: "BPJS-486-00", - description: "Pneumonia, organisme tidak spesifik", - category: "Penyakit Pernafasan", - severity: "berat", - bpjsRate: 2700000, - usageCount: 58, - lastUsed: "2024-01-14T08:45:00Z", - department: "Poli Paru", - }, - { - id: "3", - icd9Code: "401.9", - bpjsCode: "BPJS-401-90", - description: "Hipertensi esensial, tidak spesifik", - category: "Penyakit Kardiovaskular", - severity: "sedang", - bpjsRate: 780000, - usageCount: 120, - lastUsed: "2024-01-13T14:15:00Z", - department: "Poli Dalam", - }, - { - id: "4", - icd9Code: "530.81", - bpjsCode: "BPJS-530-81", - description: "Penyakit refluks gastroesofageal", - category: "Penyakit Pencernaan", - severity: "ringan", - bpjsRate: 620000, - usageCount: 93, - lastUsed: "2024-01-12T10:30:00Z", - department: "Gastroenterologi", - }, - { - id: "5", - icd9Code: "813.42", - bpjsCode: "BPJS-813-42", - description: "Fraktur tertutup radius distal", - category: "Cedera dan Keracunan", - severity: "berat", - bpjsRate: 3400000, - usageCount: 27, - lastUsed: "2024-01-15T16:45:00Z", - department: "Ortopedi", - }, - ]); - - // Calculate statistics - const stats: CodeUsageStats = { - totalDiagnoses: diagnoseCodes.length, - totalProcedures: icd9DiagnoseCodes.length, - totalRevenue: - diagnoseCodes.reduce((sum, d) => sum + d.bpjsRate * d.usageCount, 0) + - icd9DiagnoseCodes.reduce((sum, p) => sum + p.bpjsRate * p.usageCount, 0), - mostUsedDiagnose: - diagnoseCodes.sort((a, b) => b.usageCount - a.usageCount)[0]?.icdCode || - "", - mostUsedProcedure: - icd9DiagnoseCodes.sort((a, b) => b.usageCount - a.usageCount)[0]?.icd9Code || - "", - averageClaimValue: - (diagnoseCodes.reduce((sum, d) => sum + d.bpjsRate, 0) + - icd9DiagnoseCodes.reduce((sum, p) => sum + p.bpjsRate, 0)) / - (diagnoseCodes.length + icd9DiagnoseCodes.length), - }; - - // Filter data based on search and category - const filteredDiagnoseCodes = diagnoseCodes.filter((code) => { - const matchesSearch = - code.icdCode.toLowerCase().includes(searchTerm.toLowerCase()) || - code.description.toLowerCase().includes(searchTerm.toLowerCase()) || - code.category.toLowerCase().includes(searchTerm.toLowerCase()); - - const matchesCategory = - categoryFilter === "all" || code.category === categoryFilter; - - return matchesSearch && matchesCategory; - }); - - const filteredICD9DiagnoseCodes = icd9DiagnoseCodes.filter((code) => { - const matchesSearch = - code.icd9Code.toLowerCase().includes(searchTerm.toLowerCase()) || - code.description.toLowerCase().includes(searchTerm.toLowerCase()) || - code.category.toLowerCase().includes(searchTerm.toLowerCase()); - - const matchesCategory = - categoryFilter === "all" || code.category === categoryFilter; - - return matchesSearch && matchesCategory; - }); + // Halaman fokus pada Assist Coding return (
@@ -396,512 +211,229 @@ export default function BPJSCode() {

- BPJS Code - Diagnosis & Procedure + BPJS Assist Coding

- Kelola kode diagnosis ICD-10 dan prosedur ICP-9 untuk klaim BPJS + Bantuan penentuan kode ICD dan mapping INA-CBGs berbasis input + klinis.

-
- - -
- {/* Statistics Cards */} -
-
-
-
-

- Total Diagnosis -

-

- {stats.totalDiagnoses} -

-
-
- -
-
-
- -
-
-
-

- Total Prosedur -

-

- {stats.totalProcedures} -

-
-
- -
-
-
- -
-
-
-

- Total Revenue -

-

- {formatCurrency(stats.totalRevenue)} -

-
-
- -
-
-
- -
-
-
-

- Rata-rata Klaim -

-

- {formatCurrency(stats.averageClaimValue)} -

-
-
- -
-
-
-
- - {/* Tabs */} + {/* Assist Coding */}
-
-