พัฒนา Frontend เมนู แดชบอร์ด/ภาพรวม & คู่มือการใช้งานเบื้องต้น

This commit is contained in:
Flook 2025-11-15 06:41:09 +07:00
parent 5e9e0da972
commit 3a4d6c245c
5 changed files with 239 additions and 16 deletions

View File

@ -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'],
},
];

View File

@ -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;

View 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>
);
}

View File

@ -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: '*',

View 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,
});
};