feat(content): public Blog + Learn sections and static CMS pages (full-stack)

Adds the missing public-facing content pages and their admin authoring, all
powered by the existing content-svc Blog entity discriminated by `kind`.

Backend (content-svc):
- BlogKind enum += Learn, Page (reuses Blog CRUD/SEO/slug/publish for all three).
- SQL migration services/content/migrations/001_blog_kind_learn_page.sql
  (ALTER TYPE content.blog_kind ADD VALUE 'Learn','Page').

Frontend (public, Next.js):
- lib/content-api.ts: fetchArticles(kind) / fetchArticle(slug) / fetchPage(slug)
  with safe empty/null fallbacks.
- components/content: article-ui (card/list/detail + RTL prose), CmsPageContent,
  CmsRoute (admin-authored page or localized built-in fallback copy).
- Routes: /blog, /blog/[slug], /learn, /learn/[slug] and static pages
  /about /contact /careers /privacy /terms /cookies /help.
- Navbar "tutorials" → /learn; all footer links now resolve.

Admin:
- AdminResource: new `fixedValues` option (injects kind on create/update).
- learnConfig (kind=Learn) + pagesConfig (kind=Page) reuse the /v1/blogs endpoint;
  /admin/learn + /admin/pages routes + nav items.

i18n: blog, learn and 7 *Page namespaces added to both fa.json and en.json
(verified key parity); admin nav labels learn/pages. Frontend tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-11 22:43:25 +03:30
parent 6cf8716d7e
commit c92de06c28
25 changed files with 802 additions and 3 deletions
@@ -13,6 +13,6 @@ public enum JustifyKind { LEFT_JUSTIFY, CENTER_JUSTIFY, RIGHT_JUSTIFY, FULL_JUST
public enum AiInputType { None, TitleSuggest, BodySuggest, TranslateRtl, TranslateLtr, RemoveBG, UpscaleImage, TTS }
public enum RepeatSortStrategy { Manual, Alphabetical, Numerical, InsertOrder }
public enum AttrValueKind { fill, stroke, tracking, dropshadow }
public enum BlogKind { Blog, Landing }
public enum BlogKind { Blog, Landing, Learn, Page }
public enum SlideType { Hero, Promo, Tutorial, Category, Custom }
public enum ContainerFavoriteKind { Container }
@@ -0,0 +1,9 @@
-- Adds the Learn + Page values to the content.blog_kind enum so the Blog entity
-- can power the public Learn (tutorials) section and the static CMS pages
-- (About, Privacy, Terms, …) in addition to Blog/Landing.
--
-- Idempotent. ADD VALUE cannot run inside a transaction on older PG, so apply
-- each statement on its own (psql autocommit):
-- docker exec fr2-postgres sh -c 'PGPASSWORD="$POSTGRES_PASSWORD" psql -U "$POSTGRES_USER" -d flatrender -f -' < 001_blog_kind_learn_page.sql
ALTER TYPE content.blog_kind ADD VALUE IF NOT EXISTS 'Learn';
ALTER TYPE content.blog_kind ADD VALUE IF NOT EXISTS 'Page';