fix revision hide menu and test case

This commit is contained in:
arifal
2025-08-14 21:58:28 +07:00
parent d16934d7e4
commit 4091307e74
5 changed files with 1991 additions and 1938 deletions

View File

@@ -1,274 +1,274 @@
import { useState } from "react"; import { useState } from "react";
import { Link, useLocation } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import { import {
LayoutDashboard, LayoutDashboard,
Users, Users,
FileText, FileText,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
UserCog, // UserCog,
Lock, // Lock,
Database, Database,
Shield, Shield,
TrendingUp, TrendingUp,
} from "lucide-react"; } from "lucide-react";
import { clsx } from "clsx"; import { clsx } from "clsx";
import claimGuardLogo from "../assets/claim-guard.jpeg"; import claimGuardLogo from "../assets/claim-guard.jpeg";
interface SidebarProps { interface SidebarProps {
isCollapsed?: boolean; isCollapsed?: boolean;
onToggleCollapse?: () => void; onToggleCollapse?: () => void;
} }
export default function Sidebar({ export default function Sidebar({
isCollapsed = false, isCollapsed = false,
onToggleCollapse, onToggleCollapse,
}: SidebarProps) { }: SidebarProps) {
const location = useLocation(); const location = useLocation();
const menuItems = [ const menuItems = [
{ {
title: "Dashboard", title: "Dashboard",
icon: LayoutDashboard, icon: LayoutDashboard,
path: "/dashboard", path: "/dashboard",
color: "text-blue-600", color: "text-blue-600",
}, },
{ {
title: "Integrasi Data", title: "Integrasi Data",
icon: Database, icon: Database,
color: "text-blue-600", color: "text-blue-600",
submenu: [ submenu: [
{ {
title: "Sinkronisasi BPJS", title: "Sinkronisasi BPJS",
icon: Shield, icon: Shield,
path: "/integration/bpjs-sync", path: "/integration/bpjs-sync",
}, },
{ {
title: "Sinkronisasi Rekam Medis", title: "Sinkronisasi Rekam Medis",
icon: FileText, icon: FileText,
path: "/integration/medical-record-sync", path: "/integration/medical-record-sync",
}, },
], ],
}, },
{ {
title: "Rekam Medis", title: "Rekam Medis",
icon: Users, icon: Users,
color: "text-blue-600", color: "text-blue-600",
submenu: [ submenu: [
{ {
title: "Klinis", title: "Klinis",
icon: FileText, icon: FileText,
path: "/medical-records/clinical", path: "/medical-records/clinical",
}, },
{ {
title: "Administratif", title: "Administratif",
icon: FileText, icon: FileText,
path: "/medical-records/administrative", path: "/medical-records/administrative",
}, },
{ {
title: "Rekomendasi Biaya", title: "Rekomendasi Biaya",
icon: TrendingUp, icon: TrendingUp,
path: "/medical-records/cost-recommendation", path: "/medical-records/cost-recommendation",
}, },
{ {
title: "Kodefikasi BPJS", title: "Kodefikasi BPJS",
icon: FileText, icon: FileText,
path: "/medical-records/bpjs-codification", path: "/medical-records/bpjs-codification",
}, },
], ],
}, },
{ // {
title: "Administrasi Sistem", // title: "Administrasi Sistem",
icon: UserCog, // icon: UserCog,
color: "text-blue-600", // color: "text-blue-600",
submenu: [ // submenu: [
{ // {
title: "Pengguna", // title: "Pengguna",
icon: Users, // icon: Users,
path: "/system-administration/user", // path: "/system-administration/user",
}, // },
{ // {
title: "Peran", // title: "Peran",
icon: Lock, // icon: Lock,
path: "/system-administration/role", // path: "/system-administration/role",
}, // },
], // ],
}, // },
]; ];
const [expandedMenu, setExpandedMenu] = useState<string | null>(null); const [expandedMenu, setExpandedMenu] = useState<string | null>(null);
const toggleSubmenu = (title: string) => { const toggleSubmenu = (title: string) => {
setExpandedMenu(expandedMenu === title ? null : title); setExpandedMenu(expandedMenu === title ? null : title);
}; };
const isActive = (path: string) => location.pathname === path; const isActive = (path: string) => location.pathname === path;
return ( return (
<div <div
className={clsx( className={clsx(
"bg-white border-r border-gray-200 h-screen flex flex-col transition-all duration-300 ease-in-out", "bg-white border-r border-gray-200 h-screen flex flex-col transition-all duration-300 ease-in-out",
isCollapsed ? "w-16" : "w-64" isCollapsed ? "w-16" : "w-64"
)} )}
> >
{/* Header */} {/* Header */}
<div <div
className={clsx( className={clsx(
"border-b border-gray-200", "border-b border-gray-200",
isCollapsed ? "p-2" : "p-4" isCollapsed ? "p-2" : "p-4"
)} )}
> >
<div className="flex items-center justify-between w-full"> <div className="flex items-center justify-between w-full">
{!isCollapsed ? ( {!isCollapsed ? (
<> <>
<div className="flex items-center"> <div className="flex items-center">
<div className="bg-white p-1 rounded-lg mr-3 border border-gray-200"> <div className="bg-white p-1 rounded-lg mr-3 border border-gray-200">
<img <img
src={claimGuardLogo} src={claimGuardLogo}
alt="ClaimGuard Logo" alt="ClaimGuard Logo"
className="h-8 w-8 object-cover rounded-md" className="h-8 w-8 object-cover rounded-md"
/> />
</div> </div>
<div> <div>
<h1 className="text-lg font-bold text-gray-900"> <h1 className="text-lg font-bold text-gray-900">
ClaimGuard ClaimGuard
</h1> </h1>
<p className="text-xs text-gray-500">Hospital Management</p> <p className="text-xs text-gray-500">Hospital Management</p>
</div> </div>
</div> </div>
{onToggleCollapse && ( {onToggleCollapse && (
<button <button
onClick={onToggleCollapse} onClick={onToggleCollapse}
className="p-1 hover:bg-blue-50 hover:text-blue-600 rounded-md transition-colors" className="p-1 hover:bg-blue-50 hover:text-blue-600 rounded-md transition-colors"
title="Collapse Sidebar" title="Collapse Sidebar"
> >
<ChevronLeft className="h-4 w-4 text-gray-500" /> <ChevronLeft className="h-4 w-4 text-gray-500" />
</button> </button>
)} )}
</> </>
) : ( ) : (
<div className="w-full flex justify-center"> <div className="w-full flex justify-center">
<button <button
onClick={onToggleCollapse} onClick={onToggleCollapse}
className="bg-white p-2 rounded-lg hover:bg-blue-50 hover:border-blue-300 transition-colors border border-gray-200 shadow-sm" className="bg-white p-2 rounded-lg hover:bg-blue-50 hover:border-blue-300 transition-colors border border-gray-200 shadow-sm"
title="ClaimGuard - Expand Sidebar" title="ClaimGuard - Expand Sidebar"
> >
<img <img
src={claimGuardLogo} src={claimGuardLogo}
alt="ClaimGuard Logo" alt="ClaimGuard Logo"
className="h-6 w-6 object-cover rounded-sm" className="h-6 w-6 object-cover rounded-sm"
/> />
</button> </button>
</div> </div>
)} )}
</div> </div>
</div> </div>
{/* Navigation */} {/* Navigation */}
<nav className="flex-1 overflow-y-auto py-4"> <nav className="flex-1 overflow-y-auto py-4">
<div className="px-3 space-y-1"> <div className="px-3 space-y-1">
{menuItems.map((item) => ( {menuItems.map((item) => (
<div key={item.title}> <div key={item.title}>
{item.submenu ? ( {item.submenu ? (
<div> <div>
<button <button
onClick={() => { onClick={() => {
if (isCollapsed && onToggleCollapse) { if (isCollapsed && onToggleCollapse) {
onToggleCollapse(); onToggleCollapse();
} else { } else {
toggleSubmenu(item.title); toggleSubmenu(item.title);
} }
}} }}
title={ title={
isCollapsed isCollapsed
? `Klik untuk membuka ${item.title}` ? `Klik untuk membuka ${item.title}`
: item.title : item.title
} }
className={clsx( className={clsx(
"w-full flex items-center py-2 rounded-lg text-sm font-medium transition-colors", "w-full flex items-center py-2 rounded-lg text-sm font-medium transition-colors",
"hover:bg-blue-50 hover:text-blue-700 text-gray-700", "hover:bg-blue-50 hover:text-blue-700 text-gray-700",
isCollapsed ? "px-2 justify-center" : "px-3" isCollapsed ? "px-2 justify-center" : "px-3"
)} )}
> >
<div className="relative"> <div className="relative">
<item.icon <item.icon
className={clsx( className={clsx(
"h-5 w-5", "h-5 w-5",
item.color, item.color,
isCollapsed ? "" : "mr-3" isCollapsed ? "" : "mr-3"
)} )}
/> />
{isCollapsed && ( {isCollapsed && (
<div className="absolute -bottom-1 -right-1 w-2 h-2 bg-blue-500 rounded-full opacity-60"></div> <div className="absolute -bottom-1 -right-1 w-2 h-2 bg-blue-500 rounded-full opacity-60"></div>
)} )}
</div> </div>
{!isCollapsed && ( {!isCollapsed && (
<> <>
<span className="flex-1 text-left">{item.title}</span> <span className="flex-1 text-left">{item.title}</span>
<ChevronRight <ChevronRight
className={clsx( className={clsx(
"h-4 w-4 transition-transform", "h-4 w-4 transition-transform",
expandedMenu === item.title && "transform rotate-90" expandedMenu === item.title && "transform rotate-90"
)} )}
/> />
</> </>
)} )}
</button> </button>
{!isCollapsed && expandedMenu === item.title && ( {!isCollapsed && expandedMenu === item.title && (
<div className="ml-6 mt-1 space-y-1"> <div className="ml-6 mt-1 space-y-1">
{item.submenu.map((subItem) => ( {item.submenu.map((subItem) => (
<Link <Link
key={subItem.path} key={subItem.path}
to={subItem.path} to={subItem.path}
className={clsx( className={clsx(
"flex items-center px-3 py-2 rounded-lg text-sm transition-colors", "flex items-center px-3 py-2 rounded-lg text-sm transition-colors",
isActive(subItem.path) isActive(subItem.path)
? "bg-blue-50 text-blue-700 border-r-2 border-blue-600" ? "bg-blue-50 text-blue-700 border-r-2 border-blue-600"
: "text-gray-600 hover:bg-blue-50 hover:text-blue-700" : "text-gray-600 hover:bg-blue-50 hover:text-blue-700"
)} )}
> >
<subItem.icon className="h-4 w-4 mr-3" /> <subItem.icon className="h-4 w-4 mr-3" />
<span>{subItem.title}</span> <span>{subItem.title}</span>
</Link> </Link>
))} ))}
</div> </div>
)} )}
</div> </div>
) : ( ) : (
<Link <Link
to={item.path} to={item.path}
title={isCollapsed ? item.title : undefined} title={isCollapsed ? item.title : undefined}
className={clsx( className={clsx(
"flex items-center py-2 rounded-lg text-sm font-medium transition-colors", "flex items-center py-2 rounded-lg text-sm font-medium transition-colors",
isActive(item.path) isActive(item.path)
? "bg-blue-50 text-blue-700 border-r-2 border-blue-600" ? "bg-blue-50 text-blue-700 border-r-2 border-blue-600"
: "text-gray-700 hover:bg-blue-50 hover:text-blue-700", : "text-gray-700 hover:bg-blue-50 hover:text-blue-700",
isCollapsed ? "px-2 justify-center" : "px-3" isCollapsed ? "px-2 justify-center" : "px-3"
)} )}
> >
<item.icon <item.icon
className={clsx( className={clsx(
"h-5 w-5", "h-5 w-5",
item.color, item.color,
isCollapsed ? "" : "mr-3" isCollapsed ? "" : "mr-3"
)} )}
/> />
{!isCollapsed && <span>{item.title}</span>} {!isCollapsed && <span>{item.title}</span>}
</Link> </Link>
)} )}
</div> </div>
))} ))}
</div> </div>
{/* Divider */} {/* Divider */}
<div className="my-4 px-3"> <div className="my-4 px-3">
<div className="border-t border-gray-200"></div> <div className="border-t border-gray-200"></div>
</div> </div>
</nav> </nav>
{/* Footer space */} {/* Footer space */}
<div className="border-t border-gray-200 p-4"></div> <div className="border-t border-gray-200 p-4"></div>
</div> </div>
); );
} }

