690519:1631 224 to 226 AI #01
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
'use client';
|
||||
|
||||
// File: app/(admin)/admin/ai/intent-classification/[intentCode]/page.tsx
|
||||
// Change Log
|
||||
// - 2026-05-19: สร้างหน้า Intent Detail + Patterns (Admin, ADR-024).
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import {
|
||||
useIntentDefinition,
|
||||
useUpdateIntentDefinition,
|
||||
useIntentPatterns,
|
||||
useCreateIntentPattern,
|
||||
useDeleteIntentPattern,
|
||||
} from '@/hooks/ai/use-intent-classification';
|
||||
import { PatternForm } from '@/components/ai/intent-classification/pattern-form';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import { ArrowLeft, Plus, Trash2 } from 'lucide-react';
|
||||
import type { PatternType, PatternLanguage } from '@/lib/services/ai-intent.service';
|
||||
|
||||
export default function IntentDetailPage() {
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
const intentCode = params.intentCode as string;
|
||||
|
||||
const [showPatternForm, setShowPatternForm] = useState(false);
|
||||
|
||||
const { data: definition, isLoading: defLoading } = useIntentDefinition(intentCode);
|
||||
const updateMutation = useUpdateIntentDefinition(intentCode);
|
||||
const { data: patterns, isLoading: patternsLoading } = useIntentPatterns(intentCode);
|
||||
const createPatternMutation = useCreateIntentPattern(intentCode);
|
||||
const deletePatternMutation = useDeleteIntentPattern(intentCode);
|
||||
|
||||
const handleToggleActive = async (isActive: boolean) => {
|
||||
await updateMutation.mutateAsync({ isActive });
|
||||
};
|
||||
|
||||
const handleCreatePattern = async (data: {
|
||||
patternType: PatternType;
|
||||
patternValue: string;
|
||||
language?: PatternLanguage;
|
||||
priority?: number;
|
||||
}) => {
|
||||
await createPatternMutation.mutateAsync(data);
|
||||
setShowPatternForm(false);
|
||||
};
|
||||
|
||||
const handleDeletePattern = async (publicId: string) => {
|
||||
if (!confirm('ต้องการลบ Pattern นี้?')) return;
|
||||
await deletePatternMutation.mutateAsync(publicId);
|
||||
};
|
||||
|
||||
if (defLoading) {
|
||||
return <p className="text-center py-8 text-muted-foreground">กำลังโหลด...</p>;
|
||||
}
|
||||
|
||||
if (!definition) {
|
||||
return <p className="text-center py-8 text-destructive">ไม่พบ Intent: {intentCode}</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => router.push('/admin/ai/intent-classification')}
|
||||
>
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<div className="flex-1">
|
||||
<h1 className="text-2xl font-bold font-mono">{definition.intentCode}</h1>
|
||||
<p className="text-muted-foreground">{definition.descriptionTh}</p>
|
||||
<p className="text-sm text-muted-foreground">{definition.descriptionEn}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-muted-foreground">Active</span>
|
||||
<Switch
|
||||
checked={definition.isActive}
|
||||
onCheckedChange={handleToggleActive}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Info Card */}
|
||||
<Card>
|
||||
<CardContent className="pt-4 flex gap-4">
|
||||
<Badge variant="secondary">{definition.category}</Badge>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
สร้างเมื่อ: {new Date(definition.createdAt).toLocaleString('th-TH')}
|
||||
</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Patterns Table */}
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<CardTitle>Patterns ({patterns?.length || 0})</CardTitle>
|
||||
<Button size="sm" onClick={() => setShowPatternForm(true)}>
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
เพิ่ม Pattern
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{patternsLoading ? (
|
||||
<p className="text-center text-muted-foreground py-4">กำลังโหลด...</p>
|
||||
) : patterns && patterns.length > 0 ? (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Type</TableHead>
|
||||
<TableHead>Pattern Value</TableHead>
|
||||
<TableHead>Language</TableHead>
|
||||
<TableHead>Priority</TableHead>
|
||||
<TableHead>สถานะ</TableHead>
|
||||
<TableHead></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{patterns.map((p) => (
|
||||
<TableRow key={p.publicId}>
|
||||
<TableCell>
|
||||
<Badge variant="outline">{p.patternType}</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="font-mono text-sm max-w-[200px] truncate">
|
||||
{p.patternValue}
|
||||
</TableCell>
|
||||
<TableCell>{p.language}</TableCell>
|
||||
<TableCell>{p.priority}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={p.isActive ? 'default' : 'destructive'}>
|
||||
{p.isActive ? 'Active' : 'Inactive'}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleDeletePattern(p.publicId)}
|
||||
disabled={deletePatternMutation.isPending}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-destructive" />
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
) : (
|
||||
<p className="text-center text-muted-foreground py-4">
|
||||
ยังไม่มี Pattern — เพิ่มเพื่อให้ Pattern Matching ทำงาน
|
||||
</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Create Pattern Form Dialog */}
|
||||
<PatternForm
|
||||
open={showPatternForm}
|
||||
onClose={() => setShowPatternForm(false)}
|
||||
onSubmit={handleCreatePattern}
|
||||
isLoading={createPatternMutation.isPending}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// File: app/(admin)/admin/ai/intent-classification/analytics/page.tsx
|
||||
// Change Log
|
||||
// - 2026-05-19: สร้างหน้า Analytics Dashboard สำหรับ Intent Classification (T037, US3).
|
||||
|
||||
'use client';
|
||||
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { useIntentAnalytics } from '@/hooks/ai/use-intent-classification';
|
||||
import { AnalyticsSummaryCards } from '@/components/ai/intent-classification/analytics/analytics-summary-cards';
|
||||
import { MethodBreakdownTable } from '@/components/ai/intent-classification/analytics/method-breakdown-table';
|
||||
import { IntentBreakdownTable } from '@/components/ai/intent-classification/analytics/intent-breakdown-table';
|
||||
import { RecalibrationPanel } from '@/components/ai/intent-classification/analytics/recalibration-panel';
|
||||
|
||||
/**
|
||||
* หน้า Analytics Dashboard สำหรับ Intent Classification
|
||||
* แสดง Summary Cards, Method Breakdown, Intent Breakdown, Recalibration
|
||||
*/
|
||||
export default function IntentAnalyticsPage() {
|
||||
const { data, isLoading, isError, error } = useIntentAnalytics();
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="space-y-6 p-6">
|
||||
<h1 className="text-2xl font-bold">Intent Classification Analytics</h1>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{Array.from({ length: 4 }).map((_, i) => (
|
||||
<Skeleton key={i} className="h-[120px]" />
|
||||
))}
|
||||
</div>
|
||||
<Skeleton className="h-[300px]" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">Intent Classification Analytics</h1>
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<p className="text-destructive">
|
||||
เกิดข้อผิดพลาด: {error instanceof Error ? error.message : 'ไม่สามารถโหลดข้อมูลได้'}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Intent Classification Analytics</h1>
|
||||
<p className="text-sm text-muted-foreground">ข้อมูลย้อนหลัง 30 วัน</p>
|
||||
</div>
|
||||
|
||||
{/* Summary Cards */}
|
||||
<AnalyticsSummaryCards data={data} />
|
||||
|
||||
{/* Method Breakdown */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Classification Method Breakdown</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<MethodBreakdownTable data={data.byMethod} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Intent Breakdown */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Intent Code Breakdown</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<IntentBreakdownTable data={data.byIntent} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Recalibration */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Recalibration Recommendations</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<RecalibrationPanel data={data.recalibration} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
'use client';
|
||||
|
||||
// File: app/(admin)/admin/ai/intent-classification/page.tsx
|
||||
// Change Log
|
||||
// - 2026-05-19: สร้างหน้า Intent Definitions List (Admin, ADR-024).
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import {
|
||||
useIntentDefinitions,
|
||||
useCreateIntentDefinition,
|
||||
} from '@/hooks/ai/use-intent-classification';
|
||||
import { IntentForm } from '@/components/ai/intent-classification/intent-form';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import { Plus, Brain, TestTube } from 'lucide-react';
|
||||
import type { IntentCategory } from '@/lib/services/ai-intent.service';
|
||||
|
||||
/** สีของ category badge */
|
||||
const CATEGORY_COLORS: Record<IntentCategory, string> = {
|
||||
read: 'bg-blue-100 text-blue-800',
|
||||
suggest: 'bg-purple-100 text-purple-800',
|
||||
utility: 'bg-gray-100 text-gray-800',
|
||||
};
|
||||
|
||||
export default function IntentClassificationPage() {
|
||||
const router = useRouter();
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const { data: definitions, isLoading } = useIntentDefinitions();
|
||||
const createMutation = useCreateIntentDefinition();
|
||||
|
||||
const handleCreate = async (data: {
|
||||
intentCode: string;
|
||||
descriptionTh: string;
|
||||
descriptionEn: string;
|
||||
category: IntentCategory;
|
||||
}) => {
|
||||
await createMutation.mutateAsync(data);
|
||||
setShowForm(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold flex items-center gap-2">
|
||||
<Brain className="h-6 w-6" />
|
||||
Intent Classification
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
จัดการ Intent Definitions และ Patterns สำหรับ AI Chat
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => router.push('/admin/ai/intent-classification/test-console')}
|
||||
>
|
||||
<TestTube className="h-4 w-4 mr-2" />
|
||||
Test Console
|
||||
</Button>
|
||||
<Button onClick={() => setShowForm(true)}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
สร้าง Intent
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Intent Definitions ({definitions?.length || 0})</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading ? (
|
||||
<p className="text-center text-muted-foreground py-8">กำลังโหลด...</p>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Intent Code</TableHead>
|
||||
<TableHead>คำอธิบาย</TableHead>
|
||||
<TableHead>Category</TableHead>
|
||||
<TableHead>สถานะ</TableHead>
|
||||
<TableHead>Patterns</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{definitions?.map((def) => (
|
||||
<TableRow
|
||||
key={def.publicId}
|
||||
className="cursor-pointer hover:bg-muted/50"
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/admin/ai/intent-classification/${def.intentCode}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<TableCell className="font-mono font-medium">
|
||||
{def.intentCode}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="text-sm">{def.descriptionTh}</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{def.descriptionEn}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={CATEGORY_COLORS[def.category]}
|
||||
>
|
||||
{def.category}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={def.isActive ? 'default' : 'destructive'}>
|
||||
{def.isActive ? 'Active' : 'Inactive'}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
{def.patterns?.length || 0}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Create Form Dialog */}
|
||||
<IntentForm
|
||||
open={showForm}
|
||||
onClose={() => setShowForm(false)}
|
||||
onSubmit={handleCreate}
|
||||
isLoading={createMutation.isPending}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
// File: app/(admin)/admin/ai/intent-classification/test-console/page.tsx
|
||||
// Change Log
|
||||
// - 2026-05-19: สร้างหน้า Test Console สำหรับทดสอบ Intent Classification (ADR-024).
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { TestConsolePanel } from '@/components/ai/intent-classification/test-console-panel';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
|
||||
export default function TestConsolePage() {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => router.push('/admin/ai/intent-classification')}
|
||||
>
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">Intent Test Console</h1>
|
||||
<p className="text-muted-foreground">
|
||||
ทดสอบ Intent Classification แบบ Real-time — พิมพ์คำถามเพื่อดูผล
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Test Console */}
|
||||
<TestConsolePanel />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user