690519:1631 224 to 226 AI #01
CI / CD Pipeline / build (push) Failing after 3m57s
CI / CD Pipeline / deploy (push) Has been skipped

This commit is contained in:
2026-05-19 16:31:50 +07:00
parent 3e25097470
commit ea5499123e
127 changed files with 12387 additions and 42 deletions
@@ -0,0 +1,67 @@
// File: components/ai/intent-classification/analytics/analytics-summary-cards.tsx
// Change Log
// - 2026-05-19: สร้าง Summary Cards สำหรับ Analytics Dashboard (T036, US3).
'use client';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import type { ClassificationAnalytics } from '@/lib/services/ai-intent.service';
interface AnalyticsSummaryCardsProps {
data: ClassificationAnalytics;
}
/**
* แสดงสรุปสถิติหลักในรูปแบบ Cards
* Total Requests, Pattern Hit Rate, Avg Confidence, Avg Latency
*/
export function AnalyticsSummaryCards({ data }: AnalyticsSummaryCardsProps) {
const cards = [
{
title: 'Total Requests',
value: data.totalRequests.toLocaleString(),
subtitle: `${data.successCount} สำเร็จ / ${data.failedCount} ล้มเหลว`,
color: 'text-blue-600',
},
{
title: 'Pattern Hit Rate',
value: `${data.patternHitRate}%`,
subtitle: 'เป้าหมาย: 70-80%',
color: data.patternHitRate >= 70 ? 'text-green-600' : 'text-amber-600',
},
{
title: 'Avg Confidence',
value: data.avgConfidence.toFixed(2),
subtitle: 'เป้าหมาย: ≥ 0.70',
color: data.avgConfidence >= 0.7 ? 'text-green-600' : 'text-amber-600',
},
{
title: 'Avg Latency',
value: `${data.avgLatencyMs.toFixed(1)}ms`,
subtitle: 'Pattern < 10ms, LLM < 2000ms',
color: data.avgLatencyMs < 100 ? 'text-green-600' : 'text-amber-600',
},
];
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
{cards.map((card) => (
<Card key={card.title}>
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
{card.title}
</CardTitle>
</CardHeader>
<CardContent>
<div className={`text-2xl font-bold ${card.color}`}>
{card.value}
</div>
<p className="text-xs text-muted-foreground mt-1">
{card.subtitle}
</p>
</CardContent>
</Card>
))}
</div>
);
}
@@ -0,0 +1,71 @@
// File: components/ai/intent-classification/analytics/intent-breakdown-table.tsx
// Change Log
// - 2026-05-19: สร้าง Intent Breakdown Table สำหรับ Analytics Dashboard (T036, US3).
'use client';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Progress } from '@/components/ui/progress';
import type { IntentStats } from '@/lib/services/ai-intent.service';
interface IntentBreakdownTableProps {
data: IntentStats[];
}
/**
* ตารางแสดงสถิติแยกตาม intent code พร้อม bar แสดง pattern vs llm
*/
export function IntentBreakdownTable({ data }: IntentBreakdownTableProps) {
if (data.length === 0) {
return <p className="text-sm text-muted-foreground"></p>;
}
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>Intent Code</TableHead>
<TableHead className="text-right">Total</TableHead>
<TableHead className="text-right">Pattern</TableHead>
<TableHead className="text-right">LLM</TableHead>
<TableHead className="text-right">Avg Confidence</TableHead>
<TableHead className="w-[120px]">Pattern Rate</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((row) => {
const patternRate =
row.count > 0 ? (row.patternHits / row.count) * 100 : 0;
return (
<TableRow key={row.intentCode}>
<TableCell className="font-mono text-sm">
{row.intentCode}
</TableCell>
<TableCell className="text-right">{row.count}</TableCell>
<TableCell className="text-right">{row.patternHits}</TableCell>
<TableCell className="text-right">{row.llmHits}</TableCell>
<TableCell className="text-right">
{row.avgConfidence.toFixed(2)}
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Progress value={patternRate} className="h-2" />
<span className="text-xs text-muted-foreground w-10 text-right">
{patternRate.toFixed(0)}%
</span>
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
);
}
@@ -0,0 +1,78 @@
// File: components/ai/intent-classification/analytics/method-breakdown-table.tsx
// Change Log
// - 2026-05-19: สร้าง Method Breakdown Table สำหรับ Analytics Dashboard (T036, US3).
'use client';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Badge } from '@/components/ui/badge';
import type { MethodStats } from '@/lib/services/ai-intent.service';
interface MethodBreakdownTableProps {
data: MethodStats[];
}
/** แปลงชื่อ method เป็น label + สี */
function methodBadge(method: string) {
switch (method) {
case 'pattern':
return <Badge variant="default">Pattern Match</Badge>;
case 'llm_fallback':
return <Badge variant="secondary">LLM Fallback</Badge>;
case 'semaphore_overflow':
return <Badge variant="destructive">Semaphore Overflow</Badge>;
case 'llm_error':
return <Badge variant="destructive">LLM Error</Badge>;
default:
return <Badge variant="outline">{method}</Badge>;
}
}
/**
* ตารางแสดงสถิติแยกตาม method (pattern, llm_fallback, etc.)
*/
export function MethodBreakdownTable({ data }: MethodBreakdownTableProps) {
if (data.length === 0) {
return <p className="text-sm text-muted-foreground"></p>;
}
const total = data.reduce((sum, d) => sum + d.count, 0);
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>Method</TableHead>
<TableHead className="text-right">Count</TableHead>
<TableHead className="text-right">%</TableHead>
<TableHead className="text-right">Avg Confidence</TableHead>
<TableHead className="text-right">Avg Latency</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((row) => (
<TableRow key={row.method}>
<TableCell>{methodBadge(row.method)}</TableCell>
<TableCell className="text-right">{row.count}</TableCell>
<TableCell className="text-right">
{total > 0 ? ((row.count / total) * 100).toFixed(1) : 0}%
</TableCell>
<TableCell className="text-right">
{row.avgConfidence.toFixed(2)}
</TableCell>
<TableCell className="text-right">
{row.avgLatencyMs.toFixed(1)}ms
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}
@@ -0,0 +1,78 @@
// File: components/ai/intent-classification/analytics/recalibration-panel.tsx
// Change Log
// - 2026-05-19: สร้าง Recalibration Panel สำหรับ Analytics Dashboard (T036, US3).
'use client';
import { AlertTriangle } from 'lucide-react';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import type { RecalibrationRecommendation } from '@/lib/services/ai-intent.service';
interface RecalibrationPanelProps {
data: RecalibrationRecommendation[];
}
/**
* แสดงคำแนะนำ Intent ที่ควรเพิ่ม pattern เพื่อลด LLM Calls
* ตาม SC-001: เป้าหมาย Pattern Hit Rate 70-80%
*/
export function RecalibrationPanel({ data }: RecalibrationPanelProps) {
if (data.length === 0) {
return (
<Alert>
<AlertTitle></AlertTitle>
<AlertDescription>
Intent Pattern Pattern Hit Rate
</AlertDescription>
</Alert>
);
}
return (
<div className="space-y-3">
<Alert variant="destructive">
<AlertTriangle className="h-4 w-4" />
<AlertTitle> Pattern</AlertTitle>
<AlertDescription>
Intent classify LLM keyword/regex pattern
LLM
</AlertDescription>
</Alert>
<Table>
<TableHeader>
<TableRow>
<TableHead>Intent Code</TableHead>
<TableHead className="text-right">LLM Calls</TableHead>
<TableHead className="text-right">Avg Confidence</TableHead>
<TableHead className="text-right">Priority</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((row) => (
<TableRow key={row.intentCode}>
<TableCell className="font-mono text-sm">
{row.intentCode}
</TableCell>
<TableCell className="text-right">{row.llmCallCount}</TableCell>
<TableCell className="text-right">
{row.avgConfidence.toFixed(2)}
</TableCell>
<TableCell className="text-right font-medium text-amber-600">
{row.priority}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}