พัฒนา Frontend เมนู แดชบอร์ด/ภาพรวม & คู่มือการใช้งานเบื้องต้น
This commit is contained in:
parent
5e9e0da972
commit
3a4d6c245c
@ -39,10 +39,10 @@ const routes = [
|
||||
requiredRole: ['viewer', 'admin', 'manager'],
|
||||
},
|
||||
{
|
||||
path: '/dashboard/settings',
|
||||
path: '/dashboard/guide',
|
||||
icon: <FaCog className="w-5 h-5 flex-shrink-0" />,
|
||||
name: 'ตั้งค่าระบบ',
|
||||
requiredRole: ['admin'],
|
||||
name: 'คู่มือการใช้งาน (Guide)',
|
||||
requiredRole: ['viewer', 'manager', 'admin'],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -1,25 +1,143 @@
|
||||
import React from 'react';
|
||||
import TitleCard from '../components/TitleCard';
|
||||
import { useModelList } from '../services/modelRegistryApi';
|
||||
import { useSystemHealth } from '../services/healthApi';
|
||||
import { useInferenceSummary } from '../services/auditApi';
|
||||
|
||||
function Dashboard() {
|
||||
const TopButtons = (
|
||||
<button className="btn btn-primary btn-sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-5 h-5 mr-2"><path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" /></svg>
|
||||
สร้างรายงาน
|
||||
</button>
|
||||
import { FaPlay, FaDatabase, FaClock, FaCheckCircle, FaTimesCircle, FaPercent, FaSyncAlt } from 'react-icons/fa';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Helper Component: สรุปตัวเลขสถิติ (Stats)
|
||||
// ----------------------------------------------------
|
||||
const StatCard = ({ icon, title, value, unit, colorClass, linkPath, isLoading }) => {
|
||||
|
||||
// แสดง loading spinner ถ้ากำลังโหลดข้อมูล
|
||||
const displayValue = isLoading ? (
|
||||
<span className="loading loading-spinner loading-md text-primary"></span>
|
||||
) : (
|
||||
value
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
{/* ใช้งาน TitleCard พร้อมปุ่มด้านบน */}
|
||||
<TitleCard title="ภาพรวมประสิทธิภาพระบบ" TopSideButtons={TopButtons} topMargin="mt-0">
|
||||
<p className="text-gray-600">
|
||||
แสดงสถิติสำคัญและ Model ทั้งหมด
|
||||
<div className="stats shadow-lg bg-base-100 hover:bg-base-200 transition duration-150">
|
||||
<div className="stat">
|
||||
<div className={`stat-figure text-secondary ${colorClass}`}>
|
||||
{icon}
|
||||
</div>
|
||||
<div className="stat-title text-base-content/70">{title}</div>
|
||||
<div className="stat-value text-base-content">{displayValue} <span className="text-sm font-normal">{unit}</span></div>
|
||||
{linkPath && (
|
||||
<div className="stat-desc mt-2">
|
||||
<Link to={linkPath} className="link link-hover text-primary hover:underline">
|
||||
ดูรายละเอียด
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function Dashboard() {
|
||||
// Hooks สำหรับดึงข้อมูลสถานะ
|
||||
const { data: models, isLoading: modelsLoading } = useModelList();
|
||||
const { data: health, isLoading: healthLoading, isFetching: healthFetching } = useSystemHealth();
|
||||
const { data: summary, isLoading: summaryLoading } = useInferenceSummary();
|
||||
|
||||
// --- Logic คำนวณ ---
|
||||
const activeModelCount = models?.filter(m => m.status === 'ACTIVE').length || 0;
|
||||
const totalModelCount = models?.length || 0;
|
||||
|
||||
// Health Status
|
||||
const healthStatus = health?.status || (healthLoading ? 'CHECKING' : 'UNKNOWN');
|
||||
const isHealthy = healthStatus === 'Healthy';
|
||||
const healthIcon = isHealthy ? <FaCheckCircle /> : <FaTimesCircle />;
|
||||
const healthColor = isHealthy ? 'text-success' : 'text-error';
|
||||
|
||||
// Inference Stats (จาก useInferenceSummary)
|
||||
const totalRuns = summary?.total_runs || 0;
|
||||
const successRate = summary?.success_rate || 0;
|
||||
const avgLatencyMs = summary?.avg_latency_ms || 0;
|
||||
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<h1 className="text-3xl font-bold text-base-content">แดชบอร์ด/ภาพรวม</h1>
|
||||
|
||||
{/* 1. ส่วนแสดงสถิติสำคัญ (KPI Cards) */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
|
||||
{/* Card 1: Model Active Count */}
|
||||
<StatCard
|
||||
icon={<FaDatabase />}
|
||||
title="Model พร้อมรัน (ACTIVE)"
|
||||
value={activeModelCount}
|
||||
unit={`จาก ${totalModelCount}`}
|
||||
colorClass="text-success"
|
||||
linkPath="/dashboard/model-registry"
|
||||
isLoading={modelsLoading}
|
||||
/>
|
||||
|
||||
{/* Card 2: Total Inference Runs (Audit Log) */}
|
||||
<StatCard
|
||||
icon={<FaPlay />}
|
||||
title="งานรันทั้งหมด (24 ชม.)"
|
||||
value={totalRuns}
|
||||
unit="ครั้ง"
|
||||
colorClass="text-info"
|
||||
isLoading={summaryLoading}
|
||||
// Note: ต้องมีเมนู Pipeline Logs หรือ Audit Log List
|
||||
/>
|
||||
|
||||
{/* Card 3: Success Rate (Audit Log) */}
|
||||
<StatCard
|
||||
icon={<FaPercent />}
|
||||
title="Success Rate (24 ชม.)"
|
||||
value={successRate}
|
||||
unit="%"
|
||||
colorClass={successRate > 90 ? "text-success" : "text-warning"}
|
||||
isLoading={summaryLoading}
|
||||
/>
|
||||
|
||||
{/* Card 4: Avg Latency (Audit Log) */}
|
||||
<StatCard
|
||||
icon={<FaClock />}
|
||||
title="Latency เฉลี่ย (AI)"
|
||||
value={avgLatencyMs}
|
||||
unit="ms"
|
||||
colorClass={avgLatencyMs > 5000 ? "text-error" : "text-warning"}
|
||||
isLoading={summaryLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 2. ส่วนแสดงสถานะ Infrastructure (Health) */}
|
||||
<TitleCard title="สถานะ Infrastructure & Health" topMargin="mt-0"
|
||||
TopSideButtons={healthFetching ? <FaSyncAlt className="animate-spin text-primary" title="Refreshing..." /> : null}
|
||||
>
|
||||
<StatCard
|
||||
icon={healthIcon}
|
||||
title="สถานะรวมของระบบ"
|
||||
value={healthStatus}
|
||||
unit={healthLoading ? "(กำลังตรวจสอบ...)" : ""}
|
||||
colorClass={healthColor}
|
||||
linkPath="/dashboard/health"
|
||||
isLoading={healthLoading}
|
||||
/>
|
||||
|
||||
<p className="text-xs text-base-content/60 mt-4">
|
||||
ตรวจสอบสถานะของ DB, Redis, MinIO, และ AI Model Endpoints ได้ที่เมนูสถานะระบบ
|
||||
</p>
|
||||
{/* เพิ่มส่วนของการแสดง Metric สำคัญ เช่น Stats Component */}
|
||||
</TitleCard>
|
||||
|
||||
{/* 3. ส่วนแสดง Log ล่าสุด (Audit Log) */}
|
||||
<TitleCard title="Recent MLOps Events (Audit Log)">
|
||||
<pre className="text-sm bg-base-200 p-3 rounded max-h-40 overflow-auto">
|
||||
{summaryLoading ? "กำลังโหลด Log ล่าสุด..." : JSON.stringify(summary?.last_logs || [], null, 2)}
|
||||
</pre>
|
||||
</TitleCard>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Dashboard;
|
||||
export default Dashboard;
|
||||
77
web/src/pages/system/UserGuide.jsx
Normal file
77
web/src/pages/system/UserGuide.jsx
Normal file
@ -0,0 +1,77 @@
|
||||
// src/pages/Guide/UserGuide.jsx
|
||||
import React from 'react';
|
||||
import { FaUpload, FaFlask, FaDatabase, FaServer, FaBook } from 'react-icons/fa';
|
||||
import TitleCard from '../../components/TitleCard';
|
||||
|
||||
export default function UserGuide() {
|
||||
return (
|
||||
<TitleCard
|
||||
title="คู่มือการใช้งาน (Reference Guide)"
|
||||
topMargin="mt-0"
|
||||
TopSideButtons={<FaBook className="w-5 h-5 text-warning" />}
|
||||
>
|
||||
{/* SINGLE COLUMN LAYOUT */}
|
||||
<main className="w-full">
|
||||
<div className="prose max-w-none">
|
||||
|
||||
{/* ภาพรวม */}
|
||||
<section id="overview" className="pb-4 mb-6 scroll-mt-28">
|
||||
<h2 className="text-2xl font-bold">ภาพรวมระบบ DDO Console</h2>
|
||||
<p className="mt-2">
|
||||
DDO Console ถูกออกแบบมาเพื่อเป็น <b>MLOps Gateway</b> สำหรับจัดการวงจรชีวิต
|
||||
ของ AI Model ทางการแพทย์ (Medical Imaging AI) โดยเฉพาะ...
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Model Registry */}
|
||||
<section id="registry" className="pb-4 mb-6 scroll-mt-28">
|
||||
<h2 className="text-2xl font-bold flex items-center">
|
||||
<FaDatabase className="mr-3 text-warning" /> 1. Model Registry & Control
|
||||
</h2>
|
||||
|
||||
<ul className="list-disc list-inside space-y-2 ml-4 text-sm text-base-content/90">
|
||||
<li>ไปที่เมนู <b>"Model Registry & Control"</b></li>
|
||||
<li><b>การลงทะเบียน:</b> ต้องกรอก Base URL และ Inference Path</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* Inference */}
|
||||
<section id="inference" className="pb-4 mb-6 scroll-mt-28">
|
||||
<h2 className="text-2xl font-bold flex items-center">
|
||||
<FaFlask className="mr-3 text-info" /> 2. AI Inference (Run)
|
||||
</h2>
|
||||
|
||||
<ul className="list-disc list-inside space-y-2 ml-4 text-sm text-base-content/90">
|
||||
<li><b>เลือก Model:</b> ต้องเลือกเฉพาะ Model ที่เป็น <b>ACTIVE</b></li>
|
||||
<li><b>อัปโหลดไฟล์:</b> รองรับไฟล์ภาพ NIfTI (.nii)</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* การเตรียม Model */}
|
||||
<section id="deployment" className="pb-4 mb-6 scroll-mt-28">
|
||||
<h2 className="text-2xl font-bold flex items-center">
|
||||
<FaUpload className="mr-3 text-success" /> 3. การเตรียม Model (Deployment)
|
||||
</h2>
|
||||
|
||||
<ul className="list-disc list-inside space-y-2 ml-4 text-sm text-base-content/90">
|
||||
<li><b>การจัดเก็บ:</b> ไฟล์โมเดลต้องอยู่ใน MinIO bucket <b>'models'</b></li>
|
||||
<li><b>FastAPI:</b> ต้องมี Health Check (GET)</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* การตรวจสอบสถานะ */}
|
||||
<section id="ops" className="pb-8 mb-6 scroll-mt-28">
|
||||
<h2 className="text-2xl font-bold flex items-center">
|
||||
<FaServer className="mr-3 text-primary" /> 4. การตรวจสอบสถานะระบบ (System Ops)
|
||||
</h2>
|
||||
|
||||
<p className="mt-4 text-sm text-base-content/90 ml-4">
|
||||
ใช้เมนู <b>"สถานะระบบ (Health)"</b> เพื่อตรวจสอบความพร้อมของ infrastructure และ Model ACTIVE
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</TitleCard>
|
||||
);
|
||||
}
|
||||
@ -3,6 +3,7 @@ import BlankPage from '../pages/BlankPage';
|
||||
import ModelRegistry from '../pages/data/ModelRegistry';
|
||||
import InferenceRun from '../pages/data/InferenceRun';
|
||||
import SystemHealth from '../pages/system/Health';
|
||||
import UserGuide from '../pages/system/UserGuide';
|
||||
|
||||
// Array ของเส้นทางย่อยภายใต้ /dashboard/
|
||||
const pageRoutes = [
|
||||
@ -29,6 +30,12 @@ const pageRoutes = [
|
||||
path: 'health',
|
||||
element: <SystemHealth />,
|
||||
},
|
||||
// --- คู่มือการใช้งาน (Guide) ---
|
||||
{
|
||||
// Path: /dashboard/guide
|
||||
path: 'guide',
|
||||
element: <UserGuide />,
|
||||
},
|
||||
// Fallback สำหรับเส้นทางที่ไม่ตรงกับเมนูใดๆ
|
||||
{
|
||||
path: '*',
|
||||
|
||||
21
web/src/services/auditApi.js
Normal file
21
web/src/services/auditApi.js
Normal file
@ -0,0 +1,21 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import axiosClient from './axiosClient';
|
||||
|
||||
const STALE_TIME = 60000; // 1 นาที
|
||||
|
||||
/**
|
||||
* Hook สำหรับดึงสรุปสถิติ Inference (Total Runs, Success Rate, Avg Latency)
|
||||
* Endpoint: GET /api/v1/audit/inference-summary/
|
||||
*/
|
||||
export const useInferenceSummary = () => {
|
||||
return useQuery({
|
||||
queryKey: ['inferenceSummary'],
|
||||
queryFn: async () => {
|
||||
const response = await axiosClient.get('/api/v1/audit/inference-summary/');
|
||||
return response.data;
|
||||
},
|
||||
staleTime: STALE_TIME,
|
||||
// ดึงข้อมูลใหม่ทุก 30 วินาทีเพื่ออัปเดตสถิติ Dashboard
|
||||
refetchInterval: 30000,
|
||||
});
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user