feat: scope articles by brand

This commit is contained in:
2026-05-20 15:37:25 +08:00
parent 5fb9d0b0dd
commit dd082e2ed1
72 changed files with 3213 additions and 432 deletions
+73 -25
View File
@@ -99,7 +99,7 @@ const enUS = {
email: 'Email',
loginIdentifier: 'Phone / Email',
password: 'Password',
rememberAccount: 'Remember account',
rememberAccount: 'Remember account and password',
forgotPassword: 'Forgot password',
},
membershipBlocked: {
@@ -137,6 +137,9 @@ const enUS = {
userFallback: 'Admin',
logout: 'Logout',
planFallback: 'Free Plan',
currentCompany: 'Company',
currentCompanyPlaceholder: 'Select company',
createCompany: 'Create company',
openNavigation: 'Open navigation',
collapseNavigation: 'Collapse navigation',
expandNavigation: 'Expand navigation',
@@ -169,6 +172,10 @@ const enUS = {
title: 'Welcome back',
description: 'Sign in with a real tenant account.',
},
brandOnboarding: {
title: 'Choose a brand',
description: 'Create the first brand and initialize its keyword library.',
},
workspace: {
title: 'Workspace',
description: 'Template shortcuts, overview data, and recent generations in one place.',
@@ -250,6 +257,47 @@ const enUS = {
description: 'The link you opened might have moved or no longer exists.',
},
},
onboardingBrand: {
eyebrow: 'Brand Onboarding',
title: 'Choose a brand or company',
subtitle: 'You have access to multiple brands or companies. Choose one to start working.',
createBrand: 'Create new brand or company',
createHint: 'After creation, keyword initialization starts automatically.',
emptyDescription:
'New users need to create a brand or company before entering the workspace.',
noWebsite: 'No website yet',
form: {
title: 'Create brand or company',
name: 'Brand or company name',
namePlaceholder: 'Example: GEO Console / Acme Furniture',
website: 'Website (optional)',
websitePlaceholder: 'https://example.com',
description: 'Description',
descriptionPlaceholder: 'Briefly describe the business, products, service area, or core value.',
},
ai: {
title: 'Generating brand or company questions',
stages: {
fetch: 'Fetching website and business context...',
generate: 'Generating candidate search terms for monitoring...',
refine: 'Deduplicating and filtering savable questions...',
},
},
questions: {
title: 'Select brand or company questions',
subtitle:
'Choose up to {limit} questions to save to the keyword library. You can add or edit more later.',
confirm: 'Save and enter workspace',
skip: 'Skip and enter',
empty: 'No usable questions were generated. You can enter the workspace and add them manually.',
selectionLimit: 'Choose up to {limit} questions.',
},
messages: {
nameRequired: 'Enter a brand or company name.',
questionRequired: 'Select at least one question, or skip and add them manually later.',
aiCharged: 'This generation consumed {count} AI point.',
},
},
notFound: {
title: 'This page got lost',
description:
@@ -723,7 +771,7 @@ const enUS = {
sourceURL: 'Article URL',
sourceTitle: 'Source title',
language: 'Article language',
brandName: 'Brand / subject',
brandName: 'Current company',
region: 'Region',
keywords: 'Optimization keywords',
preservePoints: 'Keep / emphasize',
@@ -738,17 +786,17 @@ const enUS = {
'Selected groups are retrieved in the backend and passed to generation as reference context.',
sourceURLPlaceholder: 'https://example.com/article',
sourceTitlePlaceholder: 'Optional. Citation titles are prefilled automatically.',
brandNamePlaceholder: 'Required. Type the target subject, or choose from the brand library.',
brandNamePlaceholder: 'Select the current company in the top bar first.',
regionPlaceholder: 'For example: Shanghai, East China, nationwide, North America',
keywordsPlaceholder:
'Type optimization keywords and press Enter. Brand-library options appear after choosing a subject.',
'Type optimization keywords and press Enter. Search-term options appear after selecting the current company.',
preservePointsPlaceholder: 'List facts, data, arguments, or examples that must be kept.',
avoidPointsPlaceholder: 'List claims, competitors, compliance risks, or wording to avoid.',
extraRequirementsPlaceholder:
'Add channel, structure, title style, or industry-specific instructions.',
queued: 'Rewrite task submitted and queued.',
sourceURLRequired: 'Enter the source article URL first.',
brandNameRequired: 'Enter the brand / subject first.',
brandNameRequired: 'Select the current company in the top bar first.',
submitError: 'Failed to submit rewrite task.',
backToList: 'Back to rewrites',
},
@@ -821,7 +869,7 @@ const enUS = {
drawerTitle: 'Template generation',
steps: {
basic: 'Basic info',
basicDesc: 'Language, template fields, and brand context',
basicDesc: 'Language, template fields, and company context',
structure: 'Structure',
structureDesc: 'Title and outline',
generate: 'Generate',
@@ -829,13 +877,13 @@ const enUS = {
},
sections: {
templateFields: 'Template fields',
brandContext: 'Brand context',
brandContext: 'Company context',
language: 'Language',
brandInfo: 'Brand',
brandLibrary: 'Brand library',
brandName: 'Company name',
brandInfo: 'Company',
brandLibrary: 'Company library',
brandName: 'Current company',
website: 'Official website',
brandSummary: 'AI brand summary',
brandSummary: 'AI company summary',
keywords: 'Keywords',
brandQuestions: 'Keyword library (search terms)',
primaryQuestion: 'Optimization keyword',
@@ -854,21 +902,21 @@ const enUS = {
inputParams: 'Input params',
},
hints: {
brandContext: 'Optional. Selecting a brand pulls in keywords and competitor context.',
brandContext: 'The current company pulls in keywords and competitor context.',
brandFlow:
'You can type a company name or choose one from the brand library. After you choose an optimization keyword, AI will prepare the summary, competitors, and title options.',
'Articles use the current company selected in the top bar. After you choose an optimization keyword, AI will prepare the summary, competitors, and title options.',
brandSummaryPlaceholder:
'After analysis, AI will summarize the brand angle, positioning, and content opportunities here.',
'After analysis, AI will summarize the company angle, positioning, and content opportunities here.',
keywords:
'Type keywords directly or reuse brand-library keywords. AI results will be merged into this list.',
brandQuestions:
'The optimization keyword controls the article spine; supplemental keywords broaden nearby search intent.',
supplementalQuestions:
'Choose up to 3 so the article covers related intent without drifting.',
questionEmpty: 'No questions are available for this brand yet.',
selectBrandFirst: 'Choose a library brand from the company name dropdown first.',
questionEmpty: 'No questions are available for this company yet.',
selectBrandFirst: 'Select the current company in the top bar first.',
competitors:
'Competitors stay editable. You can add rows manually; saving to the competitor library is only available after choosing a library brand.',
'Competitors stay editable. You can add rows manually; saving to the competitor library is available after selecting the current company.',
competitorEmptyEditable:
'No competitors yet. Add one manually or let AI fill the list first.',
templateFields:
@@ -883,7 +931,7 @@ const enUS = {
outlinePreview: 'The panel on the right updates from the current outline in real time.',
outlineEmpty: 'Generate the outline first to edit the article structure here.',
review: 'Check the final setup before creating the generation task.',
competitorEmpty: 'No competitors are available under this brand yet.',
competitorEmpty: 'No competitors are available under this company yet.',
customTitle: 'Use this field if you want to override the title manually.',
keyPoints: 'Add any key messages, angles, or style notes here.',
async:
@@ -904,7 +952,7 @@ const enUS = {
review: {
templateName: 'Template',
articleTitle: 'Article title',
brandContext: 'Brand context',
brandContext: 'Company context',
outline: 'Outline',
currentTemplate: 'Current template',
promptVisibility: 'Prompt visibility',
@@ -922,8 +970,8 @@ const enUS = {
actions: 'Actions',
},
placeholders: {
brandLibrary: 'Choose an existing brand from the library',
brandName: 'Enter a company name, or choose from the brand library',
brandLibrary: 'Choose the current company',
brandName: 'Current company from the top bar',
website: 'Official website, optional',
keywords: 'Type keywords and press Enter',
primaryQuestion: 'Choose one optimization keyword',
@@ -975,18 +1023,18 @@ const enUS = {
},
messages: {
requiredField: 'Please fill in {field} first.',
missingBrand: 'Enter or choose a company name first.',
missingBrand: 'Select the current company in the top bar first.',
selectBrandBeforeQuestion:
'Choose a library brand from the company name dropdown before selecting a question.',
'Select the current company in the top bar before continuing.',
missingPrimaryQuestion: 'Choose one primary brand question.',
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.',
selectBrandBeforeCompetitors:
'Choose a library brand from the company name dropdown before managing competitors.',
'Select the current company in the top bar before managing competitors.',
selectBrandBeforeFavorite:
'Choose a library brand from the company name dropdown before saving competitors.',
'Select the current company in the top bar before saving competitors.',
missingCompetitorName: 'Enter a competitor name before saving it to the library.',
savedCompetitor: 'Competitor saved to the brand library.',
unsavedCompetitor: 'Competitor removed from the brand library.',