feat(template-wizard): require library brand before favoriting competitors

Drop the implicit brand-creation path in handleFavoriteCompetitor and
just refuse to save a competitor until the user has explicitly chosen a
library brand; clear saved state on competitor drafts when the brand is
unset. Also enforce the configured custom-outline label length cap and
auto-focus newly added outline nodes for faster keyboard editing. New
i18n strings cover the brand-required and outline-length messages plus
the updated competitor hint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-30 01:30:29 +08:00
parent 14a7921445
commit e15806bd91
3 changed files with 87 additions and 41 deletions
+3 -1
View File
@@ -779,7 +779,7 @@ const enUS = {
brandFlow: "You can pick a brand from the library or type one manually. AI analysis will expand keywords, competitors, and title options.",
brandSummaryPlaceholder: "After analysis, AI will summarize the brand angle, positioning, and content opportunities here.",
keywords: "Type keywords directly or reuse brand-library keywords. AI results will be merged into this list.",
competitors: "Competitors stay editable. You can add rows manually and save useful ones back into the competitor library.",
competitors: "Competitors stay editable. You can add rows manually; saving to the competitor library is only available after choosing a library brand.",
competitorEmptyEditable: "No competitors yet. Add one manually or let AI fill the list first.",
templateFields: "Keep any extra template-specific fields here to constrain the writing direction.",
titleSelection: "AI will return 5 better title options before you move forward. Choose one or rewrite it.",
@@ -866,6 +866,8 @@ const enUS = {
missingTitle: "Please choose or enter an article title.",
missingOutline: "Please select at least one outline section.",
missingGeneratedOutline: "Please generate and confirm the article outline first.",
customOutlineTooLong: "Custom sections cannot exceed {count} characters.",
selectBrandBeforeFavorite: "Choose a brand from the library before saving competitors.",
missingCompetitorName: "Enter a competitor name before saving it to the library.",
savedCompetitor: "Competitor saved to the brand library.",
assistReady: "AI analysis is ready. You can keep refining the draft.",
+4 -3
View File
@@ -91,7 +91,6 @@ const zhCN = {
},
auth: {
welcomeBack: "欢迎回来",
tenantAdminLogin: "租户后台登录",
loginAndEnter: "登录并进入工作台",
email: "邮箱",
loginIdentifier: "手机号 / 邮箱",
@@ -596,7 +595,7 @@ const zhCN = {
publishing: "发布中",
success: "发布成功",
failed: "发布失败",
partial_success: "部分发布",
partial_success: "部分成功",
published: "发布成功",
publish_success: "发布成功",
publish_failed: "发布失败",
@@ -781,7 +780,7 @@ const zhCN = {
brandFlow: "品牌可直接输入,也可以从品牌库带入。点击分析后,AI 会补充关键词、竞品和标题建议。",
brandSummaryPlaceholder: "AI 分析后会在这里补充品牌简介、差异点和适合切入的内容角度。",
keywords: "支持直接输入,也支持复用品牌词库里的关键词。AI 分析结果会自动补充到这里。",
competitors: "竞品支持手动编辑、追加收藏到品牌词库。AI 分析会尽量补齐候选竞品。",
competitors: "竞品支持手动编辑、追加;只有从品牌库选择品牌后,才可以收藏到品牌词库。AI 分析会尽量补齐候选竞品。",
competitorEmptyEditable: "还没有竞品,先手动添加一条或点击分析让 AI 自动补齐。",
templateFields: "这里保留当前模板自己的补充字段,用来约束 AI 生成角度。",
titleSelection: "下一步前会由 AI 给出 5 个更合适的标题候选,你可以在这里挑选或改写。",
@@ -874,6 +873,8 @@ const zhCN = {
missingReviewIntroHook: "请先选择一个评测引言钩子",
missingOutline: "请至少选择一个文章结构段落",
missingGeneratedOutline: "请先生成并确认文章大纲",
customOutlineTooLong: "自定义结构不能超过 {count} 个字",
selectBrandBeforeFavorite: "请先从品牌库选择品牌后再收藏竞品",
missingCompetitorName: "至少需要填写竞品名称后才能收藏",
savedCompetitor: "竞品已收藏到品牌词库",
assistReady: "AI 分析已完成,可以继续调整内容。",