feat: localize optimizer interface
This commit is contained in:
+12
-12
@@ -55,12 +55,12 @@ export default function Home() {
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await response.json();
|
||||
if (!response.ok) throw new Error(body.error ?? "Analyze failed");
|
||||
if (!response.ok) throw new Error(body.error ?? "分析失败");
|
||||
setJobId(body.job.id);
|
||||
setFactCard(body.candidateFactCard);
|
||||
setMessage("Candidate fact card created.");
|
||||
setMessage("候选事实卡已生成。");
|
||||
} catch (error) {
|
||||
setMessage(error instanceof Error ? error.message : "Analyze failed");
|
||||
setMessage(error instanceof Error ? error.message : "分析失败");
|
||||
} finally {
|
||||
setBusyAction(null);
|
||||
}
|
||||
@@ -77,11 +77,11 @@ export default function Home() {
|
||||
body: JSON.stringify(toConfirmedFactCard(factCard)),
|
||||
});
|
||||
const body = await response.json();
|
||||
if (!response.ok) throw new Error(body.error ?? "Confirm failed");
|
||||
if (!response.ok) throw new Error(body.error ?? "确认失败");
|
||||
setFactCard(body.factCard);
|
||||
setMessage("Fact card confirmed.");
|
||||
setMessage("事实卡已确认。");
|
||||
} catch (error) {
|
||||
setMessage(error instanceof Error ? error.message : "Confirm failed");
|
||||
setMessage(error instanceof Error ? error.message : "确认失败");
|
||||
} finally {
|
||||
setBusyAction(null);
|
||||
}
|
||||
@@ -96,16 +96,16 @@ export default function Home() {
|
||||
method: "POST",
|
||||
});
|
||||
const body = await response.json();
|
||||
if (!response.ok) throw new Error(body.error ?? "Optimize failed");
|
||||
if (!response.ok) throw new Error(body.error ?? "优化失败");
|
||||
setOptimizedArticle(body.optimizedArticle);
|
||||
setQaReport(body.qaReport);
|
||||
setMessage(
|
||||
body.qaReport.overall_status === "fail"
|
||||
? "QA found hard failures; export is blocked."
|
||||
: "Optimization complete.",
|
||||
? "质检发现硬性失败,已阻止导出。"
|
||||
: "优化完成。",
|
||||
);
|
||||
} catch (error) {
|
||||
setMessage(error instanceof Error ? error.message : "Optimize failed");
|
||||
setMessage(error instanceof Error ? error.message : "优化失败");
|
||||
} finally {
|
||||
setBusyAction(null);
|
||||
}
|
||||
@@ -115,7 +115,7 @@ export default function Home() {
|
||||
<main className="app-shell">
|
||||
<header className="topbar">
|
||||
<div>
|
||||
<h1>GEO Agent Article Optimizer</h1>
|
||||
<h1>GEO 智能文章优化器</h1>
|
||||
{message && <p>{message}</p>}
|
||||
</div>
|
||||
<button
|
||||
@@ -123,7 +123,7 @@ export default function Home() {
|
||||
onClick={optimize}
|
||||
type="button"
|
||||
>
|
||||
{busyAction === "optimize" ? "Optimizing..." : "Optimize"}
|
||||
{busyAction === "optimize" ? "优化中..." : "开始优化"}
|
||||
</button>
|
||||
</header>
|
||||
<div className="workflow-grid">
|
||||
|
||||
@@ -20,10 +20,10 @@ interface ArticleInputFormProps {
|
||||
}
|
||||
|
||||
const platforms: { value: PublishPlatform; label: string }[] = [
|
||||
{ value: "official_site", label: "Official site" },
|
||||
{ value: "media_article", label: "Media article" },
|
||||
{ value: "comparison_review", label: "Comparison review" },
|
||||
{ value: "recommendation_list", label: "Recommendation list" },
|
||||
{ value: "official_site", label: "官网文章" },
|
||||
{ value: "media_article", label: "媒体稿" },
|
||||
{ value: "comparison_review", label: "对比评测" },
|
||||
{ value: "recommendation_list", label: "推荐榜单" },
|
||||
];
|
||||
|
||||
export function ArticleInputForm({
|
||||
@@ -47,13 +47,13 @@ export function ArticleInputForm({
|
||||
return (
|
||||
<form className="panel stack" onSubmit={handleSubmit}>
|
||||
<div className="panel-heading">
|
||||
<span>Article Input</span>
|
||||
<span>文章输入</span>
|
||||
<button disabled={isSubmitting} type="submit">
|
||||
{isSubmitting ? "Analyzing..." : "Analyze"}
|
||||
{isSubmitting ? "分析中..." : "分析文章"}
|
||||
</button>
|
||||
</div>
|
||||
<label>
|
||||
<span>Title</span>
|
||||
<span>标题</span>
|
||||
<input
|
||||
required
|
||||
value={value.title}
|
||||
@@ -61,7 +61,7 @@ export function ArticleInputForm({
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Body</span>
|
||||
<span>正文</span>
|
||||
<textarea
|
||||
required
|
||||
className="body-input"
|
||||
@@ -70,14 +70,14 @@ export function ArticleInputForm({
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Images</span>
|
||||
<span>图片描述或图片链接</span>
|
||||
<textarea
|
||||
value={value.image_lines}
|
||||
onChange={(event) => update("image_lines", event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Target platform</span>
|
||||
<span>目标平台</span>
|
||||
<select
|
||||
value={value.platform}
|
||||
onChange={(event) =>
|
||||
@@ -92,7 +92,7 @@ export function ArticleInputForm({
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>User instructions</span>
|
||||
<span>用户要求</span>
|
||||
<textarea
|
||||
value={value.user_instructions}
|
||||
onChange={(event) => update("user_instructions", event.target.value)}
|
||||
|
||||
@@ -19,6 +19,16 @@ const listFields = [
|
||||
"uncertain_items",
|
||||
] as const;
|
||||
|
||||
const fieldLabels: Record<(typeof listFields)[number], string> = {
|
||||
company_short_names: "公司简称",
|
||||
brand_names: "品牌名称",
|
||||
product_names: "产品名称",
|
||||
core_claims: "核心事实/主张",
|
||||
forbidden_claims: "禁止使用的主张",
|
||||
image_topics: "图片主题",
|
||||
uncertain_items: "待确认事项",
|
||||
};
|
||||
|
||||
export function FactCardEditor({
|
||||
factCard,
|
||||
isSaving,
|
||||
@@ -28,7 +38,7 @@ export function FactCardEditor({
|
||||
if (!factCard) {
|
||||
return (
|
||||
<section className="panel empty-panel">
|
||||
<div className="panel-heading">Fact Card</div>
|
||||
<div className="panel-heading">事实卡</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -53,13 +63,13 @@ export function FactCardEditor({
|
||||
return (
|
||||
<section className="panel stack">
|
||||
<div className="panel-heading">
|
||||
<span>Fact Card</span>
|
||||
<span>事实卡</span>
|
||||
<button disabled={!canOptimize || isSaving} onClick={onConfirm}>
|
||||
{isSaving ? "Saving..." : "Confirm"}
|
||||
{isSaving ? "保存中..." : "确认事实卡"}
|
||||
</button>
|
||||
</div>
|
||||
<label>
|
||||
<span>Company full name</span>
|
||||
<span>公司全称</span>
|
||||
<input
|
||||
value={factCard.company_full_name}
|
||||
onChange={(event) => update("company_full_name", event.target.value)}
|
||||
@@ -67,14 +77,14 @@ export function FactCardEditor({
|
||||
</label>
|
||||
<div className="two-col">
|
||||
<label>
|
||||
<span>Target industry</span>
|
||||
<span>目标行业</span>
|
||||
<input
|
||||
value={factCard.target_industry}
|
||||
onChange={(event) => update("target_industry", event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Target audience</span>
|
||||
<span>目标受众</span>
|
||||
<input
|
||||
value={factCard.target_audience}
|
||||
onChange={(event) => update("target_audience", event.target.value)}
|
||||
@@ -82,7 +92,7 @@ export function FactCardEditor({
|
||||
</label>
|
||||
</div>
|
||||
<label>
|
||||
<span>Experience years</span>
|
||||
<span>经验年限</span>
|
||||
<input
|
||||
min="0"
|
||||
type="number"
|
||||
@@ -97,9 +107,9 @@ export function FactCardEditor({
|
||||
</label>
|
||||
{listFields.map((field) => (
|
||||
<label key={field}>
|
||||
<span>{field.replace(/_/g, " ")}</span>
|
||||
<span>{fieldLabels[field]}</span>
|
||||
<textarea
|
||||
value={factCard[field].join("\n")}
|
||||
value={factCard[field].join("\n")}
|
||||
onChange={(event) =>
|
||||
update(
|
||||
field,
|
||||
@@ -114,7 +124,7 @@ export function FactCardEditor({
|
||||
))}
|
||||
{!canOptimize && (
|
||||
<p className="status-text fail">
|
||||
Resolve uncertain items before optimization.
|
||||
请先处理待确认事项,再开始优化。
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@@ -18,21 +18,21 @@ export function OptimizedPreview({
|
||||
if (!article) {
|
||||
return (
|
||||
<section className="panel empty-panel">
|
||||
<div className="panel-heading">Optimized Result</div>
|
||||
<div className="panel-heading">优化结果</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="panel stack">
|
||||
<div className="panel-heading">Optimized Result</div>
|
||||
<div className="panel-heading">优化结果</div>
|
||||
<article className="preview">
|
||||
<h2>{article.title}</h2>
|
||||
<p>{article.summary}</p>
|
||||
<pre>{article.body_markdown}</pre>
|
||||
</article>
|
||||
<div>
|
||||
<h3>Image suggestions</h3>
|
||||
<h3>图片建议</h3>
|
||||
<ul>
|
||||
{article.image_suggestions.map((item) => (
|
||||
<li key={item.source}>
|
||||
@@ -42,7 +42,7 @@ export function OptimizedPreview({
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Changed sections</h3>
|
||||
<h3>修改位置</h3>
|
||||
<div className="tag-row">
|
||||
{article.changed_sections.map((section) => (
|
||||
<span className="tag" key={section}>
|
||||
@@ -53,7 +53,7 @@ export function OptimizedPreview({
|
||||
</div>
|
||||
{article.requires_user_confirmation.length > 0 && (
|
||||
<div>
|
||||
<h3>Needs confirmation</h3>
|
||||
<h3>需要人工确认</h3>
|
||||
<ul>
|
||||
{article.requires_user_confirmation.map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
|
||||
@@ -6,11 +6,17 @@ interface QaReportPanelProps {
|
||||
report: QaReport | null;
|
||||
}
|
||||
|
||||
const statusLabels = {
|
||||
pass: "通过",
|
||||
warn: "警告",
|
||||
fail: "失败",
|
||||
} as const;
|
||||
|
||||
export function QaReportPanel({ report }: QaReportPanelProps) {
|
||||
if (!report) {
|
||||
return (
|
||||
<section className="panel empty-panel">
|
||||
<div className="panel-heading">Quality Report</div>
|
||||
<div className="panel-heading">质量报告</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -18,9 +24,9 @@ export function QaReportPanel({ report }: QaReportPanelProps) {
|
||||
return (
|
||||
<section className="panel stack">
|
||||
<div className="panel-heading">
|
||||
<span>Quality Report</span>
|
||||
<span>质量报告</span>
|
||||
<span className={`status-pill ${report.overall_status}`}>
|
||||
{report.overall_status}
|
||||
{statusLabels[report.overall_status]}
|
||||
</span>
|
||||
</div>
|
||||
<div className="qa-list">
|
||||
@@ -29,7 +35,7 @@ export function QaReportPanel({ report }: QaReportPanelProps) {
|
||||
<div className="qa-title-row">
|
||||
<strong>{check.rule_id.replace(/_/g, " ")}</strong>
|
||||
<span className={`status-pill ${check.status}`}>
|
||||
{check.status}
|
||||
{statusLabels[check.status]}
|
||||
</span>
|
||||
</div>
|
||||
<p>{check.reason}</p>
|
||||
|
||||
Reference in New Issue
Block a user