"use client"; import { useState, useEffect } from "react"; import { useSession } from "next-auth/react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { schemaAdvisorApi, type SchemaSuggestion } from "@/lib/api/schema-advisor"; import { useApi } from "@/lib/hooks/use-api"; import { LoadingState, ErrorState, EmptyState } from "@/components/ui/api-states"; import { Code2, CheckCircle2, XCircle, AlertTriangle, RefreshCw, Eye, FileJson, Loader2, } from "lucide-react"; const VALIDATION_BADGE: Record = { valid: { label: "有效", className: "bg-emerald-100 text-emerald-700 hover:bg-emerald-100" }, invalid: { label: "无效", className: "bg-red-100 text-red-700 hover:bg-red-100" }, pending: { label: "待验证", className: "bg-amber-100 text-amber-700 hover:bg-amber-100" }, }; const STATUS_BADGE: Record = { pending: { label: "待处理", className: "bg-gray-100 text-gray-700 hover:bg-gray-100" }, applied: { label: "已应用", className: "bg-emerald-100 text-emerald-700 hover:bg-emerald-100" }, dismissed: { label: "已忽略", className: "bg-red-100 text-red-700 hover:bg-red-100" }, }; export default function SchemaPage() { const { data: session } = useSession(); const token = (session as { accessToken?: string })?.accessToken; const [suggestions, setSuggestions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [adviseDialogOpen, setAdviseDialogOpen] = useState(false); const [targetUrl, setTargetUrl] = useState(""); const [advising, setAdvising] = useState(false); const [adviseError, setAdviseError] = useState(null); const [detailDialogOpen, setDetailDialogOpen] = useState(false); const [selectedSuggestion, setSelectedSuggestion] = useState(null); const [statusUpdating, setStatusUpdating] = useState(false); const [statusError, setStatusError] = useState(null); const { data: brandsData } = useApi<{ items: { id: string; name: string }[] }>("/api/v1/brands/"); const brandId = brandsData?.items?.[0]?.id ?? ""; async function loadSuggestions() { if (!token || !brandId) return; try { setLoading(true); setError(null); const result = await schemaAdvisorApi.getBrandSuggestions(token, brandId); setSuggestions(result.suggestions ?? []); } catch (err) { setError(err instanceof Error ? err.message : "获取 Schema 建议失败"); } finally { setLoading(false); } } useEffect(() => { if (token && brandId) loadSuggestions(); }, [token, brandId]); async function handleAdvise() { if (!token || !brandId) return; try { setAdvising(true); setAdviseError(null); await schemaAdvisorApi.advise(token, { brand_id: brandId, target_url: targetUrl || undefined, }); setAdviseDialogOpen(false); setTargetUrl(""); loadSuggestions(); } catch (err) { setAdviseError(err instanceof Error ? err.message : "生成建议失败"); } finally { setAdvising(false); } } function openDetail(suggestion: SchemaSuggestion) { setSelectedSuggestion(suggestion); setStatusError(null); setDetailDialogOpen(true); } async function handleUpdateStatus(suggestionId: string, status: string) { if (!token) return; try { setStatusUpdating(true); setStatusError(null); await schemaAdvisorApi.updateStatus(token, suggestionId, status); setDetailDialogOpen(false); setSelectedSuggestion(null); loadSuggestions(); } catch (err) { setStatusError(err instanceof Error ? err.message : "状态更新失败"); } finally { setStatusUpdating(false); } } if (loading) { return (

Schema 建议

结构化数据优化建议

); } if (error) { return (

Schema 建议

结构化数据优化建议

); } return (

Schema 建议

结构化数据优化建议,提升搜索引擎与 AI 平台的理解能力

{suggestions.length === 0 ? ( } message="暂无 Schema 建议" description="点击右上角按钮生成结构化数据优化建议" action={ } /> ) : ( 建议列表
Schema 类型 目标 URL 验证状态 优先级 状态 创建时间 操作 {suggestions.map((s) => { const vCfg = VALIDATION_BADGE[s.validation_status ?? "pending"] ?? { label: s.validation_status ?? "未知", className: "bg-gray-100 text-gray-600", }; const sCfg = STATUS_BADGE[s.status] ?? { label: s.status, className: "bg-gray-100 text-gray-600", }; return ( openDetail(s)} >
{s.schema_type}
{s.target_url ?? "—"} {s.validation_status === "valid" && } {s.validation_status === "invalid" && } {s.validation_status === "pending" && } {vCfg.label} {s.priority !== null ? ( {s.priority} ) : "—"} {sCfg.label} {new Date(s.created_at).toLocaleString("zh-CN")}
); })}
)} 生成 Schema 建议
setTargetUrl(e.target.value)} />
{adviseError && (

{adviseError}

)}
{selectedSuggestion?.schema_type} {selectedSuggestion && (
目标 URL: {selectedSuggestion.target_url ?? "—"}
优先级: {selectedSuggestion.priority ?? "—"}
验证状态: {VALIDATION_BADGE[selectedSuggestion.validation_status ?? "pending"]?.label ?? "未知"}
当前状态: {STATUS_BADGE[selectedSuggestion.status]?.label ?? selectedSuggestion.status}
JSON-LD
                  {JSON.stringify(selectedSuggestion.json_ld, null, 2)}
                
{selectedSuggestion.validation_errors && selectedSuggestion.validation_errors.length > 0 && (
验证错误
    {selectedSuggestion.validation_errors.map((err, i) => (
  • {err}
  • ))}
)} {statusError && (

{statusError}

)} {selectedSuggestion.status === "pending" && (
)}
)}
); }