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

View File

@@ -1,472 +1,472 @@
import {
Users,
TrendingUp,
AlertTriangle,
Shield,
Database,
RefreshCw,
FileText,
Search,
Bell,
CheckCircle,
XCircle,
Clock,
ArrowRight,
Code,
Stethoscope,
Users,
TrendingUp,
AlertTriangle,
Shield,
Database,
RefreshCw,
FileText,
Search,
Bell,
CheckCircle,
XCircle,
Clock,
ArrowRight,
Code,
Stethoscope,
} from "lucide-react";
export default function Dashboard() {
// Stats berdasarkan fitur project yang ada
const stats = [
{
title: "BPJS Sync Status",
value: "18/25",
change: "72% berhasil",
changeColor: "text-green-600",
icon: RefreshCw,
color: "text-blue-600",
subtitle: "Sync hari ini",
},
{
title: "Medical Records",
value: "89",
change: "+12 hari ini",
changeColor: "text-green-600",
icon: FileText,
color: "text-green-600",
subtitle: "Record tersinkron",
},
{
title: "ICD Recommendations",
value: "23",
change: "87% confidence",
changeColor: "text-blue-600",
icon: Code,
color: "text-purple-600",
subtitle: "Generated today",
},
{
title: "Active Users",
value: "28/30",
change: "24 login hari ini",
changeColor: "text-green-600",
icon: Users,
color: "text-orange-600",
subtitle: "System users",
},
];
// Stats berdasarkan fitur project yang ada
const stats = [
{
title: "BPJS Sync Status",
value: "18/25",
change: "72% berhasil",
changeColor: "text-green-600",
icon: RefreshCw,
color: "text-blue-600",
subtitle: "Sync hari ini",
},
{
title: "Medical Records",
value: "89",
change: "+12 hari ini",
changeColor: "text-green-600",
icon: FileText,
color: "text-green-600",
subtitle: "Record tersinkron",
},
{
title: "ICD Recommendations",
value: "23",
change: "87% confidence",
changeColor: "text-blue-600",
icon: Code,
color: "text-purple-600",
subtitle: "Generated today",
},
{
title: "Active Users",
value: "28/30",
change: "24 login hari ini",
changeColor: "text-green-600",
icon: Users,
color: "text-orange-600",
subtitle: "System users",
},
];
return (
<div className="p-6">
<div className="max-w-7xl mx-auto">
{/* Welcome Section */}
<div className="mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-2">
Dashboard ClaimGuard
</h2>
<p className="text-gray-600">
Monitor BPJS operations, medical records, ICD recommendations, dan
sistem management secara real-time.
</p>
</div>
return (
<div className="p-6">
<div className="max-w-7xl mx-auto">
{/* Welcome Section */}
<div className="mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-2">
Dashboard ClaimGuard
</h2>
<p className="text-gray-600">
Monitor BPJS operations, medical records, ICD recommendations, dan
sistem management secara real-time.
</p>
</div>
{/* Alert/Warning Section */}
<div className="bg-white rounded-lg shadow-sm border mb-6 p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Bell className="h-5 w-5 text-red-500 mr-2" />
Peringatan & Notifikasi
</h3>
<span className="text-sm text-gray-500">Real-time alerts</span>
</div>
<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="flex items-start justify-between">
<div>
<div className="flex items-center">
<AlertTriangle className="h-4 w-4 text-red-600 mr-2" />
<span className="text-sm font-medium text-red-900">
Overclaim Detected
</span>
</div>
<p className="text-sm text-red-700 mt-1">
7 kasus interval &lt; 30 hari
</p>
<p className="text-xs text-red-600 mt-1">
ICD: I10, E11, J18.9
</p>
</div>
<button className="text-red-600 hover:text-red-800">
<ArrowRight className="h-4 w-4" />
</button>
</div>
</div>
{/* Alert/Warning Section */}
<div className="bg-white rounded-lg shadow-sm border mb-6 p-4 hidden">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Bell className="h-5 w-5 text-red-500 mr-2" />
Peringatan & Notifikasi
</h3>
<span className="text-sm text-gray-500">Real-time alerts</span>
</div>
<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="flex items-start justify-between">
<div>
<div className="flex items-center">
<AlertTriangle className="h-4 w-4 text-red-600 mr-2" />
<span className="text-sm font-medium text-red-900">
Overclaim Detected
</span>
</div>
<p className="text-sm text-red-700 mt-1">
7 kasus interval &lt; 30 hari
</p>
<p className="text-xs text-red-600 mt-1">
ICD: I10, E11, J18.9
</p>
</div>
<button className="text-red-600 hover:text-red-800">
<ArrowRight className="h-4 w-4" />
</button>
</div>
</div>
<div className="bg-orange-50 border border-orange-200 rounded-lg p-3">
<div className="flex items-start justify-between">
<div>
<div className="flex items-center">
<Clock className="h-4 w-4 text-orange-600 mr-2" />
<span className="text-sm font-medium text-orange-900">
Inconsistent Visits
</span>
</div>
<p className="text-sm text-orange-700 mt-1">
3 kontrol tidak konsisten
</p>
<p className="text-xs text-orange-600 mt-1">
Tipe vs alasan tidak sesuai
</p>
</div>
<button className="text-orange-600 hover:text-orange-800">
<ArrowRight className="h-4 w-4" />
</button>
</div>
</div>
<div className="bg-orange-50 border border-orange-200 rounded-lg p-3">
<div className="flex items-start justify-between">
<div>
<div className="flex items-center">
<Clock className="h-4 w-4 text-orange-600 mr-2" />
<span className="text-sm font-medium text-orange-900">
Inconsistent Visits
</span>
</div>
<p className="text-sm text-orange-700 mt-1">
3 kontrol tidak konsisten
</p>
<p className="text-xs text-orange-600 mt-1">
Tipe vs alasan tidak sesuai
</p>
</div>
<button className="text-orange-600 hover:text-orange-800">
<ArrowRight className="h-4 w-4" />
</button>
</div>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
<div className="flex items-start justify-between">
<div>
<div className="flex items-center">
<XCircle className="h-4 w-4 text-blue-600 mr-2" />
<span className="text-sm font-medium text-blue-900">
Sync Failures
</span>
</div>
<p className="text-sm text-blue-700 mt-1">
5 BPJS sync gagal
</p>
<p className="text-xs text-blue-600 mt-1">
Perlu retry manual
</p>
</div>
<button className="text-blue-600 hover:text-blue-800">
<ArrowRight className="h-4 w-4" />
</button>
</div>
</div>
</div>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
<div className="flex items-start justify-between">
<div>
<div className="flex items-center">
<XCircle className="h-4 w-4 text-blue-600 mr-2" />
<span className="text-sm font-medium text-blue-900">
Sync Failures
</span>
</div>
<p className="text-sm text-blue-700 mt-1">
5 BPJS sync gagal
</p>
<p className="text-xs text-blue-600 mt-1">
Perlu retry manual
</p>
</div>
<button className="text-blue-600 hover:text-blue-800">
<ArrowRight className="h-4 w-4" />
</button>
</div>
</div>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{stats.map((stat, index) => (
<div key={index} className="card p-6">
<div className="flex items-center justify-between mb-4">
<div className={`p-2 rounded-lg bg-gray-100 ${stat.color}`}>
<stat.icon className="h-5 w-5" />
</div>
<span className={`text-sm font-medium ${stat.changeColor}`}>
{stat.change}
</span>
</div>
<div>
<p className="text-2xl font-bold text-gray-900 mb-1">
{stat.value}
</p>
<p className="text-sm font-semibold text-gray-700 mb-1">
{stat.title}
</p>
<p className="text-xs text-gray-500">{stat.subtitle}</p>
</div>
</div>
))}
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8 hidden">
{stats.map((stat, index) => (
<div key={index} className="card p-6">
<div className="flex items-center justify-between mb-4">
<div className={`p-2 rounded-lg bg-gray-100 ${stat.color}`}>
<stat.icon className="h-5 w-5" />
</div>
<span className={`text-sm font-medium ${stat.changeColor}`}>
{stat.change}
</span>
</div>
<div>
<p className="text-2xl font-bold text-gray-900 mb-1">
{stat.value}
</p>
<p className="text-sm font-semibold text-gray-700 mb-1">
{stat.title}
</p>
<p className="text-xs text-gray-500">{stat.subtitle}</p>
</div>
</div>
))}
</div>
{/* Main Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
{/* BPJS Operations */}
<div className="card p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Database className="h-5 w-5 text-blue-600 mr-2" />
BPJS Operations
</h3>
<button className="text-sm text-blue-600 hover:text-blue-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button>
</div>
{/* Main Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6 hidden">
{/* BPJS Operations */}
<div className="card p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Database className="h-5 w-5 text-blue-600 mr-2" />
BPJS Operations
</h3>
<button className="text-sm text-blue-600 hover:text-blue-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<RefreshCw className="h-4 w-4 text-blue-600" />
<span className="text-sm font-medium text-blue-900">
Sync Status
</span>
</div>
<p className="text-sm text-blue-700">Berhasil: 18/25 (72%)</p>
<p className="text-xs text-blue-600">
Last sync: 15 Jan 2024 14:30
</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-blue-600">18</div>
<div className="text-xs text-blue-500">Sukses hari ini</div>
</div>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<RefreshCw className="h-4 w-4 text-blue-600" />
<span className="text-sm font-medium text-blue-900">
Sync Status
</span>
</div>
<p className="text-sm text-blue-700">Berhasil: 18/25 (72%)</p>
<p className="text-xs text-blue-600">
Last sync: 15 Jan 2024 14:30
</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-blue-600">18</div>
<div className="text-xs text-blue-500">Sukses hari ini</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Code className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium text-green-900">
Active Codes
</span>
</div>
<p className="text-sm text-green-700">156 kode BPJS aktif</p>
<p className="text-xs text-green-600">
8 kode baru bulan ini
</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-green-600">156</div>
<div className="text-xs text-green-500">Total kode</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Code className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium text-green-900">
Active Codes
</span>
</div>
<p className="text-sm text-green-700">156 kode BPJS aktif</p>
<p className="text-xs text-green-600">
8 kode baru bulan ini
</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-green-600">156</div>
<div className="text-xs text-green-500">Total kode</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-purple-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Shield className="h-4 w-4 text-purple-600" />
<span className="text-sm font-medium text-purple-900">
Assist Coding
</span>
</div>
<p className="text-sm text-purple-700">
45 coding assistance
</p>
<p className="text-xs text-purple-600">12 queries hari ini</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-purple-600">45</div>
<div className="text-xs text-purple-500">Total assist</div>
</div>
</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-purple-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Shield className="h-4 w-4 text-purple-600" />
<span className="text-sm font-medium text-purple-900">
Assist Coding
</span>
</div>
<p className="text-sm text-purple-700">
45 coding assistance
</p>
<p className="text-xs text-purple-600">12 queries hari ini</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-purple-600">45</div>
<div className="text-xs text-purple-500">Total assist</div>
</div>
</div>
</div>
</div>
{/* Medical Record Operations */}
<div className="card p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Stethoscope className="h-5 w-5 text-green-600 mr-2" />
Medical Records
</h3>
<button className="text-sm text-green-600 hover:text-green-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button>
</div>
{/* Medical Record Operations */}
<div className="card p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Stethoscope className="h-5 w-5 text-green-600 mr-2" />
Medical Records
</h3>
<button className="text-sm text-green-600 hover:text-green-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<CheckCircle className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium text-green-900">
Sync Records
</span>
</div>
<p className="text-sm text-green-700">
1,250 records tersinkron
</p>
<p className="text-xs text-green-600">89 records hari ini</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-green-600">89</div>
<div className="text-xs text-green-500">Hari ini</div>
</div>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<CheckCircle className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium text-green-900">
Sync Records
</span>
</div>
<p className="text-sm text-green-700">
1,250 records tersinkron
</p>
<p className="text-xs text-green-600">89 records hari ini</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-green-600">89</div>
<div className="text-xs text-green-500">Hari ini</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Search className="h-4 w-4 text-blue-600" />
<span className="text-sm font-medium text-blue-900">
Patient Search
</span>
</div>
<p className="text-sm text-blue-700">45 pencarian hari ini</p>
<p className="text-xs text-blue-600">84% success rate</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-blue-600">45</div>
<div className="text-xs text-blue-500">Searches</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Search className="h-4 w-4 text-blue-600" />
<span className="text-sm font-medium text-blue-900">
Patient Search
</span>
</div>
<p className="text-sm text-blue-700">45 pencarian hari ini</p>
<p className="text-xs text-blue-600">84% success rate</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-blue-600">45</div>
<div className="text-xs text-blue-500">Searches</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-orange-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<FileText className="h-4 w-4 text-orange-600" />
<span className="text-sm font-medium text-orange-900">
Latest Records
</span>
</div>
<p className="text-sm text-orange-700">
30 records dalam 30 hari
</p>
<p className="text-xs text-orange-600">Filter aktif</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-orange-600">30</div>
<div className="text-xs text-orange-500">Records</div>
</div>
</div>
</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-orange-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<FileText className="h-4 w-4 text-orange-600" />
<span className="text-sm font-medium text-orange-900">
Latest Records
</span>
</div>
<p className="text-sm text-orange-700">
30 records dalam 30 hari
</p>
<p className="text-xs text-orange-600">Filter aktif</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-orange-600">30</div>
<div className="text-xs text-orange-500">Records</div>
</div>
</div>
</div>
</div>
</div>
{/* Second Row */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* ICD Recommendations & Cost */}
<div className="card p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<TrendingUp className="h-5 w-5 text-purple-600 mr-2" />
ICD & Cost Management
</h3>
<button className="text-sm text-purple-600 hover:text-purple-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button>
</div>
{/* Second Row */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 hidden">
{/* ICD Recommendations & Cost */}
<div className="card p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<TrendingUp className="h-5 w-5 text-purple-600 mr-2" />
ICD & Cost Management
</h3>
<button className="text-sm text-purple-600 hover:text-purple-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button>
</div>
<div className="space-y-4">
{[
{
title: "ICD Recommendations",
value: "23",
subtitle: "Generated today",
detail: "87% avg confidence",
color: "text-purple-600",
bgColor: "bg-purple-50",
icon: Code,
},
{
title: "BPJS Mappings",
value: "19/23",
subtitle: "Successfully mapped",
detail: "83% mapping rate",
color: "text-blue-600",
bgColor: "bg-blue-50",
icon: Shield,
},
{
title: "Cost Analysis",
value: "12",
subtitle: "Exports today",
detail: "JSON format",
color: "text-green-600",
bgColor: "bg-green-50",
icon: TrendingUp,
},
].map((item, index) => (
<div
key={index}
className={`flex items-center justify-between p-3 ${item.bgColor} rounded-lg`}
>
<div>
<div className="flex items-center space-x-2 mb-1">
<item.icon className={`h-4 w-4 ${item.color}`} />
<span
className={`text-sm font-medium ${item.color
.replace("text-", "text-")
.replace("-600", "-900")}`}
>
{item.title}
</span>
</div>
<p
className={`text-sm ${item.color.replace(
"-600",
"-700"
)}`}
>
{item.subtitle}
</p>
<p className={`text-xs ${item.color}`}>{item.detail}</p>
</div>
<div className="text-right">
<div className={`text-lg font-bold ${item.color}`}>
{item.value}
</div>
</div>
</div>
))}
</div>
</div>
<div className="space-y-4">
{[
{
title: "ICD Recommendations",
value: "23",
subtitle: "Generated today",
detail: "87% avg confidence",
color: "text-purple-600",
bgColor: "bg-purple-50",
icon: Code,
},
{
title: "BPJS Mappings",
value: "19/23",
subtitle: "Successfully mapped",
detail: "83% mapping rate",
color: "text-blue-600",
bgColor: "bg-blue-50",
icon: Shield,
},
{
title: "Cost Analysis",
value: "12",
subtitle: "Exports today",
detail: "JSON format",
color: "text-green-600",
bgColor: "bg-green-50",
icon: TrendingUp,
},
].map((item, index) => (
<div
key={index}
className={`flex items-center justify-between p-3 ${item.bgColor} rounded-lg`}
>
<div>
<div className="flex items-center space-x-2 mb-1">
<item.icon className={`h-4 w-4 ${item.color}`} />
<span
className={`text-sm font-medium ${item.color
.replace("text-", "text-")
.replace("-600", "-900")}`}
>
{item.title}
</span>
</div>
<p
className={`text-sm ${item.color.replace(
"-600",
"-700"
)}`}
>
{item.subtitle}
</p>
<p className={`text-xs ${item.color}`}>{item.detail}</p>
</div>
<div className="text-right">
<div className={`text-lg font-bold ${item.color}`}>
{item.value}
</div>
</div>
</div>
))}
</div>
</div>
{/* System Management */}
<div className="card p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Users className="h-5 w-5 text-orange-600 mr-2" />
System Management
</h3>
<button className="text-sm text-orange-600 hover:text-orange-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button>
</div>
{/* System Management */}
<div className="card p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<Users className="h-5 w-5 text-orange-600 mr-2" />
System Management
</h3>
<button className="text-sm text-orange-600 hover:text-orange-800 flex items-center">
Lihat Detail <ArrowRight className="h-4 w-4 ml-1" />
</button>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-orange-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Users className="h-4 w-4 text-orange-600" />
<span className="text-sm font-medium text-orange-900">
User Management
</span>
</div>
<p className="text-sm text-orange-700">28/30 active users</p>
<p className="text-xs text-orange-600">24 login hari ini</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-orange-600">28</div>
<div className="text-xs text-orange-500">Active</div>
</div>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-orange-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Users className="h-4 w-4 text-orange-600" />
<span className="text-sm font-medium text-orange-900">
User Management
</span>
</div>
<p className="text-sm text-orange-700">28/30 active users</p>
<p className="text-xs text-orange-600">24 login hari ini</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-orange-600">28</div>
<div className="text-xs text-orange-500">Active</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-purple-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Shield className="h-4 w-4 text-purple-600" />
<span className="text-sm font-medium text-purple-900">
Role Management
</span>
</div>
<p className="text-sm text-purple-700">25 roles configured</p>
<p className="text-xs text-purple-600">
2 permissions updated
</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-purple-600">25</div>
<div className="text-xs text-purple-500">Roles</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-purple-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<Shield className="h-4 w-4 text-purple-600" />
<span className="text-sm font-medium text-purple-900">
Role Management
</span>
</div>
<p className="text-sm text-purple-700">25 roles configured</p>
<p className="text-xs text-purple-600">
2 permissions updated
</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-purple-600">25</div>
<div className="text-xs text-purple-500">Roles</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<CheckCircle className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium text-green-900">
System Health
</span>
</div>
<p className="text-sm text-green-700">99.8% uptime</p>
<p className="text-xs text-green-600">2.1% error rate</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-green-600">99.8%</div>
<div className="text-xs text-green-500">Uptime</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div>
<div className="flex items-center space-x-2 mb-1">
<CheckCircle className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium text-green-900">
System Health
</span>
</div>
<p className="text-sm text-green-700">99.8% uptime</p>
<p className="text-xs text-green-600">2.1% error rate</p>
</div>
<div className="text-right">
<div className="text-lg font-bold text-green-600">99.8%</div>
<div className="text-xs text-green-500">Uptime</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 {
Code,
AlertCircle,
Wand2,
ShieldCheck,
ClipboardList,
FileText,
Code,
AlertCircle,
Wand2,
ShieldCheck,
ClipboardList,
FileText,
} from "lucide-react";
interface AssistInput {
clinicalNotes: string;
labResults: string;
procedures: string;
clinicalNotes: string;
procedures: string;
}
interface ICD10Code {
code: string;
description: string;
reasoning: string;
code: string;
description: string;
reasoning: string;
}
interface ProcedureCode {
code: string;
description: string;
reasoning: string;
code: string;
description: string;
reasoning: string;
}
interface BPJSMapping {
ina_cbg_code: string;
description: string;
tariff: number;
ina_cbg_code: string;
description: string;
tariff: number;
}
interface AssistResult {
icd_10_codes: ICD10Code[];
procedure_codes: ProcedureCode[];
bpjs_mapping: BPJSMapping;
icd_10_codes: ICD10Code[];
procedure_codes: ProcedureCode[];
bpjs_mapping: BPJSMapping;
}
export default function BPJSCodeification() {
const [assistInput, setAssistInput] = useState<AssistInput>({
clinicalNotes: "",
labResults: "",
procedures: "",
});
const [assistLoading, setAssistLoading] = useState(false);
const [assistError, setAssistError] = useState<string | null>(null);
const [assistResult, setAssistResult] = useState<AssistResult | null>(null);
const [assistInput, setAssistInput] = useState<AssistInput>({
clinicalNotes: "",
procedures: "",
});
const [assistLoading, setAssistLoading] = useState(false);
const [assistError, setAssistError] = useState<string | null>(null);
const [assistResult, setAssistResult] = useState<AssistResult | null>(null);
const formatCurrency = (amount: number) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
minimumFractionDigits: 0,
}).format(amount);
const formatCurrency = (amount: number) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
minimumFractionDigits: 0,
}).format(amount);
// Mock pipeline with improved JSON output format
const preprocessInput = (input: AssistInput): AssistInput => {
const normalize = (s: string) => s.replace(/\s+/g, " ").trim();
return {
clinicalNotes: normalize(input.clinicalNotes),
labResults: normalize(input.labResults),
procedures: normalize(input.procedures),
};
};
// Mock pipeline with improved JSON output format
const preprocessInput = (input: AssistInput): AssistInput => {
const normalize = (s: string) => s.replace(/\s+/g, " ").trim();
return {
clinicalNotes: normalize(input.clinicalNotes),
procedures: normalize(input.procedures),
};
};
const mockAssistPipeline = async (
input: AssistInput
): Promise<AssistResult> => {
const text =
`${input.clinicalNotes} ${input.labResults} ${input.procedures}`.toLowerCase();
const mockAssistPipeline = async (
input: AssistInput
): Promise<AssistResult> => {
const text = `${input.clinicalNotes} ${input.procedures}`.toLowerCase();
const icd10Codes: ICD10Code[] = [];
const procedureCodes: ProcedureCode[] = [];
const icd10Codes: ICD10Code[] = [];
const procedureCodes: ProcedureCode[] = [];
// ICD-10 Code Detection
if (
text.includes("pneumonia") ||
text.includes("infiltrat") ||
text.includes("demam") ||
text.includes("batuk") ||
text.includes("sesak")
) {
icd10Codes.push({
code: "J18.9",
description: "Pneumonia dengan kuman tidak spesifik",
reasoning:
"Gejala demam, batuk, sesak napas, dan hasil pemeriksaan rontgen mengarah pada pneumonia.",
});
}
// ICD-10 Code Detection
if (
text.includes("pneumonia") ||
text.includes("infiltrat") ||
text.includes("demam") ||
text.includes("batuk") ||
text.includes("sesak")
) {
icd10Codes.push({
code: "J18.9",
description: "Pneumonia dengan kuman tidak spesifik",
reasoning:
"Gejala demam, batuk, sesak napas, dan hasil pemeriksaan rontgen mengarah pada pneumonia.",
});
}
if (
text.includes("hipertensi") ||
text.includes("bp 150/95") ||
text.includes("tekanan darah tinggi") ||
text.includes("hypertension")
) {
icd10Codes.push({
code: "I10",
description: "Hipertensi esensial (primer)",
reasoning:
"Temuan tekanan darah tinggi pada catatan klinis dan pemeriksaan fisik.",
});
}
if (
text.includes("hipertensi") ||
text.includes("bp 150/95") ||
text.includes("tekanan darah tinggi") ||
text.includes("hypertension")
) {
icd10Codes.push({
code: "I10",
description: "Hipertensi esensial (primer)",
reasoning:
"Temuan tekanan darah tinggi pada catatan klinis dan pemeriksaan fisik.",
});
}
if (
text.includes("diabetes") ||
text.includes("hba1c") ||
text.includes("glukosa") ||
text.includes("gula darah")
) {
icd10Codes.push({
code: "E11.9",
description: "Diabetes mellitus tipe 2 tanpa komplikasi",
reasoning:
"Hasil laboratorium menunjukkan peningkatan HbA1c dan glukosa darah yang mendukung diagnosis diabetes.",
});
}
if (
text.includes("diabetes") ||
text.includes("hba1c") ||
text.includes("glukosa") ||
text.includes("gula darah")
) {
icd10Codes.push({
code: "E11.9",
description: "Diabetes mellitus tipe 2 tanpa komplikasi",
reasoning:
"Hasil laboratorium menunjukkan peningkatan HbA1c dan glukosa darah yang mendukung diagnosis diabetes.",
});
}
if (
text.includes("gastritis") ||
text.includes("nyeri perut") ||
text.includes("mual") ||
text.includes("muntah")
) {
icd10Codes.push({
code: "K29.1",
description: "Gastritis akut lainnya",
reasoning:
"Gejala nyeri perut, mual, muntah mengarah pada gastritis akut.",
});
}
if (
text.includes("gastritis") ||
text.includes("nyeri perut") ||
text.includes("mual") ||
text.includes("muntah")
) {
icd10Codes.push({
code: "K29.1",
description: "Gastritis akut lainnya",
reasoning:
"Gejala nyeri perut, mual, muntah mengarah pada gastritis akut.",
});
}
if (
text.includes("influenza") ||
text.includes("flu") ||
text.includes("demam") ||
text.includes("sakit kepala")
) {
icd10Codes.push({
code: "J11.1",
description:
"Influenza dengan virus tidak teridentifikasi disertai manifestasi respiratori",
reasoning:
"Gejala demam, sakit kepala, dan manifestasi respiratori mengarah pada influenza.",
});
}
if (
text.includes("influenza") ||
text.includes("flu") ||
text.includes("demam") ||
text.includes("sakit kepala")
) {
icd10Codes.push({
code: "J11.1",
description:
"Influenza dengan virus tidak teridentifikasi disertai manifestasi respiratori",
reasoning:
"Gejala demam, sakit kepala, dan manifestasi respiratori mengarah pada influenza.",
});
}
// Procedure Code Detection
if (
text.includes("rontgen") ||
text.includes("x-ray") ||
text.includes("chest x-ray") ||
text.includes("foto thorax")
) {
procedureCodes.push({
code: "87.44",
description: "Foto Rontgen thorax, proyeksi tunggal",
reasoning: "Tindakan rontgen dada sesuai gejala respiratori.",
});
}
// Procedure Code Detection
if (
text.includes("rontgen") ||
text.includes("x-ray") ||
text.includes("chest x-ray") ||
text.includes("foto thorax")
) {
procedureCodes.push({
code: "87.44",
description: "Foto Rontgen thorax, proyeksi tunggal",
reasoning: "Tindakan rontgen dada sesuai gejala respiratori.",
});
}
if (
text.includes("darah lengkap") ||
text.includes("complete blood count") ||
text.includes("cbc") ||
text.includes("lab darah")
) {
procedureCodes.push({
code: "90.59",
description: "Pemeriksaan darah lengkap",
reasoning: "Pemeriksaan darah lengkap untuk mendukung diagnosis.",
});
}
if (
text.includes("darah lengkap") ||
text.includes("complete blood count") ||
text.includes("cbc") ||
text.includes("lab darah")
) {
procedureCodes.push({
code: "90.59",
description: "Pemeriksaan darah lengkap",
reasoning: "Pemeriksaan darah lengkap untuk mendukung diagnosis.",
});
}
if (
text.includes("endoskopi") ||
text.includes("endoscopy") ||
text.includes("gastroscopy")
) {
procedureCodes.push({
code: "45.13",
description: "Esofagogastroduodenoskopi (EGD)",
reasoning: "Tindakan endoskopi lambung untuk evaluasi gastritis.",
});
}
if (
text.includes("endoskopi") ||
text.includes("endoscopy") ||
text.includes("gastroscopy")
) {
procedureCodes.push({
code: "45.13",
description: "Esofagogastroduodenoskopi (EGD)",
reasoning: "Tindakan endoskopi lambung untuk evaluasi gastritis.",
});
}
if (
text.includes("ekg") ||
text.includes("ecg") ||
text.includes("elektrokardiogram")
) {
procedureCodes.push({
code: "89.52",
description: "Elektrokardiogram (EKG)",
reasoning: "Pemeriksaan EKG untuk evaluasi kardiovaskular.",
});
}
if (
text.includes("ekg") ||
text.includes("ecg") ||
text.includes("elektrokardiogram")
) {
procedureCodes.push({
code: "89.52",
description: "Elektrokardiogram (EKG)",
reasoning: "Pemeriksaan EKG untuk evaluasi kardiovaskular.",
});
}
if (
text.includes("usg") ||
text.includes("ultrasound") ||
text.includes("ultrasonografi")
) {
procedureCodes.push({
code: "88.76",
description: "USG abdomen dan retroperitoneum diagnostik",
reasoning: "Pemeriksaan USG abdomen untuk evaluasi organ dalam.",
});
}
if (
text.includes("usg") ||
text.includes("ultrasound") ||
text.includes("ultrasonografi")
) {
procedureCodes.push({
code: "88.76",
description: "USG abdomen dan retroperitoneum diagnostik",
reasoning: "Pemeriksaan USG abdomen untuk evaluasi organ dalam.",
});
}
// Default codes if nothing specific detected
if (icd10Codes.length === 0) {
icd10Codes.push({
code: "R69",
description: "Penyakit tidak spesifik",
reasoning:
"Tidak ada gejala spesifik yang terdeteksi, memerlukan klarifikasi klinis lebih lanjut.",
});
}
// Default codes if nothing specific detected
if (icd10Codes.length === 0) {
icd10Codes.push({
code: "R69",
description: "Penyakit tidak spesifik",
reasoning:
"Tidak ada gejala spesifik yang terdeteksi, memerlukan klarifikasi klinis lebih lanjut.",
});
}
if (procedureCodes.length === 0) {
procedureCodes.push({
code: "99213",
description: "Kunjungan rawat jalan",
reasoning: "Kunjungan rawat jalan untuk evaluasi medis.",
});
}
if (procedureCodes.length === 0) {
procedureCodes.push({
code: "99213",
description: "Kunjungan rawat jalan",
reasoning: "Kunjungan rawat jalan untuk evaluasi medis.",
});
}
// BPJS Mapping based on primary diagnosis
let bpjsMapping: BPJSMapping;
// BPJS Mapping based on primary diagnosis
let bpjsMapping: BPJSMapping;
const hasPneumonia = icd10Codes.some((code) => code.code.startsWith("J18"));
const hasDiabetes = icd10Codes.some((code) => code.code.startsWith("E11"));
const hasHypertension = icd10Codes.some((code) =>
code.code.startsWith("I10")
);
const hasGastritis = icd10Codes.some((code) => code.code.startsWith("K29"));
const hasInfluenza = icd10Codes.some((code) => code.code.startsWith("J11"));
const hasPneumonia = icd10Codes.some((code) => code.code.startsWith("J18"));
const hasDiabetes = icd10Codes.some((code) => code.code.startsWith("E11"));
const hasHypertension = icd10Codes.some((code) =>
code.code.startsWith("I10")
);
const hasGastritis = icd10Codes.some((code) => code.code.startsWith("K29"));
const hasInfluenza = icd10Codes.some((code) => code.code.startsWith("J11"));
if (hasPneumonia) {
bpjsMapping = {
ina_cbg_code: "B-4-13-I",
description: "Pneumonia tanpa komplikasi",
tariff: 3500000,
};
} else if (hasDiabetes) {
bpjsMapping = {
ina_cbg_code: "E-4-10-I",
description: "Diabetes Mellitus tanpa komplikasi",
tariff: 2100000,
};
} else if (hasHypertension) {
bpjsMapping = {
ina_cbg_code: "F-4-13-II",
description: "Hipertensi dengan komplikasi minor",
tariff: 1850000,
};
} else if (hasGastritis) {
bpjsMapping = {
ina_cbg_code: "G-4-10-I",
description: "Gastritis akut tanpa komplikasi",
tariff: 1200000,
};
} else if (hasInfluenza) {
bpjsMapping = {
ina_cbg_code: "H-4-11-I",
description: "Influenza tanpa komplikasi",
tariff: 950000,
};
} else {
bpjsMapping = {
ina_cbg_code: "Z-4-00-I",
description: "Kondisi tidak spesifik",
tariff: 750000,
};
}
if (hasPneumonia) {
bpjsMapping = {
ina_cbg_code: "B-4-13-I",
description: "Pneumonia tanpa komplikasi",
tariff: 3500000,
};
} else if (hasDiabetes) {
bpjsMapping = {
ina_cbg_code: "E-4-10-I",
description: "Diabetes Mellitus tanpa komplikasi",
tariff: 2100000,
};
} else if (hasHypertension) {
bpjsMapping = {
ina_cbg_code: "F-4-13-II",
description: "Hipertensi dengan komplikasi minor",
tariff: 1850000,
};
} else if (hasGastritis) {
bpjsMapping = {
ina_cbg_code: "G-4-10-I",
description: "Gastritis akut tanpa komplikasi",
tariff: 1200000,
};
} else if (hasInfluenza) {
bpjsMapping = {
ina_cbg_code: "H-4-11-I",
description: "Influenza tanpa komplikasi",
tariff: 950000,
};
} else {
bpjsMapping = {
ina_cbg_code: "Z-4-00-I",
description: "Kondisi tidak spesifik",
tariff: 750000,
};
}
await new Promise((resolve) => setTimeout(resolve, 1500)); // Simulate processing time
await new Promise((resolve) => setTimeout(resolve, 1500)); // Simulate processing time
return {
icd_10_codes: icd10Codes.slice(0, 3), // Limit to top 3
procedure_codes: procedureCodes.slice(0, 3), // Limit to top 3
bpjs_mapping: bpjsMapping,
};
};
return {
icd_10_codes: icd10Codes.slice(0, 3), // Limit to top 3
procedure_codes: procedureCodes.slice(0, 3), // Limit to top 3
bpjs_mapping: bpjsMapping,
};
};
const runAssistPipeline = async () => {
setAssistLoading(true);
setAssistError(null);
setAssistResult(null);
const runAssistPipeline = async () => {
setAssistLoading(true);
setAssistError(null);
setAssistResult(null);
try {
const preprocessed = preprocessInput(assistInput);
const result = await mockAssistPipeline(preprocessed);
setAssistResult(result);
} catch (e) {
console.error(e);
setAssistError(
"Gagal menjalankan BPJS Assist Coding. Silakan coba lagi."
);
} finally {
setAssistLoading(false);
}
};
try {
const preprocessed = preprocessInput(assistInput);
const result = await mockAssistPipeline(preprocessed);
setAssistResult(result);
} catch (e) {
console.error(e);
setAssistError(
"Gagal menjalankan BPJS Assist Coding. Silakan coba lagi."
);
} finally {
setAssistLoading(false);
}
};
return (
<div className="p-6 space-y-6">
{/* Header */}
<div className="flex items-center space-x-3">
<div className="p-2 bg-blue-100 rounded-lg">
<Code className="h-6 w-6 text-blue-600" />
</div>
<div>
<h1 className="text-2xl font-bold text-gray-900">
Asisten Kodefikasi BPJS
</h1>
<p className="text-gray-600">
Bantuan penentuan kode ICD-10, prosedur, dan mapping INA-CBGs untuk
klaim BPJS
</p>
</div>
</div>
return (
<div className="p-6 space-y-6">
{/* Header */}
<div className="flex items-center space-x-3">
<div className="p-2 bg-blue-100 rounded-lg">
<Code className="h-6 w-6 text-blue-600" />
</div>
<div>
<h1 className="text-2xl font-bold text-gray-900">
Asisten Kodefikasi BPJS
</h1>
<p className="text-gray-600">
Bantuan penentuan kode ICD-10, prosedur, dan mapping INA-CBGs untuk
klaim BPJS
</p>
</div>
</div>
{/* Form Bantuan Kodefikasi */}
<div className="bg-white rounded-lg shadow-sm border mb-6">
<div className="p-6 border-b border-gray-200">
<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" />
Input Data Klinis
</h3>
<div className="grid grid-cols-1 gap-4">
<div>
<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" />
Catatan Medis / Anamnesis
</label>
<textarea
rows={4}
value={assistInput.clinicalNotes}
onChange={(e) =>
setAssistInput((s) => ({
...s,
clinicalNotes: e.target.value,
}))
}
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"
/>
</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>
{/* Form Bantuan Kodefikasi */}
<div className="bg-white rounded-lg shadow-sm border mb-6">
<div className="p-6 border-b border-gray-200">
<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" />
Input Data Klinis
</h3>
<div className="grid grid-cols-1 gap-4">
<div>
<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" />
Catatan Medis / Anamnesis
</label>
<textarea
rows={4}
value={assistInput.clinicalNotes}
onChange={(e) =>
setAssistInput((s) => ({
...s,
clinicalNotes: e.target.value,
}))
}
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"
/>
</div>
{/* Results Section */}
<div className="p-6">
{!assistResult ? (
<div className="text-center py-12">
<AlertCircle className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">
Belum ada hasil
</h3>
<p className="mt-1 text-sm text-gray-500">
Isi input data klinis dan klik "Generate Kode BPJS" untuk
memulai.
</p>
</div>
) : (
<div className="space-y-6">
{/* ICD-10 Codes */}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<h4 className="text-lg font-medium text-blue-900 mb-3 flex items-center">
<ShieldCheck className="h-5 w-5 mr-2" />
Kode ICD-10 Diagnosis
</h4>
<div className="space-y-3">
{assistResult.icd_10_codes.map((icd, index) => (
<div
key={index}
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>
<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.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>
{/* Procedure Codes */}
<div className="bg-green-50 border border-green-200 rounded-lg p-4">
<h4 className="text-lg font-medium text-green-900 mb-3 flex items-center">
<FileText className="h-5 w-5 mr-2" />
Kode Prosedur
</h4>
<div className="space-y-3">
{assistResult.procedure_codes.map((proc, index) => (
<div
key={index}
className="bg-white border border-green-200 rounded-md p-3"
>
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="font-semibold text-green-900">
{proc.code} - {proc.description}
</div>
<div className="text-sm text-green-700 mt-1">
<strong>Alasan:</strong> {proc.reasoning}
</div>
</div>
</div>
</div>
))}
</div>
</div>
{/* Results Section */}
<div className="p-6">
{!assistResult ? (
<div className="text-center py-12">
<AlertCircle className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">
Belum ada hasil
</h3>
<p className="mt-1 text-sm text-gray-500">
Isi input data klinis dan klik "Generate Kode BPJS" untuk
memulai.
</p>
</div>
) : (
<div className="space-y-6">
{/* ICD-10 Codes */}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<h4 className="text-lg font-medium text-blue-900 mb-3 flex items-center">
<ShieldCheck className="h-5 w-5 mr-2" />
Kode ICD-10 Diagnosis
</h4>
<div className="space-y-3">
{assistResult.icd_10_codes.map((icd, index) => (
<div
key={index}
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 */}
<div className="bg-purple-50 border border-purple-200 rounded-lg p-4">
<h4 className="text-lg font-medium text-purple-900 mb-3">
Mapping INA-CBGs BPJS
</h4>
<div className="bg-white border border-purple-200 rounded-md p-4">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
<div>
<span className="text-sm font-medium text-gray-600">
Kode INA-CBGs:
</span>
<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>
);
{/* Procedure Codes */}
<div className="bg-green-50 border border-green-200 rounded-lg p-4">
<h4 className="text-lg font-medium text-green-900 mb-3 flex items-center">
<FileText className="h-5 w-5 mr-2" />
Kode Prosedur
</h4>
<div className="space-y-3">
{assistResult.procedure_codes.map((proc, index) => (
<div
key={index}
className="bg-white border border-green-200 rounded-md p-3"
>
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="font-semibold text-green-900">
{proc.code} - {proc.description}
</div>
<div className="text-sm text-green-700 mt-1">
<strong>Alasan:</strong> {proc.reasoning}
</div>
</div>
</div>
</div>
))}
</div>
</div>
{/* BPJS Mapping */}
<div className="bg-purple-50 border border-purple-200 rounded-lg p-4">
<h4 className="text-lg font-medium text-purple-900 mb-3">
Mapping INA-CBGs BPJS
</h4>
<div className="bg-white border border-purple-200 rounded-md p-4">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
<div>
<span className="text-sm font-medium text-gray-600">
Kode INA-CBGs:
</span>
<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:
1. Clinical.tsx - Test Cases (Rekam Medis Klinis)
2. Administrative.tsx - Test Cases (Rekam Medis Administratif)
3. CostRecommendation.tsx - Test Cases (Rekomendasi Biaya)
4. BPJSCodeification.tsx - Test Cases (Kodefikasi BPJS)
5. Instruksi Penggunaan Test Cases
3. BPJSCodeification.tsx - Test Cases (Asisten Kodefikasi BPJS)
4. CostRecommendation.tsx - Test Cases (Rekomendasi Biaya)
5. Sample Data untuk Testing
6. Instruksi Penggunaan Test Cases
================================================================================
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:
- Nama/Nomor RM/BPJS: Ahmad Budi Santoso
- Tanggal Lahir: 15 Januari 1985
- Nama/No. RM/No. BPJS: John Doe
- Tanggal Lahir: 15 Juni 1985
Expected Result:
Menampilkan rekam medis lengkap dengan identitas pasien, riwayat medis,
pemeriksaan fisik, diagnosis, rencana pengobatan, dan dokumen medis.
Menampilkan rekam medis lengkap dengan:
- 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:
- Nama/Nomor RM/BPJS: MRN2024001
- Nama/No. RM/No. BPJS: MR2024001
- Tanggal Lahir: (kosong)
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:
- Nama/Nomor RM/BPJS: 000987654321
- 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
- Nama/No. RM/No. BPJS: 0001234567890
- Tanggal Lahir: (kosong)
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)
================================================================================
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:
- Nama/Nomor RM/BPJS: Siti Nurhaliza
- Tanggal Lahir: 25 Juli 1990
- Nama/No. RM/No. BPJS: Ahmad Budi Santoso
- Tanggal Lahir: 10 Mei 1985
Expected Result:
Detail administratif menampilkan status klaim aktif, informasi pembayaran,
dan riwayat kunjungan.
Menampilkan data administratif:
- 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:
- Nama/Nomor RM/BPJS: 000123456789
- Nama/No. RM/No. BPJS: ahmad santoso
- Tanggal Lahir: (kosong)
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:
- Nama/Nomor RM/BPJS: MRN2024002
- 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
- Nama/No. RM/No. BPJS: Siti Aminah
- Tanggal Lahir: (kosong)
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
@@ -118,8 +225,8 @@ Expected Result:
Analisis AI menunjukkan peringatan potensi overclaim untuk transfusi
dengan Hb > 7 g/dL threshold.
TEST CASE 2: KASUS PNEUMONIA
============================
TEST CASE 2: KASUS PNEUMONIA COMMUNITY
======================================
Input Form:
Diagnosis Klinis:
@@ -138,204 +245,153 @@ Tanggal Kunjungan Terakhir: 15 Agustus 2024
Expected Result:
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
================================================
Input Form:
CLINICAL PAGE - MOCK DATA:
=========================
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:
Nyeri perut kanan bawah sejak 12 jam yang lalu, mula-mula nyeri
di epigastrium kemudian berpindah ke fossa iliaka dextra.
Disertai mual, muntah 2x, demam subfebris.
Medical History:
- Previous Diseases: Hypertension (2020), Diabetes Mellitus Type 2 (2021)
- Allergies: Penicillin, Shellfish
- Current Medications: Metformin 500mg, Lisinopril 10mg
Pemeriksaan Fisik:
TD 110/70 mmHg, nadi 88x/menit, suhu 37.8°C. Abdomen:
McBurney sign (+), Rovsing sign (+), defans muskuler (+)
regio iliaka dextra. Bising usus normal.
Primary Diagnosis: Unstable Angina (I20.0)
Secondary: Essential Hypertension (I10), Type 2 Diabetes (E11.9)
Pemeriksaan Penunjang:
Leukosit 12.500/μL dengan shift to left, USG abdomen:
appendix menebal dengan fluid collection, foto polos
abdomen dalam batas normal.
ADMINISTRATIVE PAGE - MOCK DATA:
===============================
Patient 1:
- 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:
ICD-10 K35.9, kode prosedur untuk laparoscopic appendectomy,
mapping INA-CBG dengan tarif bedah.
BPJS CODIFICATION - DETECTION KEYWORDS:
======================================
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
============================================
Input Form:
Hypertension Keywords: hipertensi, bp 150/95, tekanan darah tinggi
→ ICD-10: I10, INA-CBG: F-4-13-II (Rp 1.850.000)
Keluhan Pasien:
Nyeri kepala hebat sejak pagi, pandangan kabur, mual muntah.
Pasien memiliki riwayat hipertensi tidak terkontrol,
tidak rutin minum obat.
Diabetes Keywords: diabetes, hba1c, glukosa, gula darah
→ ICD-10: E11.9, INA-CBG: E-4-10-I (Rp 2.100.000)
Pemeriksaan Fisik:
Kesadaran compos mentis, TD 220/120 mmHg, nadi 95x/menit
reguler, RR 20x/menit. Funduskopi: perdarahan retina (+),
papil edema (+). JVP tidak meningkat.
Gastritis Keywords: gastritis, nyeri perut, mual, muntah
→ ICD-10: K29.1, INA-CBG: G-4-10-I (Rp 1.200.000)
Pemeriksaan Penunjang:
EKG: LVH dengan strain pattern, urinalisis: proteinuria +2,
kreatinin 1.8 mg/dL, troponin negatif. Foto thorax:
kardiomegali dengan pulmonary edema.
Diagnosis: Hypertensive emergency with target organ damage
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.
Procedure Keywords:
- rontgen, x-ray → 87.44 Foto Rontgen thorax
- darah lengkap, cbc → 90.59 Pemeriksaan darah lengkap
- endoskopi → 45.13 Esofagogastroduodenoskopi
- ekg, ecg → 89.52 Elektrokardiogram
- usg, ultrasound → 88.76 USG abdomen
================================================================================
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:
=======================
1. CLINICAL.TSX:
- Masukkan data pasien sesuai test case
- Klik tombol "Cari Rekam Medis"
- Verifikasi tampilan rekam medis lengkap
- Input: "john" → Harus menemukan "John Doe"
- Input: "MR2024001" → Harus menemukan pasien
- Input: "0001234567890" → Harus menemukan via BPJS
2. ADMINISTRATIVE.TSX:
- Input data pasien
- Klik tombol "Cari Rekam Medis"
- Periksa informasi billing/klaim status
- Input: "ahmad santoso" → Harus menemukan "Ahmad Budi Santoso"
- Input: "siti" → Harus menemukan "Siti Aminah"
- Input: "MRN2024001" → Harus menemukan via MRN
- Verifikasi flexible word matching
3. COSTRECOMMENDATION.TSX:
- Masukkan diagnosis klinis (copy paste dari test case)
- Masukkan prosedur/tindakan
- Pilih tanggal kunjungan terakhir
- Klik "Analisis Rekomendasi"
- Verifikasi analisis AI dan deteksi overclaim
3. BPJSCODIFICATION.TSX:
- Hanya isi 2 field (bukan 3)
- Test dengan keyword: "pneumonia, batuk, sesak"
- Prosedur: "rontgen thorax"
- Verifikasi ICD-10 dan tarif yang benar
4. BPJSCODIFICATION.TSX:
- Input data klinis lengkap sesuai test case
- Klik tombol untuk generate koding
- Periksa hasil ICD-10, prosedur, dan mapping INA-CBG
4. COSTRECOMMENDATION.TSX:
- Copy paste diagnosis klinis lengkap
- Isi prosedur/tindakan
- 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
✓ Hasil Lengkap: Informasi pasien/analisis yang komprehensif
Error Handling: Pesan yang tepat jika tidak ada hasil
✓ Format Tanggal: Format Indonesia di seluruh aplikasi
Status Tombol: Disabled/enabled yang tepat
Responsive: Berfungsi di berbagai ukuran layar
Bahasa Indonesia: Semua text dalam bahasa Indonesia
✓ 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
EXPECTED BEHAVIOR:
==================
Pencarian TIDAK mengembalikan "tidak ada data" jika input valid
✓ Flexible matching untuk nama dengan urutan kata berbeda
Interface yang sederhana dan user-friendly
Performance yang responsif
Data yang konsisten dan realistis
================================================================================
CATATAN PENTING:
================================================================================
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
UPDATED: Desember 2024
VERSION: 2.0
CHANGES: Updated schema for unified search input and removed lab results field
SYSTEM: Claim Guard Frontend - Medical Records Module
================================================================================
================================================================================