feat(blog): add /blog, /blog/$slug, /preview/$id (mock-backed)#604
Merged
feat(blog): add /blog, /blog/$slug, /preview/$id (mock-backed)#604
Conversation
…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>
Collaborator
Author
|
closes issue #524 |
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/blog,/blog/$slug,/preview/$id— backed by an in-bundle mock dataset shaped to match the Directusblog_articlescollection. The fetch helpers infrontend/src/features/blog/mock-api.tsare stubs that match the shape of the real CDN endpoint, so wiring it up later is one line each.DESIGN.md(the two diverge on primary colour and hero font; the blog matches what's on screen). Long-form prose is rendered withreact-markdown+remark-gfm+rehype-sanitize, with custom element renderers that map every block to the landing's existing Tailwind classes./preview/$id(UUID) — fetches anystatusand shows a "Preview mode" banner. Index and detail filter tostatus="published".isPublicPath()inmain.tsx) updated so unauthenticated visitors aren't bounced to/loginon the new routes. Authenticated users are not redirected away either — the blog is readable in both states.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); aSHIP NOTEcomment inmock-api.tscalls it out for the CDN-wiring follow-up.docs/BLOG_IMPLEMENTATION.mdcovers schema, routes, design language, and the production checklist.Differences from #524
?preview=<token>query param/preview/$id(UUID)VITE_BLOG_CDN_URL/VITE_BLOG_PREVIEW_TOKENenv-drivenSHIP NOTEblog_articlesshape (product,tags,series,author,hero_image,status,content_commit_sha, etc.)DESIGN.mdRefs
docs/BLOG_IMPLEMENTATION.md)Test plan
npm run dev(frontend), then visit:http://localhost:3000/blog— 4 published articles, sorted newest first; loading skeleton flashes brieflyhttp://localhost:3000/blog/push-approvals-walkthrough— full article renders with hero, prose, code block, blockquote, lists, tag chips, author biohttp://localhost:3000/blog/aes-envelope-encryption— second article variant renders cleanlyhttp://localhost:3000/preview/9a0f6a4d-6a3a-4f8b-9b6c-001000000005— draft article renders with purple "Preview mode" banner + Draft status pill at tophttp://localhost:3000/blog/does-not-exist— "not found" card with back-link to/bloghttp://localhost:3000/preview/00000000-0000-0000-0000-000000000000— same not-found card/) is byte-identical — no link to the blog appears in the landing nav (blog discoverability decision deferred to follow-up)/blogand/blog/$slug— should NOT redirect to/dashboard; signed-in users can read the blog/preview/<uuid>— should NOT redirect to/loginnpm run build— clean,article-viewchunk stays under ~50 KB gznpm run lint— no new errors (15 pre-existing warnings in unrelated files)🤖 Generated with Claude Code