Skip to content

feat(blog): add /blog, /blog/$slug, /preview/$id (mock-backed)#604

Merged
ctkm-aelf merged 4 commits intomainfrom
feat/blog-landing-page
May 3, 2026
Merged

feat(blog): add /blog, /blog/$slug, /preview/$id (mock-backed)#604
ctkm-aelf merged 4 commits intomainfrom
feat/blog-landing-page

Conversation

@ctkm-aelf
Copy link
Copy Markdown
Collaborator

Summary

  • Adds three public routes — /blog, /blog/$slug, /preview/$id — backed by an in-bundle mock dataset shaped to match the Directus blog_articles collection. The fetch helpers in frontend/src/features/blog/mock-api.ts are stubs that match the shape of the real CDN endpoint, so wiring it up later is one line each.
  • Visual language follows the landing page, not strictly DESIGN.md (the two diverge on primary colour and hero font; the blog matches what's on screen). Long-form prose is rendered with react-markdown + remark-gfm + rehype-sanitize, with custom element renderers that map every block to the landing's existing Tailwind classes.
  • Preview is /preview/$id (UUID) — fetches any status and shows a "Preview mode" banner. Index and detail filter to status="published".
  • Auth shell (isPublicPath() in main.tsx) updated so unauthenticated visitors aren't bounced to /login on the new routes. Authenticated users are not redirected away either — the blog is readable in both states.
  • Codex review run (codex review --uncommitted) — gate: PASS. P1 (auth allowlist gap) was caught and fixed before this PR. One P2 remains and is intrinsic to the mock layer (draft article body bundled into JS); a SHIP NOTE comment in mock-api.ts calls it out for the CDN-wiring follow-up.
  • Documentation in docs/BLOG_IMPLEMENTATION.md covers schema, routes, design language, and the production checklist.

Differences from #524

#524 spec This PR Reason
?preview=<token> query param /preview/$id (UUID) Schema spec the maintainer later supplied says "UUID is the preview-URL secret"; this PR follows that.
VITE_BLOG_CDN_URL / VITE_BLOG_PREVIEW_TOKEN env-driven Mock API in the bundle, marked with SHIP NOTE No backend / CDN bucket yet (#525 still open). Wiring is a follow-up PR.
Frontmatter for title/date/author/slug Full Directus blog_articles shape (product, tags, series, author, hero_image, status, content_commit_sha, etc.) Maintainer supplied the Directus schema explicitly during scoping.
Styling per DESIGN.md Styling per the live landing Maintainer redirected: "lets look at landing page, we will ignore design.md for now."

Refs

Test plan

  • npm run dev (frontend), then visit:
    • http://localhost:3000/blog — 4 published articles, sorted newest first; loading skeleton flashes briefly
    • http://localhost:3000/blog/push-approvals-walkthrough — full article renders with hero, prose, code block, blockquote, lists, tag chips, author bio
    • http://localhost:3000/blog/aes-envelope-encryption — second article variant renders cleanly
    • http://localhost:3000/preview/9a0f6a4d-6a3a-4f8b-9b6c-001000000005 — draft article renders with purple "Preview mode" banner + Draft status pill at top
    • http://localhost:3000/blog/does-not-exist — "not found" card with back-link to /blog
    • http://localhost:3000/preview/00000000-0000-0000-0000-000000000000 — same not-found card
  • Landing page (/) is byte-identical — no link to the blog appears in the landing nav (blog discoverability decision deferred to follow-up)
  • Sign in, then revisit /blog and /blog/$slug — should NOT redirect to /dashboard; signed-in users can read the blog
  • Sign out, then visit /preview/<uuid> — should NOT redirect to /login
  • npm run build — clean, article-view chunk stays under ~50 KB gz
  • npm run lint — no new errors (15 pre-existing warnings in unrelated files)

🤖 Generated with Claude Code

ctkm-aelf and others added 4 commits May 4, 2026 00:20
…mock data

Three new public routes for a marketing blog. Data layer mirrors the
Directus blog_articles schema so the swap to the real CDN endpoint is
one line per fetch helper. Preview reads any status by UUID; detail and
index filter to status="published".

- frontend/src/features/blog: types, mock-data (5 articles), mock-api,
  utils, page components, and components/ (article-body uses
  react-markdown + remark-gfm + rehype-sanitize, custom element renderers
  matching the landing's Tailwind classes)
- main.tsx: extend isPublicPath() so unauthenticated visitors aren't
  bounced to /login on /blog, /blog/<slug>, or /preview/<uuid>
- router.tsx + lazy.ts: wire the three routes under rootRoute (no auth
  gate; authenticated users are not redirected away)
- package.json: react-markdown 10, remark-gfm 4, rehype-sanitize 6
  (~50 KB gz, isolated to the article-view chunk)
- docs/BLOG_IMPLEMENTATION.md: schema, routes, design language, and
  production checklist (CDN wiring, sitemap/RSS, SEO meta, env vars)
- designs/blog/: standalone HTML mockups from early exploration
  (archived as design history, not part of the React app)

Refs #524.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Strengthen BLOG_IMPLEMENTATION.md so the placeholder nature of
mock-api.ts and mock-data.ts is unambiguous: they exist as a
reference implementation while the Directus CMS + CDN endpoint are
being set up, and both will be deleted/rewritten when the real
backend lands.

- Status banner: name the files explicitly and call out that they
  will be deleted, not just "swapped"
- New "Mock layer (placeholder for Directus + CDN)" section
  replacing the terser "Mock vs production" — adds a Today/Future
  table, the file-by-file swap path, and the boundary contract
  (only mock-api.ts imports mock-data.ts, so the swap is one file)
- File map: annotate each entry with PLACEHOLDER vs KEEP
- Production checklist: split the "replace mock" item, add an
  explicit checklist row for adding VITE_BLOG_CDN_URL to docs/ENV.md

Refs #524.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The wizard freshness test computes a hash over the frontend source
closure. Adding the blog feature touched main.tsx, router.tsx, and
lazy.ts, which are part of that closure, so the committed bundle
hash drifted. Regenerated via `npm --prefix frontend run build:wizard`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ctkm-aelf ctkm-aelf merged commit 768b939 into main May 3, 2026
10 checks passed
@ctkm-aelf ctkm-aelf deleted the feat/blog-landing-page branch May 3, 2026 16:39
@ctkm-aelf
Copy link
Copy Markdown
Collaborator Author

closes issue #524

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant