feat(cms): Phase A — page type selector, translation UI, SEO cleanup
Some checks failed
Some checks failed
Content page editor improvements: - Page type selector: Content Page / Landing Page dropdown (sets template) - Title language tabs: translate page titles per language (same pattern as sections) - Content language tabs: translate page content per language - Meta description language tabs: translatable SEO descriptions - Template-driven section palette: template defines which sections are available (store landing pages hide Pricing, platform homepages show all) - Hide content editor when Landing Page selected, hide sections when Content Page Schema changes (migration cms_003): - Add meta_description_translations column (JSON) to content_pages - Drop meta_keywords column (obsolete, ignored by all search engines since 2009) - Remove meta keywords tag from storefront and platform base templates API + service updates: - title_translations, content_translations, meta_description_translations added to create/update schemas, route handlers, and service methods Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,19 +24,27 @@ class ContentPageCreate(BaseModel):
|
||||
description="URL-safe identifier (about, faq, contact, etc.)",
|
||||
)
|
||||
title: str = Field(..., max_length=200, description="Page title")
|
||||
title_translations: dict[str, str] | None = Field(
|
||||
None, description="Title translations keyed by language code"
|
||||
)
|
||||
content: str = Field(..., description="HTML or Markdown content")
|
||||
content_translations: dict[str, str] | None = Field(
|
||||
None, description="Content translations keyed by language code"
|
||||
)
|
||||
content_format: str = Field(
|
||||
default="html", description="Content format: html or markdown"
|
||||
)
|
||||
template: str = Field(
|
||||
default="default",
|
||||
max_length=50,
|
||||
description="Template name (default, minimal, modern)",
|
||||
description="Template name (default, minimal, modern, full)",
|
||||
)
|
||||
meta_description: str | None = Field(
|
||||
None, max_length=300, description="SEO meta description"
|
||||
)
|
||||
meta_keywords: str | None = Field(None, max_length=300, description="SEO keywords")
|
||||
meta_description_translations: dict[str, str] | None = Field(
|
||||
None, description="Meta description translations keyed by language code"
|
||||
)
|
||||
is_published: bool = Field(default=False, description="Publish immediately")
|
||||
show_in_footer: bool = Field(default=True, description="Show in footer navigation")
|
||||
show_in_header: bool = Field(default=False, description="Show in header navigation")
|
||||
@@ -53,11 +61,13 @@ class ContentPageUpdate(BaseModel):
|
||||
"""Schema for updating a content page (admin)."""
|
||||
|
||||
title: str | None = Field(None, max_length=200)
|
||||
title_translations: dict[str, str] | None = None
|
||||
content: str | None = None
|
||||
content_translations: dict[str, str] | None = None
|
||||
content_format: str | None = None
|
||||
template: str | None = Field(None, max_length=50)
|
||||
meta_description: str | None = Field(None, max_length=300)
|
||||
meta_keywords: str | None = Field(None, max_length=300)
|
||||
meta_description_translations: dict[str, str] | None = None
|
||||
is_published: bool | None = None
|
||||
show_in_footer: bool | None = None
|
||||
show_in_header: bool | None = None
|
||||
@@ -78,11 +88,13 @@ class ContentPageResponse(BaseModel):
|
||||
store_name: str | None
|
||||
slug: str
|
||||
title: str
|
||||
title_translations: dict[str, str] | None = None
|
||||
content: str
|
||||
content_translations: dict[str, str] | None = None
|
||||
content_format: str
|
||||
template: str | None = None
|
||||
meta_description: str | None
|
||||
meta_keywords: str | None
|
||||
meta_description_translations: dict[str, str] | None = None
|
||||
is_published: bool
|
||||
published_at: str | None
|
||||
display_order: int
|
||||
@@ -135,7 +147,6 @@ class StoreContentPageCreate(BaseModel):
|
||||
meta_description: str | None = Field(
|
||||
None, max_length=300, description="SEO meta description"
|
||||
)
|
||||
meta_keywords: str | None = Field(None, max_length=300, description="SEO keywords")
|
||||
is_published: bool = Field(default=False, description="Publish immediately")
|
||||
show_in_footer: bool = Field(default=True, description="Show in footer navigation")
|
||||
show_in_header: bool = Field(default=False, description="Show in header navigation")
|
||||
@@ -152,7 +163,6 @@ class StoreContentPageUpdate(BaseModel):
|
||||
content: str | None = None
|
||||
content_format: str | None = None
|
||||
meta_description: str | None = Field(None, max_length=300)
|
||||
meta_keywords: str | None = Field(None, max_length=300)
|
||||
is_published: bool | None = None
|
||||
show_in_footer: bool | None = None
|
||||
show_in_header: bool | None = None
|
||||
@@ -187,7 +197,6 @@ class PublicContentPageResponse(BaseModel):
|
||||
content: str
|
||||
content_format: str
|
||||
meta_description: str | None
|
||||
meta_keywords: str | None
|
||||
published_at: str | None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user