fix(web): 標示 AIOps 範例資料模式
All checks were successful
CD Pipeline / tests (push) Successful in 1m23s
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / build-and-deploy (push) Successful in 3m41s
CD Pipeline / post-deploy-checks (push) Successful in 2m7s

This commit is contained in:
Your Name
2026-06-05 01:28:31 +08:00
parent 96df223100
commit d5ce17c72d
10 changed files with 21 additions and 20 deletions

View File

@@ -2239,7 +2239,7 @@
"aiopsTimeline": {
"title": "AIOps 全景時序",
"subtitle": "告警→感官調查→AI決策→自動執行→驗證→學習 完整鏈路",
"mockBadge": "MOCK 模式",
"sampleBadge": "範例資料",
"stages": {
"alert": "告警觸發",
"diagnose": "感官調查",

View File

@@ -2239,7 +2239,7 @@
"aiopsTimeline": {
"title": "AIOps 全景時序",
"subtitle": "告警→感官調查→AI決策→自動執行→驗證→學習 完整鏈路",
"mockBadge": "MOCK 模式",
"sampleBadge": "範例資料",
"stages": {
"alert": "告警觸發",
"diagnose": "感官調查",

View File

@@ -5,7 +5,7 @@
// AIOps 全景時序頁面
// 告警→感官調查→AI決策→自動執行→驗證→學習 完整鏈路視覺化
//
// Mock 模式NEXT_PUBLIC_AIOPS_TIMELINE_MOCK=true
// 範例資料模式NEXT_PUBLIC_AIOPS_TIMELINE_MOCK=true
// 真實 API: GET /api/v1/aiops/timeline?incident_id=&limit=20
// ============================================================

View File

@@ -12,7 +12,7 @@
* - ApprovalCard 滑入動畫
* - 記憶體安全清理
*
* 真實性條款: 禁止任何 Mock Data
* 真實性條款:禁止以範例資料掩蓋 API 狀態
* i18n: 100% next-intl
*/

View File

@@ -20,7 +20,7 @@ import {
import { cn } from '@/lib/utils'
import { TimelineFilter } from './TimelineFilter'
import { TimelineStage } from './TimelineStage'
import { MOCK_INCIDENTS } from './mock-data'
import { SAMPLE_TIMELINE_INCIDENTS } from './sample-incidents'
import type {
TimelineIncident,
TimelineFilterState,
@@ -31,7 +31,8 @@ import type {
// Constants
// ============================================================
const IS_MOCK = process.env.NEXT_PUBLIC_AIOPS_TIMELINE_MOCK === 'true'
// 保留既有環境變數相容UI 以「範例資料」呈現,避免誤讀為正式證據。
const USE_SAMPLE_TIMELINE = process.env.NEXT_PUBLIC_AIOPS_TIMELINE_MOCK === 'true'
const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? ''
const STAGE_ORDER: StageType[] = ['alert', 'diagnose', 'decide', 'execute', 'verify', 'learn']
@@ -280,14 +281,14 @@ export default function AiopsTimelinePanel() {
} = useQuery({
queryKey: ['aiops-timeline', filter.incident_id, filter.time_range],
queryFn: () => fetchTimeline(filter.incident_id),
enabled: !IS_MOCK,
enabled: !USE_SAMPLE_TIMELINE,
staleTime: 30_000,
refetchInterval: 60_000,
})
// 客戶端篩選 — rawIncidents 內聯進 useMemo 避免 conditional 依賴警告
const incidents = useMemo(() => {
const rawIncidents: TimelineIncident[] = IS_MOCK ? MOCK_INCIDENTS : (apiData ?? [])
const rawIncidents: TimelineIncident[] = USE_SAMPLE_TIMELINE ? SAMPLE_TIMELINE_INCIDENTS : (apiData ?? [])
let list = rawIncidents
if (filter.incident_id.trim()) {
@@ -325,9 +326,9 @@ export default function AiopsTimelinePanel() {
<h1 className="font-heading text-2xl font-bold text-nothing-black flex items-center gap-2.5">
<GitBranch className="w-6 h-6 text-claw-blue" aria-hidden="true" />
{t('title')}
{IS_MOCK && (
{USE_SAMPLE_TIMELINE && (
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-body font-bold uppercase tracking-widest bg-status-warning/10 border border-status-warning/20 text-status-warning">
{t('mockBadge')}
{t('sampleBadge')}
</span>
)}
</h1>
@@ -337,7 +338,7 @@ export default function AiopsTimelinePanel() {
</div>
{/* Refresh button (僅真實模式顯示) */}
{!IS_MOCK && (
{!USE_SAMPLE_TIMELINE && (
<button
onClick={() => refetch()}
disabled={isLoading}
@@ -362,7 +363,7 @@ export default function AiopsTimelinePanel() {
/>
{/* Loading state (真實 API) */}
{!IS_MOCK && isLoading && (
{!USE_SAMPLE_TIMELINE && isLoading && (
<div className="flex items-center justify-center py-20" role="status" aria-live="polite">
<RefreshCw className="w-5 h-5 animate-spin text-nothing-gray-400" />
<span className="ml-2 font-body text-sm text-nothing-gray-400">{t('loading')}</span>
@@ -370,7 +371,7 @@ export default function AiopsTimelinePanel() {
)}
{/* Error state */}
{!IS_MOCK && error && !isLoading && (
{!USE_SAMPLE_TIMELINE && error && !isLoading && (
<div
className="flex flex-col items-center justify-center py-20 gap-3"
role="alert"

View File

@@ -3,5 +3,5 @@ export { TimelineStage } from './TimelineStage'
export { TimelineStageDetails } from './TimelineStageDetails'
export { TimelineFilter } from './TimelineFilter'
export { EvidenceViewer } from './EvidenceViewer'
export { MOCK_INCIDENTS } from './mock-data'
export { SAMPLE_TIMELINE_INCIDENTS } from './sample-incidents'
export type * from './types'

View File

@@ -1,12 +1,12 @@
// 2026-04-26 P2.5 by Claude — AIOps Timeline
// ============================================================
// Mock Data — 3 範例 incident完整 6 階段 + metadata
// Sample incidents — 3 範例 incident完整 6 階段 + metadata
// NEXT_PUBLIC_AIOPS_TIMELINE_MOCK=true 時使用
// ============================================================
import type { TimelineIncident } from './types'
export const MOCK_INCIDENTS: TimelineIncident[] = [
export const SAMPLE_TIMELINE_INCIDENTS: TimelineIncident[] = [
{
incident_id: 'INC-2026-0425-001',
title: 'API Server CPU 100% — k8s-188',

View File

@@ -9,7 +9,7 @@
* 3. getTerminalComponents — 只返回 allowInTerminal=true 的組件
* 4. validateProps — 錯誤碼分類 (UNKNOWN_COMPONENT / ZOD_VALIDATION_FAILED)
*
* 注意: registry.ts 使用 React.lazy在 Node 環境需要 Mock
* 注意registry.ts 使用 React.lazy在 Node 環境需以測試替身隔離 lazy 載入
*
* @see ADR-032 GenUI Dynamic Rendering
* Phase 19.6 ogt 2026-03-31 (台北時間)
@@ -17,7 +17,7 @@
import { vi, describe, it, expect, beforeAll } from 'vitest'
// ===== Mock React (lazy Node 環境無法執行) =====
// ===== React lazy 測試替身(Node 環境無法執行 lazy import =====
vi.mock('react', () => ({
lazy: vi.fn((factory: () => Promise<unknown>) => factory),
}))

View File

@@ -5,7 +5,7 @@
* =====================================
* 顯示真實待審核的操作,整合 NuclearKeyButton 長按確認
*
* 2026-04-07 Claude Code: Sprint F 打假行動 — 移除 MOCK_PENDING
* 2026-04-07 Claude Code: Sprint F 打假行動 — 移除舊待審範例常數
* 專案鐵律: 接真實 /api/v1/approvals/pending無資料顯示 EmptyState
*/

View File

@@ -5,7 +5,7 @@
* ================================
* 5 KPI + 執行路徑分佈 + Playbook 排名 + 時間軸
*
* 2026-04-07 Claude Code: Sprint F 打假行動 — 移除所有 MOCK 常數
* 2026-04-07 Claude Code: Sprint F 打假行動 — 移除所有假資料常數
* 專案鐵律: 所有數據從 stats/playbooks/history props 計算,禁止寫死!
*/