View File

@@ -1,472 +1,472 @@
import { import {
Users, Users,
TrendingUp, TrendingUp,
AlertTriangle, AlertTriangle,
Shield, Shield,
Database, Database,
RefreshCw, RefreshCw,
FileText, FileText,
Search, Search,
Bell, Bell,
CheckCircle, CheckCircle,
XCircle, XCircle,
Clock, Clock,
ArrowRight, ArrowRight,
Code, Code,
Stethoscope, Stethoscope,
} from "lucide-react"; } from "lucide-react";
export default function Dashboard() { export default function Dashboard() {
// Stats berdasarkan fitur project yang ada // Stats berdasarkan fitur project yang ada
const stats = [ const stats = [
{ {
title: "BPJS Sync Status", title: "BPJS Sync Status",
value: "18/25", value: "18/25",
change: "72% berhasil", change: "72% berhasil",
changeColor: "text-green-600", changeColor: "text-green-600",
icon: RefreshCw, icon: RefreshCw,
color: "text-blue-600", color: "text-blue-600",
subtitle: "Sync hari ini", subtitle: "Sync hari ini",
}, },
{ {
title: "Medical Records", title: "Medical Records",
value: "89", value: "89",
change: "+12 hari ini", change: "+12 hari ini",
changeColor: "text-green-600", changeColor: "text-green-600",
icon: FileText, icon: FileText,
color: "text-green-600", color: "text-green-600",
subtitle: "Record tersinkron", subtitle: "Record tersinkron",
}, },
{ {
title: "ICD Recommendations", title: "ICD Recommendations",
value: "23", value: "23",
change: "87% confidence", change: "87% confidence",
changeColor: "text-blue-600", changeColor: "text-blue-600",
icon: Code, icon: Code,
color: "text-purple-600", color: "text-purple-600",
subtitle: "Generated today", subtitle: "Generated today",
}, },
{ {
title: "Active Users", title: "Active Users",
value: "28/30", value: "28/30",
change: "24 login hari ini", change: "24 login hari ini",
changeColor: "text-green-600", changeColor: "text-green-600",
icon: Users, icon: Users,
color: "text-orange-600", color: "text-orange-600",
subtitle: "System users", subtitle: "System users",
}, },
]; ];
return ( return (
<div className="p-6"> <div className="p-6">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
{/* Welcome Section */} {/* Welcome Section */}
<div className="mb-8"> <div className="mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-2"> <h2 className="text-3xl font-bold text-gray-900 mb-2">
Dashboard ClaimGuard Dashboard ClaimGuard
</h2> </h2>
<p className="text-gray-600"> <p className="text-gray-600">
Monitor BPJS operations, medical records, ICD recommendations, dan Monitor BPJS operations, medical records, ICD recommendations, dan
sistem management secara real-time. sistem management secara real-time.
</p> </p>
</div> </div>
{/* Alert/Warning Section */} {/* Alert/Warning Section */}
<div className="bg-white rounded-lg shadow-sm border mb-6 p-4"> <div className="bg-white rounded-lg shadow-sm border mb-6 p-4 hidden">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900 flex items-center"> <h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Bell className="h-5 w-5 text-red-500 mr-2" /> <Bell className="h-5 w-5 text-red-500 mr-2" />
Peringatan & Notifikasi Peringatan & Notifikasi
</h3> </h3>
<span className="text-sm text-gray-500">Real-time alerts</span> <span className="text-sm text-gray-500">Real-time alerts</span>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-red-50 border border-red-200 rounded-lg p-3"> <div className="bg-red-50 border border-red-200 rounded-lg p-3">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div> <div>
<div className="flex items-center"> <div className="flex items-center">
<AlertTriangle className="h-4 w-4 text-red-600 mr-2" /> <AlertTriangle className="h-4 w-4 text-red-600 mr-2" />
<span className="text-sm font-medium text-red-900"> <span className="text-sm font-medium text-red-900">
Overclaim Detected Overclaim Detected
</span> </span>
</div> </div>
<p className="text-sm text-red-700 mt-1"> <p className="text-sm text-red-700 mt-1">
7 kasus interval &lt; 30 hari 7 kasus interval &lt; 30 hari
</p> </p>
<p className="text-xs text-red-600 mt-1"> <p className="text-xs text-red-600 mt-1">
ICD: I10, E11, J18.9 ICD: I10, E11, J18.9
</p> </p>
</div> </div>
<button className="text-red-600 hover:text-red-800"> <button className="text-red-600 hover:text-red-800">
<ArrowRight className="h-4 w-4" /> <ArrowRight className="h-4 w-4" />
</button> </button>
</div> </div>
</div> </div>
<div className="bg-orange-50 border border-orange-200 rounded-lg p-3"> <div className="bg-orange-50 border border-orange-200 rounded-lg p-3">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div> <div>
<div className="flex items-center"> <div className="flex items-center">
<Clock className="h-4 w-4 text-orange-600 mr-2" /> <Clock className="h-4 w-4 text-orange-600 mr-2" />
<span className="text-sm font-medium text-orange-900"> <span className="text-sm font-medium text-orange-900">
Inconsistent Visits Inconsistent Visits
</span> </span>
</div> </div>
<p className="text-sm text-orange-700 mt-1"> <p className="text-sm text-orange-700 mt-1">
3 kontrol tidak konsisten 3 kontrol tidak konsisten
</p> </p>
<p className="text-xs text-orange-600 mt-1"> <p className="text-xs text-orange-600 mt-1">
Tipe vs alasan tidak sesuai Tipe vs alasan tidak sesuai
</p> </p>
</div> </div>
<button className="text-orange-600 hover:text-orange-800"> <button className="text-orange-600 hover:text-orange-800">
<ArrowRight className="h-4 w-4" /> <ArrowRight className="h-4 w-4" />
</button> </button>
</div> </div>
</div> </div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3"> <div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div> <div>
<div className="flex items-center"> <div className="flex items-center">
<XCircle className="h-4 w-4 text-blue-600 mr-2" /> <XCircle className="h-4 w-4 text-blue-600 mr-2" />
<span className="text-sm font-medium text-blue-900"> <span className="text-sm font-medium text-blue-900">
Sync Failures Sync Failures
</span> </span>
</div> </div>
<p className="text-sm text-blue-700 mt-1"> <p className="text-sm text-blue-700 mt-1">
5 BPJS sync gagal 5 BPJS sync gagal
</p> </p>
<p className="text-xs text-blue-600 mt-1"> <p className="text-xs text-blue-600 mt-1">
Perlu retry manual Perlu retry manual
</p> </p>
</div> </div>
<button className="text-blue-600 hover:text-blue-800"> <button className="text-blue-600 hover:text-blue-800">
<ArrowRight className="h-4 w-4" /> <ArrowRight className="h-4 w-4" />
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Stats Grid */} {/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8 hidden">
{stats.map((stat, index) => ( {stats.map((stat, index) => (
<div key={index} className="card p-6"> <div key={index} className="card p-6">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<div className={`p-2 rounded-lg bg-gray-100 ${stat.color}`}> <div className={`p-2 rounded-lg bg-gray-100 ${stat.color}`}>
<stat.icon className="h-5 w-5" /> <stat.icon className="h-5 w-5" />
</div> </div>
<span className={`text-sm font-medium ${stat.changeColor}`}> <span className={`text-sm font-medium ${stat.changeColor}`}>
{stat.change} {stat.change}
</span> </span>
</div> </div>
<div> <div>
<p className="text-2xl font-bold text-gray-900 mb-1"> <p className="text-2xl font-bold text-gray-900 mb-1">
{stat.value} {stat.value}
</p> </p>
<p className="text-sm font-semibold text-gray-700 mb-1"> <p className="text-sm font-semibold text-gray-700 mb-1">
{stat.title} {stat.title}
</p> </p>
<p className="text-xs text-gray-500">{stat.subtitle}</p> <p className="text-xs text-gray-500">{stat.subtitle}</p>
</div> </div>
</div> </div>
))} ))}
</div> </div>
{/* Main Grid */} {/* Main Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6 hidden">
{/* BPJS Operations */} {/* BPJS Operations */}
<div className="card p-6"> <div className="card p-6">
<div className="flex items-center justify-between mb-6"> <div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center"> <h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Database className="h-5 w-5 text-blue-600 mr-2" /> <Database className="h-5 w-5 text-blue-600 mr-2" />
BPJS Operations BPJS Operations
</h3> </h3>
<button className="text-sm text-blue-600 hover:text-blue-800 flex items-center"> <button className="text-sm text-blue-600 hover:text-blue-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" /> Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button> </button>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg"> <div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<RefreshCw className="h-4 w-4 text-blue-600" /> <RefreshCw className="h-4 w-4 text-blue-600" />
<span className="text-sm font-medium text-blue-900"> <span className="text-sm font-medium text-blue-900">
Sync Status Sync Status
</span> </span>
</div> </div>
<p className="text-sm text-blue-700">Berhasil: 18/25 (72%)</p> <p className="text-sm text-blue-700">Berhasil: 18/25 (72%)</p>
<p className="text-xs text-blue-600"> <p className="text-xs text-blue-600">
Last sync: 15 Jan 2024 14:30 Last sync: 15 Jan 2024 14:30
</p> </p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-blue-600">18</div> <div className="text-lg font-bold text-blue-600">18</div>
<div className="text-xs text-blue-500">Sukses hari ini</div> <div className="text-xs text-blue-500">Sukses hari ini</div>
</div> </div>
</div> </div>
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg"> <div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<Code className="h-4 w-4 text-green-600" /> <Code className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium text-green-900"> <span className="text-sm font-medium text-green-900">
Active Codes Active Codes
</span> </span>
</div> </div>
<p className="text-sm text-green-700">156 kode BPJS aktif</p> <p className="text-sm text-green-700">156 kode BPJS aktif</p>
<p className="text-xs text-green-600"> <p className="text-xs text-green-600">
8 kode baru bulan ini 8 kode baru bulan ini
</p> </p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-green-600">156</div> <div className="text-lg font-bold text-green-600">156</div>
<div className="text-xs text-green-500">Total kode</div> <div className="text-xs text-green-500">Total kode</div>
</div> </div>
</div> </div>
<div className="flex items-center justify-between p-3 bg-purple-50 rounded-lg"> <div className="flex items-center justify-between p-3 bg-purple-50 rounded-lg">
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<Shield className="h-4 w-4 text-purple-600" /> <Shield className="h-4 w-4 text-purple-600" />
<span className="text-sm font-medium text-purple-900"> <span className="text-sm font-medium text-purple-900">
Assist Coding Assist Coding
</span> </span>
</div> </div>
<p className="text-sm text-purple-700"> <p className="text-sm text-purple-700">
45 coding assistance 45 coding assistance
</p> </p>
<p className="text-xs text-purple-600">12 queries hari ini</p> <p className="text-xs text-purple-600">12 queries hari ini</p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-purple-600">45</div> <div className="text-lg font-bold text-purple-600">45</div>
<div className="text-xs text-purple-500">Total assist</div> <div className="text-xs text-purple-500">Total assist</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Medical Record Operations */} {/* Medical Record Operations */}
<div className="card p-6"> <div className="card p-6">
<div className="flex items-center justify-between mb-6"> <div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center"> <h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Stethoscope className="h-5 w-5 text-green-600 mr-2" /> <Stethoscope className="h-5 w-5 text-green-600 mr-2" />
Medical Records Medical Records
</h3> </h3>
<button className="text-sm text-green-600 hover:text-green-800 flex items-center"> <button className="text-sm text-green-600 hover:text-green-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" /> Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button> </button>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg"> <div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<CheckCircle className="h-4 w-4 text-green-600" /> <CheckCircle className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium text-green-900"> <span className="text-sm font-medium text-green-900">
Sync Records Sync Records
</span> </span>
</div> </div>
<p className="text-sm text-green-700"> <p className="text-sm text-green-700">
1,250 records tersinkron 1,250 records tersinkron
</p> </p>
<p className="text-xs text-green-600">89 records hari ini</p> <p className="text-xs text-green-600">89 records hari ini</p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-green-600">89</div> <div className="text-lg font-bold text-green-600">89</div>
<div className="text-xs text-green-500">Hari ini</div> <div className="text-xs text-green-500">Hari ini</div>
</div> </div>
</div> </div>
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg"> <div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<Search className="h-4 w-4 text-blue-600" /> <Search className="h-4 w-4 text-blue-600" />
<span className="text-sm font-medium text-blue-900"> <span className="text-sm font-medium text-blue-900">
Patient Search Patient Search
</span> </span>
</div> </div>
<p className="text-sm text-blue-700">45 pencarian hari ini</p> <p className="text-sm text-blue-700">45 pencarian hari ini</p>
<p className="text-xs text-blue-600">84% success rate</p> <p className="text-xs text-blue-600">84% success rate</p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-blue-600">45</div> <div className="text-lg font-bold text-blue-600">45</div>
<div className="text-xs text-blue-500">Searches</div> <div className="text-xs text-blue-500">Searches</div>
</div> </div>
</div> </div>
<div className="flex items-center justify-between p-3 bg-orange-50 rounded-lg"> <div className="flex items-center justify-between p-3 bg-orange-50 rounded-lg">
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<FileText className="h-4 w-4 text-orange-600" /> <FileText className="h-4 w-4 text-orange-600" />
<span className="text-sm font-medium text-orange-900"> <span className="text-sm font-medium text-orange-900">
Latest Records Latest Records
</span> </span>
</div> </div>
<p className="text-sm text-orange-700"> <p className="text-sm text-orange-700">
30 records dalam 30 hari 30 records dalam 30 hari
</p> </p>
<p className="text-xs text-orange-600">Filter aktif</p> <p className="text-xs text-orange-600">Filter aktif</p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-orange-600">30</div> <div className="text-lg font-bold text-orange-600">30</div>
<div className="text-xs text-orange-500">Records</div> <div className="text-xs text-orange-500">Records</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Second Row */} {/* Second Row */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 hidden">
{/* ICD Recommendations & Cost */} {/* ICD Recommendations & Cost */}
<div className="card p-6"> <div className="card p-6">
<div className="flex items-center justify-between mb-6"> <div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center"> <h3 className="text-lg font-semibold text-gray-900 flex items-center">
<TrendingUp className="h-5 w-5 text-purple-600 mr-2" /> <TrendingUp className="h-5 w-5 text-purple-600 mr-2" />
ICD & Cost Management ICD & Cost Management
</h3> </h3>
<button className="text-sm text-purple-600 hover:text-purple-800 flex items-center"> <button className="text-sm text-purple-600 hover:text-purple-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" /> Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button> </button>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
{[ {[
{ {
title: "ICD Recommendations", title: "ICD Recommendations",
value: "23", value: "23",
subtitle: "Generated today", subtitle: "Generated today",
detail: "87% avg confidence", detail: "87% avg confidence",
color: "text-purple-600", color: "text-purple-600",
bgColor: "bg-purple-50", bgColor: "bg-purple-50",
icon: Code, icon: Code,
}, },
{ {
title: "BPJS Mappings", title: "BPJS Mappings",
value: "19/23", value: "19/23",
subtitle: "Successfully mapped", subtitle: "Successfully mapped",
detail: "83% mapping rate", detail: "83% mapping rate",
color: "text-blue-600", color: "text-blue-600",
bgColor: "bg-blue-50", bgColor: "bg-blue-50",
icon: Shield, icon: Shield,
}, },
{ {
title: "Cost Analysis", title: "Cost Analysis",
value: "12", value: "12",
subtitle: "Exports today", subtitle: "Exports today",
detail: "JSON format", detail: "JSON format",
color: "text-green-600", color: "text-green-600",
bgColor: "bg-green-50", bgColor: "bg-green-50",
icon: TrendingUp, icon: TrendingUp,
}, },
].map((item, index) => ( ].map((item, index) => (
<div <div
key={index} key={index}
className={`flex items-center justify-between p-3 ${item.bgColor} rounded-lg`} className={`flex items-center justify-between p-3 ${item.bgColor} rounded-lg`}
> >
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<item.icon className={`h-4 w-4 ${item.color}`} /> <item.icon className={`h-4 w-4 ${item.color}`} />
<span <span
className={`text-sm font-medium ${item.color className={`text-sm font-medium ${item.color
.replace("text-", "text-") .replace("text-", "text-")
.replace("-600", "-900")}`} .replace("-600", "-900")}`}
> >
{item.title} {item.title}
</span> </span>
</div> </div>
<p <p
className={`text-sm ${item.color.replace( className={`text-sm ${item.color.replace(
"-600", "-600",
"-700" "-700"
)}`} )}`}
> >
{item.subtitle} {item.subtitle}
</p> </p>
<p className={`text-xs ${item.color}`}>{item.detail}</p> <p className={`text-xs ${item.color}`}>{item.detail}</p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className={`text-lg font-bold ${item.color}`}> <div className={`text-lg font-bold ${item.color}`}>
{item.value} {item.value}
</div> </div>
</div> </div>
</div> </div>
))} ))}
</div> </div>
</div> </div>
{/* System Management */} {/* System Management */}
<div className="card p-6"> <div className="card p-6">
<div className="flex items-center justify-between mb-6"> <div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center"> <h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Users className="h-5 w-5 text-orange-600 mr-2" /> <Users className="h-5 w-5 text-orange-600 mr-2" />
System Management System Management
</h3> </h3>
<button className="text-sm text-orange-600 hover:text-orange-800 flex items-center"> <button className="text-sm text-orange-600 hover:text-orange-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" /> Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button> </button>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-orange-50 rounded-lg"> <div className="flex items-center justify-between p-3 bg-orange-50 rounded-lg">
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<Users className="h-4 w-4 text-orange-600" /> <Users className="h-4 w-4 text-orange-600" />
<span className="text-sm font-medium text-orange-900"> <span className="text-sm font-medium text-orange-900">
User Management User Management
</span> </span>
</div> </div>
<p className="text-sm text-orange-700">28/30 active users</p> <p className="text-sm text-orange-700">28/30 active users</p>
<p className="text-xs text-orange-600">24 login hari ini</p> <p className="text-xs text-orange-600">24 login hari ini</p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-orange-600">28</div> <div className="text-lg font-bold text-orange-600">28</div>
<div className="text-xs text-orange-500">Active</div> <div className="text-xs text-orange-500">Active</div>
</div> </div>
</div> </div>
<div className="flex items-center justify-between p-3 bg-purple-50 rounded-lg"> <div className="flex items-center justify-between p-3 bg-purple-50 rounded-lg">
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<Shield className="h-4 w-4 text-purple-600" /> <Shield className="h-4 w-4 text-purple-600" />
<span className="text-sm font-medium text-purple-900"> <span className="text-sm font-medium text-purple-900">
Role Management Role Management
</span> </span>
</div> </div>
<p className="text-sm text-purple-700">25 roles configured</p> <p className="text-sm text-purple-700">25 roles configured</p>
<p className="text-xs text-purple-600"> <p className="text-xs text-purple-600">
2 permissions updated 2 permissions updated
</p> </p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-purple-600">25</div> <div className="text-lg font-bold text-purple-600">25</div>
<div className="text-xs text-purple-500">Roles</div> <div className="text-xs text-purple-500">Roles</div>
</div> </div>
</div> </div>
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg"> <div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div> <div>
<div className="flex items-center space-x-2 mb-1"> <div className="flex items-center space-x-2 mb-1">
<CheckCircle className="h-4 w-4 text-green-600" /> <CheckCircle className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium text-green-900"> <span className="text-sm font-medium text-green-900">
System Health System Health
</span> </span>
</div> </div>
<p className="text-sm text-green-700">99.8% uptime</p> <p className="text-sm text-green-700">99.8% uptime</p>
<p className="text-xs text-green-600">2.1% error rate</p> <p className="text-xs text-green-600">2.1% error rate</p>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-green-600">99.8%</div> <div className="text-lg font-bold text-green-600">99.8%</div>
<div className="text-xs text-green-500">Uptime</div> <div className="text-xs text-green-500">Uptime</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); );
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,520 +1,498 @@
import { useState } from "react"; import { useState } from "react";
import { import {
Code, Code,
AlertCircle, AlertCircle,
Wand2, Wand2,
ShieldCheck, ShieldCheck,
ClipboardList, ClipboardList,
FileText, FileText,
} from "lucide-react"; } from "lucide-react";
interface AssistInput { interface AssistInput {
clinicalNotes: string; clinicalNotes: string;
labResults: string; procedures: string;
procedures: string;
} }
interface ICD10Code { interface ICD10Code {
code: string; code: string;
description: string; description: string;
reasoning: string; reasoning: string;
} }
interface ProcedureCode { interface ProcedureCode {
code: string; code: string;
description: string; description: string;
reasoning: string; reasoning: string;
} }
interface BPJSMapping { interface BPJSMapping {
ina_cbg_code: string; ina_cbg_code: string;
description: string; description: string;
tariff: number; tariff: number;
} }
interface AssistResult { interface AssistResult {
icd_10_codes: ICD10Code[]; icd_10_codes: ICD10Code[];
procedure_codes: ProcedureCode[]; procedure_codes: ProcedureCode[];
bpjs_mapping: BPJSMapping; bpjs_mapping: BPJSMapping;
} }
export default function BPJSCodeification() { export default function BPJSCodeification() {
const [assistInput, setAssistInput] = useState<AssistInput>({ const [assistInput, setAssistInput] = useState<AssistInput>({
clinicalNotes: "", clinicalNotes: "",
labResults: "", procedures: "",
procedures: "", });
}); const [assistLoading, setAssistLoading] = useState(false);
const [assistLoading, setAssistLoading] = useState(false); const [assistError, setAssistError] = useState<string | null>(null);
const [assistError, setAssistError] = useState<string | null>(null); const [assistResult, setAssistResult] = useState<AssistResult | null>(null);
const [assistResult, setAssistResult] = useState<AssistResult | null>(null);
const formatCurrency = (amount: number) => const formatCurrency = (amount: number) =>
new Intl.NumberFormat("id-ID", { new Intl.NumberFormat("id-ID", {
style: "currency", style: "currency",
currency: "IDR", currency: "IDR",
minimumFractionDigits: 0, minimumFractionDigits: 0,
}).format(amount); }).format(amount);
// Mock pipeline with improved JSON output format // Mock pipeline with improved JSON output format
const preprocessInput = (input: AssistInput): AssistInput => { const preprocessInput = (input: AssistInput): AssistInput => {
const normalize = (s: string) => s.replace(/\s+/g, " ").trim(); const normalize = (s: string) => s.replace(/\s+/g, " ").trim();
return { return {
clinicalNotes: normalize(input.clinicalNotes), clinicalNotes: normalize(input.clinicalNotes),
labResults: normalize(input.labResults), procedures: normalize(input.procedures),
procedures: normalize(input.procedures), };
}; };
};
const mockAssistPipeline = async ( const mockAssistPipeline = async (
input: AssistInput input: AssistInput
): Promise<AssistResult> => { ): Promise<AssistResult> => {
const text = const text = `${input.clinicalNotes} ${input.procedures}`.toLowerCase();
`${input.clinicalNotes} ${input.labResults} ${input.procedures}`.toLowerCase();
const icd10Codes: ICD10Code[] = []; const icd10Codes: ICD10Code[] = [];
const procedureCodes: ProcedureCode[] = []; const procedureCodes: ProcedureCode[] = [];
// ICD-10 Code Detection // ICD-10 Code Detection
if ( if (
text.includes("pneumonia") || text.includes("pneumonia") ||
text.includes("infiltrat") || text.includes("infiltrat") ||
text.includes("demam") || text.includes("demam") ||
text.includes("batuk") || text.includes("batuk") ||
text.includes("sesak") text.includes("sesak")
) { ) {
icd10Codes.push({ icd10Codes.push({
code: "J18.9", code: "J18.9",
description: "Pneumonia dengan kuman tidak spesifik", description: "Pneumonia dengan kuman tidak spesifik",
reasoning: reasoning:
"Gejala demam, batuk, sesak napas, dan hasil pemeriksaan rontgen mengarah pada pneumonia.", "Gejala demam, batuk, sesak napas, dan hasil pemeriksaan rontgen mengarah pada pneumonia.",
}); });
} }
if ( if (
text.includes("hipertensi") || text.includes("hipertensi") ||
text.includes("bp 150/95") || text.includes("bp 150/95") ||
text.includes("tekanan darah tinggi") || text.includes("tekanan darah tinggi") ||
text.includes("hypertension") text.includes("hypertension")
) { ) {
icd10Codes.push({ icd10Codes.push({
code: "I10", code: "I10",
description: "Hipertensi esensial (primer)", description: "Hipertensi esensial (primer)",
reasoning: reasoning:
"Temuan tekanan darah tinggi pada catatan klinis dan pemeriksaan fisik.", "Temuan tekanan darah tinggi pada catatan klinis dan pemeriksaan fisik.",
}); });
} }
if ( if (
text.includes("diabetes") || text.includes("diabetes") ||
text.includes("hba1c") || text.includes("hba1c") ||
text.includes("glukosa") || text.includes("glukosa") ||
text.includes("gula darah") text.includes("gula darah")
) { ) {
icd10Codes.push({ icd10Codes.push({
code: "E11.9", code: "E11.9",
description: "Diabetes mellitus tipe 2 tanpa komplikasi", description: "Diabetes mellitus tipe 2 tanpa komplikasi",
reasoning: reasoning:
"Hasil laboratorium menunjukkan peningkatan HbA1c dan glukosa darah yang mendukung diagnosis diabetes.", "Hasil laboratorium menunjukkan peningkatan HbA1c dan glukosa darah yang mendukung diagnosis diabetes.",
}); });
} }
if ( if (
text.includes("gastritis") || text.includes("gastritis") ||
text.includes("nyeri perut") || text.includes("nyeri perut") ||
text.includes("mual") || text.includes("mual") ||
text.includes("muntah") text.includes("muntah")
) { ) {
icd10Codes.push({ icd10Codes.push({
code: "K29.1", code: "K29.1",
description: "Gastritis akut lainnya", description: "Gastritis akut lainnya",
reasoning: reasoning:
"Gejala nyeri perut, mual, muntah mengarah pada gastritis akut.", "Gejala nyeri perut, mual, muntah mengarah pada gastritis akut.",
}); });
} }
if ( if (
text.includes("influenza") || text.includes("influenza") ||
text.includes("flu") || text.includes("flu") ||
text.includes("demam") || text.includes("demam") ||
text.includes("sakit kepala") text.includes("sakit kepala")
) { ) {
icd10Codes.push({ icd10Codes.push({
code: "J11.1", code: "J11.1",
description: description:
"Influenza dengan virus tidak teridentifikasi disertai manifestasi respiratori", "Influenza dengan virus tidak teridentifikasi disertai manifestasi respiratori",
reasoning: reasoning:
"Gejala demam, sakit kepala, dan manifestasi respiratori mengarah pada influenza.", "Gejala demam, sakit kepala, dan manifestasi respiratori mengarah pada influenza.",
}); });
} }
// Procedure Code Detection // Procedure Code Detection
if ( if (
text.includes("rontgen") || text.includes("rontgen") ||
text.includes("x-ray") || text.includes("x-ray") ||
text.includes("chest x-ray") || text.includes("chest x-ray") ||
text.includes("foto thorax") text.includes("foto thorax")
) { ) {
procedureCodes.push({ procedureCodes.push({
code: "87.44", code: "87.44",
description: "Foto Rontgen thorax, proyeksi tunggal", description: "Foto Rontgen thorax, proyeksi tunggal",
reasoning: "Tindakan rontgen dada sesuai gejala respiratori.", reasoning: "Tindakan rontgen dada sesuai gejala respiratori.",
}); });
} }
if ( if (
text.includes("darah lengkap") || text.includes("darah lengkap") ||
text.includes("complete blood count") || text.includes("complete blood count") ||
text.includes("cbc") || text.includes("cbc") ||
text.includes("lab darah") text.includes("lab darah")
) { ) {
procedureCodes.push({ procedureCodes.push({
code: "90.59", code: "90.59",
description: "Pemeriksaan darah lengkap", description: "Pemeriksaan darah lengkap",
reasoning: "Pemeriksaan darah lengkap untuk mendukung diagnosis.", reasoning: "Pemeriksaan darah lengkap untuk mendukung diagnosis.",
}); });
} }
if ( if (
text.includes("endoskopi") || text.includes("endoskopi") ||
text.includes("endoscopy") || text.includes("endoscopy") ||
text.includes("gastroscopy") text.includes("gastroscopy")
) { ) {
procedureCodes.push({ procedureCodes.push({
code: "45.13", code: "45.13",
description: "Esofagogastroduodenoskopi (EGD)", description: "Esofagogastroduodenoskopi (EGD)",
reasoning: "Tindakan endoskopi lambung untuk evaluasi gastritis.", reasoning: "Tindakan endoskopi lambung untuk evaluasi gastritis.",
}); });
} }
if ( if (
text.includes("ekg") || text.includes("ekg") ||
text.includes("ecg") || text.includes("ecg") ||
text.includes("elektrokardiogram") text.includes("elektrokardiogram")
) { ) {
procedureCodes.push({ procedureCodes.push({
code: "89.52", code: "89.52",
description: "Elektrokardiogram (EKG)", description: "Elektrokardiogram (EKG)",
reasoning: "Pemeriksaan EKG untuk evaluasi kardiovaskular.", reasoning: "Pemeriksaan EKG untuk evaluasi kardiovaskular.",
}); });
} }
if ( if (
text.includes("usg") || text.includes("usg") ||
text.includes("ultrasound") || text.includes("ultrasound") ||
text.includes("ultrasonografi") text.includes("ultrasonografi")
) { ) {
procedureCodes.push({ procedureCodes.push({
code: "88.76", code: "88.76",
description: "USG abdomen dan retroperitoneum diagnostik", description: "USG abdomen dan retroperitoneum diagnostik",
reasoning: "Pemeriksaan USG abdomen untuk evaluasi organ dalam.", reasoning: "Pemeriksaan USG abdomen untuk evaluasi organ dalam.",
}); });
} }
// Default codes if nothing specific detected // Default codes if nothing specific detected
if (icd10Codes.length === 0) { if (icd10Codes.length === 0) {
icd10Codes.push({ icd10Codes.push({
code: "R69", code: "R69",
description: "Penyakit tidak spesifik", description: "Penyakit tidak spesifik",
reasoning: reasoning:
"Tidak ada gejala spesifik yang terdeteksi, memerlukan klarifikasi klinis lebih lanjut.", "Tidak ada gejala spesifik yang terdeteksi, memerlukan klarifikasi klinis lebih lanjut.",
}); });
} }
if (procedureCodes.length === 0) { if (procedureCodes.length === 0) {
procedureCodes.push({ procedureCodes.push({
code: "99213", code: "99213",
description: "Kunjungan rawat jalan", description: "Kunjungan rawat jalan",
reasoning: "Kunjungan rawat jalan untuk evaluasi medis.", reasoning: "Kunjungan rawat jalan untuk evaluasi medis.",
}); });
} }
// BPJS Mapping based on primary diagnosis // BPJS Mapping based on primary diagnosis
let bpjsMapping: BPJSMapping; let bpjsMapping: BPJSMapping;
const hasPneumonia = icd10Codes.some((code) => code.code.startsWith("J18")); const hasPneumonia = icd10Codes.some((code) => code.code.startsWith("J18"));
const hasDiabetes = icd10Codes.some((code) => code.code.startsWith("E11")); const hasDiabetes = icd10Codes.some((code) => code.code.startsWith("E11"));
const hasHypertension = icd10Codes.some((code) => const hasHypertension = icd10Codes.some((code) =>
code.code.startsWith("I10") code.code.startsWith("I10")
); );
const hasGastritis = icd10Codes.some((code) => code.code.startsWith("K29")); const hasGastritis = icd10Codes.some((code) => code.code.startsWith("K29"));
const hasInfluenza = icd10Codes.some((code) => code.code.startsWith("J11")); const hasInfluenza = icd10Codes.some((code) => code.code.startsWith("J11"));
if (hasPneumonia) { if (hasPneumonia) {
bpjsMapping = { bpjsMapping = {
ina_cbg_code: "B-4-13-I", ina_cbg_code: "B-4-13-I",
description: "Pneumonia tanpa komplikasi", description: "Pneumonia tanpa komplikasi",
tariff: 3500000, tariff: 3500000,
}; };
} else if (hasDiabetes) { } else if (hasDiabetes) {
bpjsMapping = { bpjsMapping = {
ina_cbg_code: "E-4-10-I", ina_cbg_code: "E-4-10-I",
description: "Diabetes Mellitus tanpa komplikasi", description: "Diabetes Mellitus tanpa komplikasi",
tariff: 2100000, tariff: 2100000,
}; };
} else if (hasHypertension) { } else if (hasHypertension) {
bpjsMapping = { bpjsMapping = {
ina_cbg_code: "F-4-13-II", ina_cbg_code: "F-4-13-II",
description: "Hipertensi dengan komplikasi minor", description: "Hipertensi dengan komplikasi minor",
tariff: 1850000, tariff: 1850000,
}; };
} else if (hasGastritis) { } else if (hasGastritis) {
bpjsMapping = { bpjsMapping = {
ina_cbg_code: "G-4-10-I", ina_cbg_code: "G-4-10-I",
description: "Gastritis akut tanpa komplikasi", description: "Gastritis akut tanpa komplikasi",
tariff: 1200000, tariff: 1200000,
}; };
} else if (hasInfluenza) { } else if (hasInfluenza) {
bpjsMapping = { bpjsMapping = {
ina_cbg_code: "H-4-11-I", ina_cbg_code: "H-4-11-I",
description: "Influenza tanpa komplikasi", description: "Influenza tanpa komplikasi",
tariff: 950000, tariff: 950000,
}; };
} else { } else {
bpjsMapping = { bpjsMapping = {
ina_cbg_code: "Z-4-00-I", ina_cbg_code: "Z-4-00-I",
description: "Kondisi tidak spesifik", description: "Kondisi tidak spesifik",
tariff: 750000, tariff: 750000,
}; };
} }
await new Promise((resolve) => setTimeout(resolve, 1500)); // Simulate processing time await new Promise((resolve) => setTimeout(resolve, 1500)); // Simulate processing time
return { return {
icd_10_codes: icd10Codes.slice(0, 3), // Limit to top 3 icd_10_codes: icd10Codes.slice(0, 3), // Limit to top 3
procedure_codes: procedureCodes.slice(0, 3), // Limit to top 3 procedure_codes: procedureCodes.slice(0, 3), // Limit to top 3
bpjs_mapping: bpjsMapping, bpjs_mapping: bpjsMapping,
}; };
}; };
const runAssistPipeline = async () => { const runAssistPipeline = async () => {
setAssistLoading(true); setAssistLoading(true);
setAssistError(null); setAssistError(null);
setAssistResult(null); setAssistResult(null);
try { try {
const preprocessed = preprocessInput(assistInput); const preprocessed = preprocessInput(assistInput);
const result = await mockAssistPipeline(preprocessed); const result = await mockAssistPipeline(preprocessed);
setAssistResult(result); setAssistResult(result);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
setAssistError( setAssistError(
"Gagal menjalankan BPJS Assist Coding. Silakan coba lagi." "Gagal menjalankan BPJS Assist Coding. Silakan coba lagi."
); );
} finally { } finally {
setAssistLoading(false); setAssistLoading(false);
} }
}; };
return ( return (
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
{/* Header */} {/* Header */}
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<div className="p-2 bg-blue-100 rounded-lg"> <div className="p-2 bg-blue-100 rounded-lg">
<Code className="h-6 w-6 text-blue-600" /> <Code className="h-6 w-6 text-blue-600" />
</div> </div>
<div> <div>
<h1 className="text-2xl font-bold text-gray-900"> <h1 className="text-2xl font-bold text-gray-900">
Asisten Kodefikasi BPJS Asisten Kodefikasi BPJS
</h1> </h1>
<p className="text-gray-600"> <p className="text-gray-600">
Bantuan penentuan kode ICD-10, prosedur, dan mapping INA-CBGs untuk Bantuan penentuan kode ICD-10, prosedur, dan mapping INA-CBGs untuk
klaim BPJS klaim BPJS
</p> </p>
</div> </div>
</div> </div>
{/* Form Bantuan Kodefikasi */} {/* Form Bantuan Kodefikasi */}
<div className="bg-white rounded-lg shadow-sm border mb-6"> <div className="bg-white rounded-lg shadow-sm border mb-6">
<div className="p-6 border-b border-gray-200"> <div className="p-6 border-b border-gray-200">
<h3 className="text-lg font-medium text-gray-900 mb-4 flex items-center"> <h3 className="text-lg font-medium text-gray-900 mb-4 flex items-center">
<Wand2 className="h-5 w-5 mr-2 text-blue-600" /> <Wand2 className="h-5 w-5 mr-2 text-blue-600" />
Input Data Klinis Input Data Klinis
</h3> </h3>
<div className="grid grid-cols-1 gap-4"> <div className="grid grid-cols-1 gap-4">
<div> <div>
<label className="text-sm font-medium text-gray-700 mb-1 flex items-center"> <label className="text-sm font-medium text-gray-700 mb-1 flex items-center">
<ClipboardList className="h-4 w-4 mr-2 text-blue-600" /> <ClipboardList className="h-4 w-4 mr-2 text-blue-600" />
Catatan Medis / Anamnesis Catatan Medis / Anamnesis
</label> </label>
<textarea <textarea
rows={4} rows={4}
value={assistInput.clinicalNotes} value={assistInput.clinicalNotes}
onChange={(e) => onChange={(e) =>
setAssistInput((s) => ({ setAssistInput((s) => ({
...s, ...s,
clinicalNotes: e.target.value, clinicalNotes: e.target.value,
})) }))
} }
placeholder="Pasien mengeluh demam sejak 3 hari, batuk berdahak, sesak napas, nyeri dada..." placeholder="Pasien mengeluh demam sejak 3 hari, batuk berdahak, sesak napas, nyeri dada..."
className="w-full rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent p-3" className="w-full rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent p-3"
/> />
</div> </div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Hasil Laboratorium
</label>
<textarea
rows={3}
value={assistInput.labResults}
onChange={(e) =>
setAssistInput((s) => ({
...s,
labResults: e.target.value,
}))
}
placeholder="Leukosit: 15.000/µL, CRP: 25 mg/L, HbA1c: 7.8%, Glukosa Puasa: 180 mg/dL..."
className="w-full rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent p-3"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Tindakan / Prosedur yang Dilakukan
</label>
<input
type="text"
value={assistInput.procedures}
onChange={(e) =>
setAssistInput((s) => ({
...s,
procedures: e.target.value,
}))
}
placeholder="Rontgen thorax, darah lengkap, EKG, endoskopi lambung..."
className="w-full rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent p-3"
/>
</div>
<div className="flex items-center space-x-3">
<button
onClick={runAssistPipeline}
disabled={
assistLoading ||
(!assistInput.clinicalNotes &&
!assistInput.labResults &&
!assistInput.procedures)
}
className="flex items-center space-x-2 disabled:opacity-60 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg disabled:cursor-not-allowed"
>
<Wand2 className="h-4 w-4" />
<span>
{assistLoading ? "Memproses..." : "Generate Kode BPJS"}
</span>
</button>
</div>
{assistError && (
<div className="text-sm text-red-600 bg-red-50 border border-red-200 rounded-lg p-3">
{assistError}
</div>
)}
</div>
</div>
{/* Results Section */} <div>
<div className="p-6"> <label className="block text-sm font-medium text-gray-700 mb-1">
{!assistResult ? ( Tindakan / Prosedur yang Dilakukan
<div className="text-center py-12"> </label>
<AlertCircle className="mx-auto h-12 w-12 text-gray-400" /> <input
<h3 className="mt-2 text-sm font-medium text-gray-900"> type="text"
Belum ada hasil value={assistInput.procedures}
</h3> onChange={(e) =>
<p className="mt-1 text-sm text-gray-500"> setAssistInput((s) => ({
Isi input data klinis dan klik "Generate Kode BPJS" untuk ...s,
memulai. procedures: e.target.value,
</p> }))
</div> }
) : ( placeholder="Rontgen thorax, darah lengkap, EKG, endoskopi lambung..."
<div className="space-y-6"> className="w-full rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent p-3"
{/* ICD-10 Codes */} />
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4"> </div>
<h4 className="text-lg font-medium text-blue-900 mb-3 flex items-center"> <div className="flex items-center space-x-3">
<ShieldCheck className="h-5 w-5 mr-2" /> <button
Kode ICD-10 Diagnosis onClick={runAssistPipeline}
</h4> disabled={
<div className="space-y-3"> assistLoading ||
{assistResult.icd_10_codes.map((icd, index) => ( (!assistInput.clinicalNotes && !assistInput.procedures)
<div }
key={index} className="flex items-center space-x-2 disabled:opacity-60 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg disabled:cursor-not-allowed"
className="bg-white border border-blue-200 rounded-md p-3" >
> <Wand2 className="h-4 w-4" />
<div className="flex justify-between items-start"> <span>
<div className="flex-1"> {assistLoading ? "Memproses..." : "Generate Kode BPJS"}
<div className="font-semibold text-blue-900"> </span>
{icd.code} - {icd.description} </button>
</div> </div>
<div className="text-sm text-blue-700 mt-1"> {assistError && (
<strong>Alasan:</strong> {icd.reasoning} <div className="text-sm text-red-600 bg-red-50 border border-red-200 rounded-lg p-3">
</div> {assistError}
</div> </div>
</div> )}
</div> </div>
))} </div>
</div>
</div>
{/* Procedure Codes */} {/* Results Section */}
<div className="bg-green-50 border border-green-200 rounded-lg p-4"> <div className="p-6">
<h4 className="text-lg font-medium text-green-900 mb-3 flex items-center"> {!assistResult ? (
<FileText className="h-5 w-5 mr-2" /> <div className="text-center py-12">
Kode Prosedur <AlertCircle className="mx-auto h-12 w-12 text-gray-400" />
</h4> <h3 className="mt-2 text-sm font-medium text-gray-900">
<div className="space-y-3"> Belum ada hasil
{assistResult.procedure_codes.map((proc, index) => ( </h3>
<div <p className="mt-1 text-sm text-gray-500">
key={index} Isi input data klinis dan klik "Generate Kode BPJS" untuk
className="bg-white border border-green-200 rounded-md p-3" memulai.
> </p>
<div className="flex justify-between items-start"> </div>
<div className="flex-1"> ) : (
<div className="font-semibold text-green-900"> <div className="space-y-6">
{proc.code} - {proc.description} {/* ICD-10 Codes */}
</div> <div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div className="text-sm text-green-700 mt-1"> <h4 className="text-lg font-medium text-blue-900 mb-3 flex items-center">
<strong>Alasan:</strong> {proc.reasoning} <ShieldCheck className="h-5 w-5 mr-2" />
</div> Kode ICD-10 Diagnosis
</div> </h4>
</div> <div className="space-y-3">
</div> {assistResult.icd_10_codes.map((icd, index) => (
))} <div
</div> key={index}
</div> className="bg-white border border-blue-200 rounded-md p-3"
>
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="font-semibold text-blue-900">
{icd.code} - {icd.description}
</div>
<div className="text-sm text-blue-700 mt-1">
<strong>Alasan:</strong> {icd.reasoning}
</div>
</div>
</div>
</div>
))}
</div>
</div>
{/* BPJS Mapping */} {/* Procedure Codes */}
<div className="bg-purple-50 border border-purple-200 rounded-lg p-4"> <div className="bg-green-50 border border-green-200 rounded-lg p-4">
<h4 className="text-lg font-medium text-purple-900 mb-3"> <h4 className="text-lg font-medium text-green-900 mb-3 flex items-center">
Mapping INA-CBGs BPJS <FileText className="h-5 w-5 mr-2" />
</h4> Kode Prosedur
<div className="bg-white border border-purple-200 rounded-md p-4"> </h4>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4"> <div className="space-y-3">
<div> {assistResult.procedure_codes.map((proc, index) => (
<span className="text-sm font-medium text-gray-600"> <div
Kode INA-CBGs: key={index}
</span> className="bg-white border border-green-200 rounded-md p-3"
<p className="text-lg font-bold text-purple-900"> >
{assistResult.bpjs_mapping.ina_cbg_code} <div className="flex justify-between items-start">
</p> <div className="flex-1">
</div> <div className="font-semibold text-green-900">
<div> {proc.code} - {proc.description}
<span className="text-sm font-medium text-gray-600"> </div>
Deskripsi: <div className="text-sm text-green-700 mt-1">
</span> <strong>Alasan:</strong> {proc.reasoning}
<p className="text-gray-900"> </div>
{assistResult.bpjs_mapping.description} </div>
</p> </div>
</div> </div>
<div> ))}
<span className="text-sm font-medium text-gray-600"> </div>
Estimasi Tarif: </div>
</span>
<p className="text-lg font-bold text-green-600"> {/* BPJS Mapping */}
{formatCurrency(assistResult.bpjs_mapping.tariff)} <div className="bg-purple-50 border border-purple-200 rounded-lg p-4">
</p> <h4 className="text-lg font-medium text-purple-900 mb-3">
</div> Mapping INA-CBGs BPJS
</div> </h4>
</div> <div className="bg-white border border-purple-200 rounded-md p-4">
</div> <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
</div> <div>
)} <span className="text-sm font-medium text-gray-600">
</div> Kode INA-CBGs:
</div> </span>
</div> <p className="text-lg font-bold text-purple-900">
); {assistResult.bpjs_mapping.ina_cbg_code}
</p>
</div>
<div>
<span className="text-sm font-medium text-gray-600">
Deskripsi:
</span>
<p className="text-gray-900">
{assistResult.bpjs_mapping.description}
</p>
</div>
<div>
<span className="text-sm font-medium text-gray-600">
Estimasi Tarif:
</span>
<p className="text-lg font-bold text-green-600">
{formatCurrency(assistResult.bpjs_mapping.tariff)}
</p>
</div>
</div>
</div>
</div>
</div>
)}
</div>
</div>
</div>
);
} }

View File

@@ -6,94 +6,201 @@
DAFTAR ISI: DAFTAR ISI:
1. Clinical.tsx - Test Cases (Rekam Medis Klinis) 1. Clinical.tsx - Test Cases (Rekam Medis Klinis)
2. Administrative.tsx - Test Cases (Rekam Medis Administratif) 2. Administrative.tsx - Test Cases (Rekam Medis Administratif)
3. CostRecommendation.tsx - Test Cases (Rekomendasi Biaya) 3. BPJSCodeification.tsx - Test Cases (Asisten Kodefikasi BPJS)
4. BPJSCodeification.tsx - Test Cases (Kodefikasi BPJS) 4. CostRecommendation.tsx - Test Cases (Rekomendasi Biaya)
5. Instruksi Penggunaan Test Cases 5. Sample Data untuk Testing
6. Instruksi Penggunaan Test Cases
================================================================================ ================================================================================
1. CLINICAL.TSX - TEST CASES (REKAM MEDIS KLINIS) 1. CLINICAL.TSX - TEST CASES (REKAM MEDIS KLINIS)
================================================================================ ================================================================================
TEST CASE 1: PENCARIAN PASIEN LENGKAP SCHEMA PENCARIAN:
===================================== - Input tunggal: Nama Pasien / Nomor Rekam Medis / Nomor BPJS
- Tanggal Lahir (Opsional)
- Pencarian fleksibel dengan word matching
TEST CASE 1: PENCARIAN NAMA LENGKAP
==================================
Input Form: Input Form:
- Nama/Nomor RM/BPJS: Ahmad Budi Santoso - Nama/No. RM/No. BPJS: John Doe
- Tanggal Lahir: 15 Januari 1985 - Tanggal Lahir: 15 Juni 1985
Expected Result: Expected Result:
Menampilkan rekam medis lengkap dengan identitas pasien, riwayat medis, Menampilkan rekam medis lengkap dengan:
pemeriksaan fisik, diagnosis, rencana pengobatan, dan dokumen medis. - Identitas pasien (John Doe, MR2024001, BPJS: 0001234567890)
- Riwayat medis (Hipertensi 2020, Diabetes 2021)
- Pemeriksaan fisik dengan tanda vital
- Diagnosis ICD-10 (Unstable Angina - I20.0)
- Rencana pengobatan dan dokumen medis
TEST CASE 2: PENCARIAN DENGAN NOMOR REKAM MEDIS TEST CASE 2: PENCARIAN NAMA PARSIAL (WORD MATCHING)
==================================================
Input Form:
- Nama/No. RM/No. BPJS: john
- Tanggal Lahir: (kosong)
Expected Result:
Pasien "John Doe" ditemukan meskipun hanya input "john"
TEST CASE 3: PENCARIAN DENGAN NOMOR REKAM MEDIS
=============================================== ===============================================
Input Form: Input Form:
- Nama/Nomor RM/BPJS: MRN2024001 - Nama/No. RM/No. BPJS: MR2024001
- Tanggal Lahir: (kosong) - Tanggal Lahir: (kosong)
Expected Result: Expected Result:
Pasien ditemukan berdasarkan nomor rekam medis dengan data klinis lengkap. Pasien John Doe ditemukan berdasarkan nomor rekam medis
TEST CASE 3: PENCARIAN DENGAN NOMOR BPJS TEST CASE 4: PENCARIAN DENGAN NOMOR BPJS
========================================= ========================================
Input Form: Input Form:
- Nama/Nomor RM/BPJS: 000987654321 - Nama/No. RM/No. BPJS: 0001234567890
- Tanggal Lahir: 12 April 1985
Expected Result:
Verifikasi pasien dengan nomor BPJS dan tanggal lahir yang sesuai.
TEST CASE 4: PENCARIAN NAMA PARSIAL
===================================
Input Form:
- Nama/Nomor RM/BPJS: Sari
- Tanggal Lahir: (kosong) - Tanggal Lahir: (kosong)
Expected Result: Expected Result:
Hasil pencarian untuk pasien dengan nama yang mengandung "Sari". Pasien John Doe ditemukan berdasarkan nomor BPJS
TEST CASE 5: PENCARIAN CASE INSENSITIVE
======================================
Input Form:
- Nama/No. RM/No. BPJS: JOHN DOE
- Tanggal Lahir: (kosong)
Expected Result:
Pasien ditemukan meskipun menggunakan huruf kapital
================================================================================ ================================================================================
2. ADMINISTRATIVE.TSX - TEST CASES (REKAM MEDIS ADMINISTRATIF) 2. ADMINISTRATIVE.TSX - TEST CASES (REKAM MEDIS ADMINISTRATIF)
================================================================================ ================================================================================
TEST CASE 1: PENCARIAN PASIEN AKTIF SCHEMA PENCARIAN:
=================================== - Input tunggal: Nama Pasien, No. RM, atau No. BPJS
- Tanggal Lahir (Opsional)
- Flexible word matching untuk nama
TEST CASE 1: PENCARIAN PASIEN DENGAN STATUS APPROVED
====================================================
Input Form: Input Form:
- Nama/Nomor RM/BPJS: Siti Nurhaliza - Nama/No. RM/No. BPJS: Ahmad Budi Santoso
- Tanggal Lahir: 25 Juli 1990 - Tanggal Lahir: 10 Mei 1985
Expected Result: Expected Result:
Detail administratif menampilkan status klaim aktif, informasi pembayaran, Menampilkan data administratif:
dan riwayat kunjungan. - Nama: Ahmad Budi Santoso
- MRN: MRN2024001, BPJS: 0001234567890
- Status Klaim: Disetujui (hijau)
- Status Pembayaran: Lunas (hijau)
- Total Biaya: Rp 2.500.000
- Rawat Jalan di Poli Penyakit Dalam
TEST CASE 2: PENCARIAN PASIEN BPJS TEST CASE 2: PENCARIAN DENGAN NAMA PARSIAL
================================== ==========================================
Input Form: Input Form:
- Nama/Nomor RM/BPJS: 000123456789 - Nama/No. RM/No. BPJS: ahmad santoso
- Tanggal Lahir: (kosong) - Tanggal Lahir: (kosong)
Expected Result: Expected Result:
Data administratif pasien dengan status klaim BPJS dan detail pembayaran. Menemukan "Ahmad Budi Santoso" dengan flexible matching
TEST CASE 3: PASIEN KUNJUNGAN TERBARU TEST CASE 3: PENCARIAN PASIEN UNDER REVIEW
===================================== ==========================================
Input Form: Input Form:
- Nama/Nomor RM/BPJS: MRN2024002 - Nama/No. RM/No. BPJS: Siti Aminah
- Tanggal Lahir: 10 September 1978
Expected Result:
Pasien dengan kunjungan terbaru menampilkan status proses klaim saat ini.
TEST CASE 4: PASIEN DENGAN MULTIPLE KLAIM
=========================================
Input Form:
- Nama/Nomor RM/BPJS: Bambang Sutrisno
- Tanggal Lahir: (kosong) - Tanggal Lahir: (kosong)
Expected Result: Expected Result:
Pasien dengan beberapa klaim dan riwayat pembayaran. Menampilkan pasien dengan:
- Status Klaim: Sedang Ditinjau (biru)
- Status Pembayaran: Menunggu (kuning)
- Total Biaya: Rp 1.800.000
- Sisa Tagihan: Rp 1.800.000
TEST CASE 4: PENCARIAN PASIEN INPATIENT
=======================================
Input Form:
- Nama/No. RM/No. BPJS: Roberto Silva
- Tanggal Lahir: (kosong)
Expected Result:
Menampilkan pasien dengan:
- Rawat Inap - Kelas 2
- Status Pembayaran: Sebagian (orange)
- IGD Emergency case
================================================================================ ================================================================================
3. COSTRECOMMENDATION.TSX - TEST CASES (REKOMENDASI BIAYA) 3. BPJSCODIFICATION.TSX - TEST CASES (ASISTEN KODEFIKASI BPJS)
================================================================================
SCHEMA INPUT (UPDATED):
- Catatan Medis / Anamnesis (textarea)
- Tindakan / Prosedur yang Dilakukan (input)
- TIDAK ADA input Hasil Laboratorium (sudah dihapus)
TEST CASE 1: KASUS PNEUMONIA
============================
Input Form:
Catatan Medis / Anamnesis:
Pasien mengeluh demam sejak 3 hari, batuk berdahak, sesak napas, nyeri dada.
Pemeriksaan fisik ditemukan suara napas menurun di basal kanan. Pasien tampak sesak.
Tindakan / Prosedur:
Rontgen thorax, darah lengkap
Expected Result:
- ICD-10: J18.9 - Pneumonia dengan kuman tidak spesifik
- Prosedur: 87.44 - Foto Rontgen thorax, 90.59 - Pemeriksaan darah lengkap
- INA-CBG: B-4-13-I - Pneumonia tanpa komplikasi (Rp 3.500.000)
TEST CASE 2: KASUS DIABETES + HIPERTENSI
========================================
Input Form:
Catatan Medis / Anamnesis:
Pasien dengan riwayat diabetes mellitus dan hipertensi datang untuk kontrol rutin.
Keluhan poliuria, polidipsia. Tekanan darah tinggi 150/95 mmHg. Kadar gula darah tinggi.
Tindakan / Prosedur:
EKG, pemeriksaan HbA1c
Expected Result:
- ICD-10: E11.9 - Diabetes mellitus tipe 2, I10 - Hipertensi esensial
- Prosedur: 89.52 - Elektrokardiogram
- INA-CBG: E-4-10-I - Diabetes Mellitus tanpa komplikasi (Rp 2.100.000)
TEST CASE 3: KASUS GASTRITIS
============================
Input Form:
Catatan Medis / Anamnesis:
Pasien mengeluh nyeri perut, mual, muntah sejak kemarin. Nyeri seperti terbakar di ulu hati.
Tindakan / Prosedur:
Endoskopi lambung
Expected Result:
- ICD-10: K29.1 - Gastritis akut lainnya
- Prosedur: 45.13 - Esofagogastroduodenoskopi (EGD)
- INA-CBG: G-4-10-I - Gastritis akut tanpa komplikasi (Rp 1.200.000)
TEST CASE 4: KASUS TIDAK SPESIFIK
=================================
Input Form:
Catatan Medis / Anamnesis:
Pasien datang dengan keluhan umum tidak jelas
Tindakan / Prosedur:
Pemeriksaan rutin
Expected Result:
- ICD-10: R69 - Penyakit tidak spesifik
- Prosedur: 99213 - Kunjungan rawat jalan
- INA-CBG: Z-4-00-I - Kondisi tidak spesifik (Rp 750.000)
================================================================================
4. COSTRECOMMENDATION.TSX - TEST CASES (REKOMENDASI BIAYA)
================================================================================ ================================================================================
TEST CASE 1: KASUS DEMAM BERDARAH DENGUE TEST CASE 1: KASUS DEMAM BERDARAH DENGUE
@@ -118,8 +225,8 @@ Expected Result:
Analisis AI menunjukkan peringatan potensi overclaim untuk transfusi Analisis AI menunjukkan peringatan potensi overclaim untuk transfusi
dengan Hb > 7 g/dL threshold. dengan Hb > 7 g/dL threshold.
TEST CASE 2: KASUS PNEUMONIA TEST CASE 2: KASUS PNEUMONIA COMMUNITY
============================ ======================================
Input Form: Input Form:
Diagnosis Klinis: Diagnosis Klinis:
@@ -138,204 +245,153 @@ Tanggal Kunjungan Terakhir: 15 Agustus 2024
Expected Result: Expected Result:
Analisis biaya dengan koding ICD-10 yang tepat dan rekomendasi pengobatan. Analisis biaya dengan koding ICD-10 yang tepat dan rekomendasi pengobatan.
TEST CASE 3: KASUS DIABETES FOLLOW-UP
=====================================
Input Form:
Diagnosis Klinis:
Pasien laki-laki 55 tahun dengan diabetes mellitus tipe 2 kontrol
rutin. Keluhan poliuria, polidipsia ringan. GDS 180 mg/dL,
HbA1c 8.2%. TD 140/90 mmHg. Kaki: tidak ada ulkus, sensasi normal.
Prosedur/Tindakan:
Pemeriksaan HbA1c, GDS, urinalisis, konsultasi gizi,
edukasi diabetes, penyesuaian dosis metformin, kontrol rutin 1 bulan.
Tanggal Kunjungan Terakhir: 5 September 2024
Expected Result:
Peringatan interval kontrol untuk kunjungan dalam 30 hari,
koding manajemen diabetes yang tepat.
================================================================================ ================================================================================
4. BPJSCODIFICATION.TSX - TEST CASES (KODEFIKASI BPJS) 5. SAMPLE DATA UNTUK TESTING
================================================================================ ================================================================================
TEST CASE 1: KASUS EMERGENCY - APPENDICITIS AKUT CLINICAL PAGE - MOCK DATA:
================================================ =========================
Input Form: Patient Identity:
- Name: John Doe
- Medical Record Number: MR2024001
- BPJS Number: 0001234567890
- Birth Date: 1985-06-15
- Gender: Male
- Blood Type: O+
- Address: Jl. Sudirman No. 123, Jakarta Pusat
Keluhan Pasien: Medical History:
Nyeri perut kanan bawah sejak 12 jam yang lalu, mula-mula nyeri - Previous Diseases: Hypertension (2020), Diabetes Mellitus Type 2 (2021)
di epigastrium kemudian berpindah ke fossa iliaka dextra. - Allergies: Penicillin, Shellfish
Disertai mual, muntah 2x, demam subfebris. - Current Medications: Metformin 500mg, Lisinopril 10mg
Pemeriksaan Fisik: Primary Diagnosis: Unstable Angina (I20.0)
TD 110/70 mmHg, nadi 88x/menit, suhu 37.8°C. Abdomen: Secondary: Essential Hypertension (I10), Type 2 Diabetes (E11.9)
McBurney sign (+), Rovsing sign (+), defans muskuler (+)
regio iliaka dextra. Bising usus normal.
Pemeriksaan Penunjang: ADMINISTRATIVE PAGE - MOCK DATA:
Leukosit 12.500/μL dengan shift to left, USG abdomen: ===============================
appendix menebal dengan fluid collection, foto polos Patient 1:
abdomen dalam batas normal. - Name: Ahmad Budi Santoso
- Birth Date: 1985-05-10
- MRN: MRN2024001
- BPJS: 0001234567890
- Claim Status: approved
- Payment Status: paid
- Total Cost: 2,500,000
- Admission Type: outpatient
Diagnosis: Acute appendicitis Patient 2:
- Name: Siti Aminah
- Birth Date: 1990-11-22
- MRN: MRN2024002
- BPJS: 0009876543210
- Claim Status: under_review
- Payment Status: pending
- Total Cost: 1,800,000
- Admission Type: outpatient
Tindakan: Appendectomy laparoscopic Patient 3:
- Name: Roberto Silva
- Birth Date: 1978-03-15
- MRN: MRN2024003
- BPJS: 0003456789012
- Claim Status: pending
- Payment Status: partial
- Total Cost: 5,200,000
- Admission Type: inpatient
- Room Class: Kelas 2
Expected Result: BPJS CODIFICATION - DETECTION KEYWORDS:
ICD-10 K35.9, kode prosedur untuk laparoscopic appendectomy, ======================================
mapping INA-CBG dengan tarif bedah. Pneumonia Keywords: pneumonia, infiltrat, demam, batuk, sesak
→ ICD-10: J18.9, INA-CBG: B-4-13-I (Rp 3.500.000)
TEST CASE 2: KASUS MEDIS - KRISIS HIPERTENSI Hypertension Keywords: hipertensi, bp 150/95, tekanan darah tinggi
============================================ → ICD-10: I10, INA-CBG: F-4-13-II (Rp 1.850.000)
Input Form:
Keluhan Pasien: Diabetes Keywords: diabetes, hba1c, glukosa, gula darah
Nyeri kepala hebat sejak pagi, pandangan kabur, mual muntah. → ICD-10: E11.9, INA-CBG: E-4-10-I (Rp 2.100.000)
Pasien memiliki riwayat hipertensi tidak terkontrol,
tidak rutin minum obat.
Pemeriksaan Fisik: Gastritis Keywords: gastritis, nyeri perut, mual, muntah
Kesadaran compos mentis, TD 220/120 mmHg, nadi 95x/menit → ICD-10: K29.1, INA-CBG: G-4-10-I (Rp 1.200.000)
reguler, RR 20x/menit. Funduskopi: perdarahan retina (+),
papil edema (+). JVP tidak meningkat.
Pemeriksaan Penunjang: Procedure Keywords:
EKG: LVH dengan strain pattern, urinalisis: proteinuria +2, - rontgen, x-ray → 87.44 Foto Rontgen thorax
kreatinin 1.8 mg/dL, troponin negatif. Foto thorax: - darah lengkap, cbc → 90.59 Pemeriksaan darah lengkap
kardiomegali dengan pulmonary edema. - endoskopi → 45.13 Esofagogastroduodenoskopi
- ekg, ecg → 89.52 Elektrokardiogram
Diagnosis: Hypertensive emergency with target organ damage - usg, ultrasound → 88.76 USG abdomen
Tindakan: IV antihypertensive therapy, cardiac monitoring
Expected Result:
ICD-10 I16.1, kode prosedur untuk monitoring intensif,
INA-CBG untuk emergency hipertensi.
TEST CASE 3: KASUS OBSTETRIK - PERSALINAN NORMAL
================================================
Input Form:
Keluhan Pasien:
G2P1A0 usia kehamilan 39 minggu datang dengan keluhan
kontraksi uterus teratur sejak 6 jam yang lalu,
keluar lendir bercampur darah.
Pemeriksaan Fisik:
TD 120/80 mmHg, nadi 80x/menit, kontraksi uterus 3x/10 menit
lamanya 40 detik. VT: pembukaan 6 cm, ketuban (+),
presentasi kepala, hodge II.
Pemeriksaan Penunjang:
CTG: reactive, variabilitas baik, tidak ada deselerasi.
Hb 11.5 g/dL, golongan darah O Rh+, HbsAg non-reaktif,
VDRL non-reaktif.
Diagnosis: Term pregnancy in labor, vertex presentation
Tindakan: Spontaneous vaginal delivery, episiotomy
Expected Result:
ICD-10 O80, kode prosedur untuk persalinan dan episiotomy,
paket INA-CBG maternal.
TEST CASE 4: KASUS PEDIATRIK - BRONKIOLITIS
===========================================
Input Form:
Keluhan Pasien:
Bayi laki-laki 8 bulan dibawa orangtua dengan keluhan
sesak napas sejak 2 hari, batuk, demam ringan.
Riwayat pilek pada kakak 1 minggu sebelumnya.
Pemeriksaan Fisik:
BB 8 kg, suhu 37.5°C, RR 50x/menit, retraksi intercostal (+),
wheezing ekspirasi (+) bilateral, ronki basah halus (+).
SpO2 94% udara bebas.
Pemeriksaan Penunjang:
Foto thorax: hiperinflasi bilateral, peribronchial thickening.
Rapid test RSV positif. Leukosit 8.500/μL,
analisa gas darah normal.
Diagnosis: Acute bronchiolitis due to RSV
Tindakan: Oxygen therapy, bronchodilator nebulization, supportive care
Expected Result:
ICD-10 J21.0, kode prosedur untuk respiratory support,
tarif INA-CBG pediatrik.
================================================================================ ================================================================================
5. INSTRUKSI PENGGUNAAN TEST CASES 6. INSTRUKSI PENGGUNAAN TEST CASES
================================================================================ ================================================================================
PERUBAHAN SCHEMA TERBARU:
========================
1. ADMINISTRATIVE.TSX:
✅ Input tunggal untuk Nama/MRN/BPJS (bukan 3 field terpisah)
✅ Flexible word matching untuk nama (ahmad santoso → Ahmad Budi Santoso)
✅ Case insensitive search
✅ Dapat mencari dengan kata parsial
2. BPJSCODIFICATION.TSX:
✅ DIHAPUS: Input "Hasil Laboratorium"
✅ Hanya 2 input: Catatan Medis + Prosedur
✅ Interface lebih sederhana
3. CLINICAL.TSX:
✅ Tetap sama dengan pencarian fleksibel
CARA MELAKUKAN TESTING: CARA MELAKUKAN TESTING:
======================= =======================
1. CLINICAL.TSX: 1. CLINICAL.TSX:
- Masukkan data pasien sesuai test case - Input: "john" → Harus menemukan "John Doe"
- Klik tombol "Cari Rekam Medis" - Input: "MR2024001" → Harus menemukan pasien
- Verifikasi tampilan rekam medis lengkap - Input: "0001234567890" → Harus menemukan via BPJS
2. ADMINISTRATIVE.TSX: 2. ADMINISTRATIVE.TSX:
- Input data pasien - Input: "ahmad santoso" → Harus menemukan "Ahmad Budi Santoso"
- Klik tombol "Cari Rekam Medis" - Input: "siti" → Harus menemukan "Siti Aminah"
- Periksa informasi billing/klaim status - Input: "MRN2024001" → Harus menemukan via MRN
- Verifikasi flexible word matching
3. COSTRECOMMENDATION.TSX: 3. BPJSCODIFICATION.TSX:
- Masukkan diagnosis klinis (copy paste dari test case) - Hanya isi 2 field (bukan 3)
- Masukkan prosedur/tindakan - Test dengan keyword: "pneumonia, batuk, sesak"
- Pilih tanggal kunjungan terakhir - Prosedur: "rontgen thorax"
- Klik "Analisis Rekomendasi" - Verifikasi ICD-10 dan tarif yang benar
- Verifikasi analisis AI dan deteksi overclaim
4. BPJSCODIFICATION.TSX: 4. COSTRECOMMENDATION.TSX:
- Input data klinis lengkap sesuai test case - Copy paste diagnosis klinis lengkap
- Klik tombol untuk generate koding - Isi prosedur/tindakan
- Periksa hasil ICD-10, prosedur, dan mapping INA-CBG - Pilih tanggal kunjungan
- Verifikasi analisis AI
PERILAKU YANG DIHARAPKAN: TESTING CHECKLIST:
======================== ==================
✓ Search dengan nama parsial berhasil
✓ Search case insensitive berfungsi
✓ BPJS Codification hanya 2 input field
✓ Word matching fleksibel (ahmad santoso → Ahmad Budi Santoso)
✓ Loading spinner tampil
✓ Error handling untuk data tidak ditemukan
✓ Format mata uang Indonesia (Rp)
✓ Responsive di mobile dan desktop
✓ Konsistensi warna dan UI
✓ Loading Spinner: Tampil saat proses pencarian/analisis EXPECTED BEHAVIOR:
✓ Hasil Lengkap: Informasi pasien/analisis yang komprehensif ==================
Error Handling: Pesan yang tepat jika tidak ada hasil Pencarian TIDAK mengembalikan "tidak ada data" jika input valid
✓ Format Tanggal: Format Indonesia di seluruh aplikasi ✓ Flexible matching untuk nama dengan urutan kata berbeda
Status Tombol: Disabled/enabled yang tepat Interface yang sederhana dan user-friendly
Responsive: Berfungsi di berbagai ukuran layar Performance yang responsif
Bahasa Indonesia: Semua text dalam bahasa Indonesia Data yang konsisten dan realistis
✓ Tema Biru-Putih: Konsistensi warna tombol
COVERAGE TESTING:
================
✓ Kasus Emergency (Appendicitis)
✓ Kasus Medis (Hipertensi, Pneumonia, Diabetes)
✓ Kasus Bedah (Appendectomy)
✓ Kasus Obstetrik (Persalinan Normal)
✓ Kasus Pediatrik (Bronkiolitis)
✓ Pencarian Nama, Nomor RM, BPJS
✓ Analisis Biaya dan Overclaim
✓ Kodefikasi ICD-10 dan INA-CBG
================================================================================ ================================================================================
CATATAN PENTING: UPDATED: Desember 2024
================================================================================ VERSION: 2.0
CHANGES: Updated schema for unified search input and removed lab results field
1. Semua test case menggunakan data medis yang realistis
2. Format tanggal menggunakan bahasa Indonesia
3. Input diagnosis dan prosedur sudah disesuaikan dengan terminologi medis
4. Test case mencakup berbagai spesialisasi medis
5. Verifikasi response time dan user experience
6. Pastikan tidak ada error di console browser
7. Test di berbagai browser (Chrome, Firefox, Edge)
================================================================================
CREATED: Desember 2024
VERSION: 1.0
SYSTEM: Claim Guard Frontend - Medical Records Module SYSTEM: Claim Guard Frontend - Medical Records Module
================================================================================ ================================================================================