feat(blog+categories): Wave 3-5 — blog infrastructure, categories, content, navigation
- Install @astrojs/rss and setup content collections (blog + generators) - Create BlogLayout with BlogPosting schema, article OG tags, prev/next nav - Create blog post pages EN/RU ([slug].astro) with dynamic routing - Create blog index pages EN/RU with post cards and filtering - Create RSS feeds EN/RU with auto-discovery links - Create generator category pages EN/RU (5 categories each) - Add category navigation pills to homepage - Create Breadcrumbs, RelatedGenerators, RelatedPosts components - Add breadcrumbs to GeneratorLayout and BlogLayout - Wire RelatedGenerators into GeneratorLayout - Wire RelatedPosts into blog post pages - Create 8 EN + 8 RU blog post templates with placeholder content - Add blog link to LanguageSwitcher navigation - Add category and blog translation keys
This commit is contained in:
@@ -157,7 +157,7 @@
|
||||
"plan_name": "seo-blog-categories",
|
||||
"status": "active",
|
||||
"started_at": "2026-05-13T23:32:06.345Z",
|
||||
"updated_at": "2026-05-13T23:47:20.362Z",
|
||||
"updated_at": "2026-05-14T00:06:43.314Z",
|
||||
"session_ids": [
|
||||
"ses_1de6ede59ffexUwt5C0sddEWwR"
|
||||
],
|
||||
@@ -183,12 +183,12 @@
|
||||
"task_key": "todo:7",
|
||||
"task_label": "7",
|
||||
"task_title": "Update generatorSchema.ts with category field",
|
||||
"session_id": "ses_1dc44f7c9ffeRvYIFc2EOUSbNd",
|
||||
"session_id": "ses_1dc34addfffegfOdNPOQJJYU66",
|
||||
"agent": "Sisyphus-Junior",
|
||||
"category": "quick",
|
||||
"category": "visual-engineering",
|
||||
"started_at": "2026-05-13T23:42:40.563Z",
|
||||
"status": "running",
|
||||
"updated_at": "2026-05-13T23:47:20.363Z"
|
||||
"updated_at": "2026-05-14T00:06:43.315Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,7 +196,7 @@
|
||||
"active_plan": "/home/emil/Desktop/Coding/AI/Randify.pro/.sisyphus/plans/seo-blog-categories.md",
|
||||
"started_at": "2026-05-13T23:32:06.345Z",
|
||||
"status": "active",
|
||||
"updated_at": "2026-05-13T23:47:20.362Z",
|
||||
"updated_at": "2026-05-14T00:06:43.314Z",
|
||||
"session_ids": [
|
||||
"ses_1de6ede59ffexUwt5C0sddEWwR"
|
||||
],
|
||||
@@ -222,12 +222,12 @@
|
||||
"task_key": "todo:7",
|
||||
"task_label": "7",
|
||||
"task_title": "Update generatorSchema.ts with category field",
|
||||
"session_id": "ses_1dc44f7c9ffeRvYIFc2EOUSbNd",
|
||||
"session_id": "ses_1dc34addfffegfOdNPOQJJYU66",
|
||||
"agent": "Sisyphus-Junior",
|
||||
"category": "quick",
|
||||
"category": "visual-engineering",
|
||||
"started_at": "2026-05-13T23:42:40.563Z",
|
||||
"status": "running",
|
||||
"updated_at": "2026-05-13T23:47:20.363Z"
|
||||
"updated_at": "2026-05-14T00:06:43.315Z"
|
||||
}
|
||||
},
|
||||
"agent": "atlas"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Task 11: Install @astrojs/rss and set up content collections config
|
||||
|
||||
- Package installed: @astrojs/rss (added 9 packages, 511 total)
|
||||
- File updated: src/content/config.ts
|
||||
- Imported z from astro:content
|
||||
- Added blog collection with type: "content"
|
||||
- Blog schema fields: title (max 120), description (max 160), pubDate, modDate (optional), draft (default false), lang (en/ru), category (tutorial/guide/news/tips), tags (default []), ogImage (optional, image()), relatedGenerators (optional), relatedPosts (optional)
|
||||
- Preserved existing generators collection (type: "data", schema: generatorSchema)
|
||||
- Directories created: src/content/blog/en/ and src/content/blog/ru/
|
||||
- Build verification: npm run build passed with 64 pages, 0 errors
|
||||
@@ -0,0 +1,38 @@
|
||||
Task 12: BlogLayout.astro with BlogPosting JSON-LD and Article OG Meta Tags
|
||||
|
||||
Verification: npm run build — PASSED (64 pages built)
|
||||
|
||||
File: src/layouts/BlogLayout.astro
|
||||
|
||||
Features implemented:
|
||||
1. Wraps BaseLayout, passing title, description, ogImage.
|
||||
2. Injects BlogPosting JSON-LD schema via slot="head" with:
|
||||
- @type: BlogPosting
|
||||
- headline (from title prop)
|
||||
- author: Organization { name: "Randify" }
|
||||
- publisher: Organization { name: "Randify", logo: ImageObject }
|
||||
- datePublished (ISO 8601)
|
||||
- dateModified (ISO 8601, falls back to datePublished)
|
||||
- url (canonical)
|
||||
- description
|
||||
- inLanguage
|
||||
3. OG type set to "article" via <meta property="og:type" content="article" /> in slot="head".
|
||||
4. Article-specific meta tags added:
|
||||
- article:published_time
|
||||
- article:modified_time
|
||||
5. Styled container: max-w-2xl mx-auto, matching GeneratorLayout spacing.
|
||||
6. Header with title, accent dot, publication date, and conditional "Updated" label.
|
||||
7. Prev/next post navigation with bilingual labels (EN/RU), arrow icons, and focus rings.
|
||||
8. Named slot "related" for RelatedPosts component.
|
||||
|
||||
Props accepted:
|
||||
- title: string
|
||||
- description: string
|
||||
- pubDate: Date | string
|
||||
- modDate?: Date | string
|
||||
- ogImage?: string
|
||||
- lang?: Lang
|
||||
- prevPost?: { slug: string; title: string }
|
||||
- nextPost?: { slug: string; title: string }
|
||||
|
||||
Build output: 64 pages, 0 errors.
|
||||
@@ -0,0 +1,7 @@
|
||||
Task 13: EN blog post page ([slug].astro)
|
||||
- Build: PASS (77 pages)
|
||||
- Generated: dist/blog/test-post/index.html
|
||||
- Verified: post title present in HTML
|
||||
- Verified: BlogPosting JSON-LD schema present
|
||||
- Verified: article:published_time meta tag present
|
||||
- Slug derived from post.id with en/ prefix and .md extension stripped
|
||||
@@ -0,0 +1,16 @@
|
||||
Task 14: Create RU blog post page src/pages/ru/blog/[slug].astro
|
||||
|
||||
Build result: PASS
|
||||
Pages built: 77
|
||||
RU blog post page: compiled successfully (no RU blog posts yet, so no static pages generated)
|
||||
EN blog post page: /blog/test-post/index.html generated
|
||||
|
||||
Files created/modified:
|
||||
- src/pages/ru/blog/[slug].astro (created)
|
||||
- src/layouts/BlogLayout.astro (fixed locale-aware prev/next links)
|
||||
|
||||
Verification:
|
||||
- npm run build exited with code 0
|
||||
- No TypeScript or Astro errors
|
||||
- RU [slug].astro correctly filters getCollection("blog") by lang === "ru"
|
||||
- BlogLayout prev/next navigation updated to use locale-aware paths (/ru/blog/ for RU, /blog/ for EN)
|
||||
@@ -0,0 +1,36 @@
|
||||
Task 15: Blog Index Pages — Evidence
|
||||
======================================
|
||||
|
||||
Build Status: PASS (76 pages, exit code 0)
|
||||
|
||||
Files Created:
|
||||
- src/pages/blog/index.astro
|
||||
- src/pages/ru/blog/index.astro
|
||||
|
||||
Files Modified:
|
||||
- src/i18n/translations.ts (added blogDesc, readMore, noPosts keys for EN + RU)
|
||||
|
||||
Build Output Verification:
|
||||
- dist/blog/index.html (6893 bytes)
|
||||
- dist/ru/blog/index.html (7342 bytes)
|
||||
|
||||
EN Page Features:
|
||||
- Queries getCollection("blog"), filters lang === "en" and draft !== true
|
||||
- Sorts by pubDate descending
|
||||
- Links to /blog/[slug]/
|
||||
- Date formatting: toLocaleDateString("en-US", ...)
|
||||
- JSON-LD Blog schema with blogPost array
|
||||
|
||||
RU Page Features:
|
||||
- Queries getCollection("blog"), filters lang === "ru" and draft !== true
|
||||
- Sorts by pubDate descending
|
||||
- Links to /ru/blog/[slug]/
|
||||
- Date formatting: toLocaleDateString("ru-RU", ...)
|
||||
- JSON-LD Blog schema with blogPost array
|
||||
|
||||
Both pages:
|
||||
- Use BaseLayout with title and description props
|
||||
- Include OG/Twitter Card meta tags (inherited from BaseLayout)
|
||||
- Include canonical + hreflang alternates (inherited from BaseLayout)
|
||||
- Show empty state "No posts yet" / "Пока нет записей." when collection is empty
|
||||
- Display category badge + tag badges per post card
|
||||
@@ -0,0 +1,20 @@
|
||||
Task 16: RSS Feeds
|
||||
|
||||
Files created:
|
||||
- src/pages/rss.xml.ts (EN feed)
|
||||
- src/pages/ru/rss.xml.ts (RU feed)
|
||||
|
||||
File modified:
|
||||
- src/layouts/BaseLayout.astro (added <link rel="alternate" type="application/rss+xml"> for both EN and RU feeds)
|
||||
|
||||
Build: PASS (npm run build exited 0)
|
||||
|
||||
Generated feeds:
|
||||
- dist/rss.xml exists (287 bytes)
|
||||
- dist/ru/rss.xml exists (404 bytes)
|
||||
|
||||
Feed content verified:
|
||||
- EN: title="Randify Blog", language="en", description present, link=https://randify.pro/
|
||||
- RU: title="Блог Randify", language="ru", description present, link=https://randify.pro/
|
||||
|
||||
Note: Feeds are currently empty because no blog posts exist in src/content/blog/ yet. When posts are added with lang="en"/"ru" and draft !== true, they will appear automatically.
|
||||
@@ -0,0 +1,26 @@
|
||||
Task 17: Category Pages for Generators
|
||||
======================================
|
||||
|
||||
Build Result: PASS
|
||||
Pages built: 76
|
||||
|
||||
EN Category Pages Created:
|
||||
- dist/generators/category/gaming/index.html
|
||||
- dist/generators/category/security/index.html
|
||||
- dist/generators/category/decision-making/index.html
|
||||
- dist/generators/category/creative/index.html
|
||||
- dist/generators/category/utility/index.html
|
||||
|
||||
RU Category Pages Created:
|
||||
- dist/ru/generators/category/gaming/index.html
|
||||
- dist/ru/generators/category/security/index.html
|
||||
- dist/ru/generators/category/decision-making/index.html
|
||||
- dist/ru/generators/category/creative/index.html
|
||||
- dist/ru/generators/category/utility/index.html
|
||||
|
||||
Verification:
|
||||
- EN gaming page contains: "Gaming generators for tabletop RPGs" and generator titles (Lottery, Rock Paper Scissors, etc.)
|
||||
- RU gaming page contains: "Игровые генераторы для настольных RPG" and generator titles (Колесо фортуны, Лотерея, etc.)
|
||||
- All 5 categories render correctly with getStaticPaths
|
||||
- Breadcrumb JSON-LD schema included on each page
|
||||
- Generators filtered dynamically by category field from src/data/generators.ts
|
||||
@@ -0,0 +1,16 @@
|
||||
Task 18: Category Navigation Pills on Homepage
|
||||
|
||||
Files modified:
|
||||
- src/pages/index.astro
|
||||
- src/pages/ru/index.astro
|
||||
- src/i18n/translations.ts
|
||||
|
||||
Build result: PASS (85 pages, 0 errors)
|
||||
|
||||
Verification:
|
||||
- dist/index.html contains category navigation pills
|
||||
- dist/ru/index.html contains localized category navigation pills
|
||||
- All 5 categories present: Gaming (6), Security (3), Decision Making (4), Creative (5), Utility (10)
|
||||
- Links point to /generators/category/[category]/ (EN) and /ru/generators/category/[category]/ (RU)
|
||||
- Pills styled with Tailwind: rounded-full, bg-zinc-800/60, border-zinc-700/50, hover:bg-accent/20, hover:text-accent
|
||||
- Responsive flex-wrap layout
|
||||
@@ -0,0 +1,28 @@
|
||||
Task 19: Create Breadcrumbs.astro Component — Evidence
|
||||
=====================================================
|
||||
|
||||
1. File Created
|
||||
---------------
|
||||
Path: src/components/Breadcrumbs.astro
|
||||
|
||||
2. Component Specification
|
||||
--------------------------
|
||||
- Props interface: { items: Array<{ label: string, href?: string }> }
|
||||
- Renders <nav aria-label="Breadcrumb"> containing <ol>
|
||||
- Each item rendered as <li> with "/" separator between items
|
||||
- Last item (no href or end of array): <span aria-current="page"> with text-zinc-400
|
||||
- Items with href: <a> with text-zinc-500 hover:text-accent transition-colors and focus ring
|
||||
- Tailwind styling: text-sm, muted zinc colors, accent hover state
|
||||
|
||||
3. Build Verification
|
||||
---------------------
|
||||
Command: npm run build
|
||||
Result: PASS (64 pages built, 0 errors)
|
||||
|
||||
4. Example Usage
|
||||
----------------
|
||||
<Breadcrumbs items={[
|
||||
{ label: "Home", href: "/" },
|
||||
{ label: "Gaming", href: "/generators/category/gaming/" },
|
||||
{ label: "Dice Roller" }
|
||||
]} />
|
||||
@@ -0,0 +1,19 @@
|
||||
Task 20: RelatedGenerators.astro
|
||||
|
||||
Files changed:
|
||||
- src/components/RelatedGenerators.astro (created)
|
||||
- src/layouts/GeneratorLayout.astro (updated — integrated RelatedGenerators)
|
||||
- src/i18n/translations.ts (updated — added relatedGenerators key EN+RU)
|
||||
|
||||
Build result:
|
||||
- npm run build: PASSED
|
||||
- 76 pages built, 0 errors
|
||||
|
||||
Component behavior:
|
||||
- Accepts props: currentSlug, category, optional lang
|
||||
- Filters generators by category, excludes current slug, limits to 4
|
||||
- Returns null (renders nothing) if no related generators exist
|
||||
- Locale-aware titles and links (EN / RU)
|
||||
- Compact card layout: icon + title in a responsive 2-column grid
|
||||
- Uses Tailwind with existing design tokens (accent color, zinc palette)
|
||||
- Includes focus-visible ring for accessibility
|
||||
@@ -0,0 +1,24 @@
|
||||
Task 21: RelatedPosts Component
|
||||
================================
|
||||
|
||||
Files created:
|
||||
- src/components/RelatedPosts.astro
|
||||
|
||||
Files modified:
|
||||
- src/i18n/translations.ts (added relatedPosts key: en="Related posts", ru="Похожие статьи")
|
||||
- src/pages/blog/[slug].astro (imported RelatedPosts, wired into "related" slot)
|
||||
- src/pages/ru/blog/[slug].astro (imported RelatedPosts, wired into "related" slot)
|
||||
|
||||
Build result: PASS
|
||||
- 83 pages built successfully
|
||||
- No errors or warnings
|
||||
|
||||
Implementation details:
|
||||
- Accepts post: CollectionEntry<"blog">
|
||||
- Uses getCollection to fetch non-draft posts in same language
|
||||
- Scores related posts by tag overlap count
|
||||
- Shows top 3 posts with score > 0
|
||||
- Hides section entirely if no related posts found
|
||||
- Renders title, date, and description snippet for each related post
|
||||
- Links use correct /blog/ or /ru/blog/ base path
|
||||
- Styled with Tailwind matching existing dark theme (zinc palette, accent color)
|
||||
@@ -0,0 +1,25 @@
|
||||
Task 22: Create 8 English blog post templates
|
||||
Date: 2026-05-14
|
||||
Build result: PASS
|
||||
Pages built: 85
|
||||
|
||||
Files created:
|
||||
- src/content/blog/en/what-is-cryptographically-secure-randomness.md
|
||||
- src/content/blog/en/how-to-create-strong-passwords.md
|
||||
- src/content/blog/en/dice-notation-explained.md
|
||||
- src/content/blog/en/psychology-of-randomness.md
|
||||
- src/content/blog/en/rng-in-gaming.md
|
||||
- src/content/blog/en/random-team-picker.md
|
||||
- src/content/blog/en/color-theory-palettes.md
|
||||
- src/content/blog/en/uuid-vs-sequential-ids.md
|
||||
|
||||
Build output confirms all 8 posts were generated alongside the existing test-post:
|
||||
/blog/what-is-cryptographically-secure-randomness/index.html
|
||||
/blog/how-to-create-strong-passwords/index.html
|
||||
/blog/dice-notation-explained/index.html
|
||||
/blog/psychology-of-randomness/index.html
|
||||
/blog/rng-in-gaming/index.html
|
||||
/blog/random-team-picker/index.html
|
||||
/blog/color-theory-palettes/index.html
|
||||
/blog/uuid-vs-sequential-ids/index.html
|
||||
/blog/test-post/index.html
|
||||
@@ -0,0 +1,20 @@
|
||||
Build completed: 93 pages
|
||||
RU blog posts generated (8):
|
||||
- /ru/blog/what-is-cryptographically-secure-randomness/index.html
|
||||
- /ru/blog/how-to-create-strong-passwords/index.html
|
||||
- /ru/blog/dice-notation-guide/index.html
|
||||
- /ru/blog/psychology-of-randomness/index.html
|
||||
- /ru/blog/rng-in-gaming/index.html
|
||||
- /ru/blog/random-team-selection/index.html
|
||||
- /ru/blog/color-theory-guide/index.html
|
||||
- /ru/blog/uuid-vs-sequential-ids/index.html
|
||||
Files created:
|
||||
- src/content/blog/ru/what-is-cryptographically-secure-randomness.md
|
||||
- src/content/blog/ru/how-to-create-strong-passwords.md
|
||||
- src/content/blog/ru/dice-notation-guide.md
|
||||
- src/content/blog/ru/psychology-of-randomness.md
|
||||
- src/content/blog/ru/rng-in-gaming.md
|
||||
- src/content/blog/ru/random-team-selection.md
|
||||
- src/content/blog/ru/color-theory-guide.md
|
||||
- src/content/blog/ru/uuid-vs-sequential-ids.md
|
||||
Build timestamp: 2026-05-14T03:08:00+03:00
|
||||
@@ -0,0 +1,16 @@
|
||||
Task 24 Evidence: Blog Navigation Link
|
||||
=======================================
|
||||
|
||||
Build: PASS
|
||||
- npm run build completed successfully (84 pages built)
|
||||
|
||||
Verification:
|
||||
- EN blog link (/blog/) found in dist/index.html
|
||||
- RU blog link (/ru/blog/) found in 38 RU pages including dist/ru/index.html
|
||||
|
||||
Changes made:
|
||||
- Modified src/components/LanguageSwitcher.astro
|
||||
- Added Blog / Блог link between language toggle and About link
|
||||
- Link points to /blog/ on EN pages and /ru/blog/ on RU pages
|
||||
- Used existing translation keys (blog: "Blog", blog: "Блог") from src/i18n/translations.ts
|
||||
- Styled consistently with existing About/Privacy links
|
||||
@@ -0,0 +1,17 @@
|
||||
Task: Add Breadcrumbs component to generator and blog layouts
|
||||
Date: 2026-05-14
|
||||
|
||||
1. npm run build
|
||||
Result: PASS (93 pages built, exit code 0)
|
||||
|
||||
2. Grep dist/generators/dice/index.html for breadcrumb items:
|
||||
- Found: Home, Gaming, Dice
|
||||
- Command: grep -o 'Home\|Gaming\|Dice' dist/generators/dice/index.html
|
||||
|
||||
3. Grep dist/blog/test-post/index.html for breadcrumb items:
|
||||
- Found: Home, Blog, Test Post
|
||||
- Command: grep -o 'Home\|Blog\|Test Post' dist/blog/test-post/index.html
|
||||
|
||||
Files modified:
|
||||
- src/layouts/GeneratorLayout.astro
|
||||
- src/layouts/BlogLayout.astro
|
||||
@@ -46,3 +46,154 @@
|
||||
- Removed `.optional()` from `category` in `src/lib/generator-schema.ts` (line 24).
|
||||
- Build passes with 64 pages and 0 Zod errors — schema now strictly enforces the category field.
|
||||
- Order matters: all JSONs must have the field BEFORE removing `.optional()` from schema, otherwise build fails immediately.
|
||||
|
||||
## Task 11: Install @astrojs/rss and set up content collections config
|
||||
|
||||
- Installed `@astrojs/rss` package (9 packages added, 511 total).
|
||||
- Updated `src/content/config.ts`:
|
||||
- Added `z` import from `astro:content`.
|
||||
- Defined `blog` collection with `type: "content"` and full schema: title (max 120), description (max 160), pubDate, modDate (optional), draft (default false), lang (en|ru), category (tutorial|guide|news|tips), tags (default []), ogImage (image().optional()), relatedGenerators (optional), relatedPosts (optional).
|
||||
- Exported `collections = { generators, blog }` — preserved existing generators collection untouched.
|
||||
- Created empty directories: `src/content/blog/en/` and `src/content/blog/ru/`.
|
||||
- Build passes with 64 pages and 0 errors.
|
||||
- Note: `src/data/generators.ts` remains the runtime loader for generators; the content collection config is for build-time validation only.
|
||||
|
||||
## Task 19: Reusable Breadcrumb Component
|
||||
|
||||
- Created `src/components/Breadcrumbs.astro` as a standalone, reusable component.
|
||||
- Props: `items: Array<{ label: string, href?: string }>`.
|
||||
- Accessibility: `<nav aria-label="Breadcrumb">` with `<ol>` list semantics; last item gets `aria-current="page"`.
|
||||
- Separators are plain-text `/` wrapped in `<span aria-hidden="true">` to avoid screen-reader noise.
|
||||
- Styling matches existing dark theme: `text-sm`, `text-zinc-500` for links, `text-zinc-400` for current page, `hover:text-accent` on links, plus `focus-visible:ring-accent` for keyboard navigation.
|
||||
- Intentionally NOT added to `BaseLayout` — breadcrumbs are opt-in per page (e.g., category landing pages, blog posts).
|
||||
- Build passes with 64 pages and 0 errors.
|
||||
|
||||
## Task 12: BlogLayout.astro
|
||||
|
||||
- Created `src/layouts/BlogLayout.astro` wrapping `BaseLayout`.
|
||||
- Injected `BlogPosting` JSON-LD schema with Organization author/publisher, headline, dates, and canonical URL.
|
||||
- Set OG type to `article` and added `article:published_time` / `article:modified_time` meta tags via `slot="head"`.
|
||||
- Styled container follows `GeneratorLayout` pattern (`max-w-2xl mx-auto px-4 py-12 sm:py-20`).
|
||||
- Prev/next navigation supports bilingual labels, arrow icons, and keyboard-focus rings.
|
||||
- Exposed named slot `related` for a `RelatedPosts` component.
|
||||
- Props: `title`, `description`, `pubDate`, `modDate`, `ogImage`, `lang`, `prevPost?`, `nextPost?`.
|
||||
- `PostLink` interface: `{ slug: string; title: string }`.
|
||||
- Build passes (64 pages, 0 errors).
|
||||
|
||||
|
||||
## Task 16: RSS Feeds (2026-05-14)
|
||||
|
||||
- Astro RSS endpoint pattern: `src/pages/rss.xml.ts` exports `GET(context)` and returns `rss({...})` from `@astrojs/rss`.
|
||||
- `context.site` is automatically populated from `astro.config.mjs` `site` field.
|
||||
- Use `getCollection("blog")` then filter by `post.data.lang` and `post.data.draft` to keep languages separate and exclude drafts.
|
||||
- Empty collections produce a valid but item-less RSS feed; build succeeds with a warning.
|
||||
- RSS auto-discovery links should be `<link rel="alternate" type="application/rss+xml" title="..." href="..." />` inside `<head>`.
|
||||
|
||||
## Task 15: Blog Index Pages (EN + RU)
|
||||
|
||||
- Created `src/pages/blog/index.astro` and `src/pages/ru/blog/index.astro` using `getCollection("blog")`.
|
||||
- Filter pattern: `post.data.lang === "en"/"ru" && post.data.draft !== true` — ensures no cross-language mixing and no draft posts leak.
|
||||
- Sort: `b.data.pubDate.getTime() - a.data.pubDate.getTime()` for newest-first.
|
||||
- Each post card displays: title, description, formatted date (locale-aware), category badge, tag badges.
|
||||
- Links: `/blog/${post.slug}/` for EN, `/ru/blog/${post.slug}/` for RU.
|
||||
- Added translation keys `blogDesc`, `readMore`, `noPosts` to both EN and RU objects.
|
||||
- JSON-LD `Blog` schema injected via `slot="head"` with nested `BlogPosting` array for each post.
|
||||
- Empty collection gracefully handled with localized "No posts yet" message.
|
||||
- Build passes with 76 pages; warnings about empty blog collection are expected and harmless.
|
||||
- Reused styling patterns from `src/pages/index.astro` (max-w-4xl, blur accent orb, card hover states).
|
||||
|
||||
## Task 17: Category Pages for Generators
|
||||
|
||||
- Created `src/pages/generators/category/[category].astro` and `src/pages/ru/generators/category/[category].astro`.
|
||||
- Both pages use `getStaticPaths` with 5 hardcoded categories: gaming, security, decision-making, creative, utility.
|
||||
- Generators are filtered dynamically via `generators.filter((g) => g.category === category)` — no hardcoded generator lists.
|
||||
- Each page displays a breadcrumb (Home > Categories > [Category Name] / Главная > Категории > [Name]), category-specific title/description, and a grid of `GeneratorCard` components matching the `index.astro` layout.
|
||||
- JSON-LD BreadcrumbList schema is injected via `<Fragment slot="head">` for SEO.
|
||||
- Category names are formatted for display: EN uses hyphen-split capitalization ("Decision Making"), RU uses a translation map ("Принятие решений").
|
||||
- Build passes with 76 pages (up from 64), confirming all 10 category variants (EN + RU × 5 categories) are statically generated.
|
||||
- Verified `dist/generators/category/gaming/index.html` exists and contains correct content.
|
||||
|
||||
## Task 20: RelatedGenerators.astro
|
||||
|
||||
- Created `src/components/RelatedGenerators.astro` with props `currentSlug`, `category`, and optional `lang`.
|
||||
- Filter logic: `generators.filter(g => g.category === category && g.slug !== currentSlug).slice(0, 4)`.
|
||||
- Edge case handled: returns `null` (renders nothing) when no related generators exist.
|
||||
- Locale-aware: titles and hrefs switch between EN (`/generators/{slug}/`) and RU (`/ru/generators/{slug}/`) based on `Astro.currentLocale`.
|
||||
- Compact card design: icon (from inline `icons` map matching `GeneratorCard`) + title in a responsive 2-column grid (`grid-cols-1 sm:grid-cols-2`).
|
||||
- Styling uses existing Tailwind tokens: `border-zinc-800`, `hover:border-accent`, `bg-accent/10`, `focus-visible:ring-accent`.
|
||||
- Integrated into `GeneratorLayout.astro` at the bottom of the content area (after FAQ block).
|
||||
- Added `relatedGenerators` translation key to both EN ("Related generators") and RU ("Похожие генераторы").
|
||||
- Build passes with 76 pages and 0 errors.
|
||||
|
||||
## Task 13: EN Blog Post Page ([slug].astro)
|
||||
|
||||
- Created `src/pages/blog/[slug].astro` with `getStaticPaths` querying `getCollection("blog")`.
|
||||
- Filtered posts by `lang === "en"` and excluded drafts.
|
||||
- Sorted posts by `pubDate` ascending to calculate prev/next navigation chronologically.
|
||||
- Derived clean slugs by stripping `en/` prefix and `.md` extension from `post.id` (Astro content collection `id` includes file extension).
|
||||
- Rendered markdown via `post.render()` -> `<Content />`.
|
||||
- Passed prev/next data to `BlogLayout` using `prevPost` / `nextPost` props (matching existing BlogLayout interface from Task 12).
|
||||
- Build passes with 77 pages; verified output at `dist/blog/test-post/index.html`.
|
||||
|
||||
## Task 14: RU Blog Post Page ([slug].astro)
|
||||
|
||||
- Created `src/pages/ru/blog/[slug].astro` as an exact structural copy of the EN file, with two language-specific changes:
|
||||
1. Filter: `post.data.lang === "ru"` (instead of `"en"`).
|
||||
2. Slug normalization: stripped `ru/` prefix from `post.id` (instead of `en/`).
|
||||
- Prev/next navigation uses `BlogLayout` props (`prevPost`, `nextPost`) just like EN.
|
||||
- Rendered markdown via `post.render()` -> `<Content />` inside `<article>`.
|
||||
- Passed `lang="ru"` prop to `BlogLayout` for explicit locale context.
|
||||
- **Bug fix in `BlogLayout.astro`:** prev/next links were hardcoded to `/blog/${slug}/`, breaking RU navigation. Added `const blogBase = lang === "ru" ? "/ru/blog/" : "/blog/";` and updated both prev/next `href` attributes to use `${blogBase}${slug}/`. This makes BlogLayout fully locale-aware without duplicating layout logic.
|
||||
- Build passes with 77 pages; RU `[slug].astro` compiles correctly. No RU blog posts exist yet, so no static RU post pages are generated (expected — will populate in Tasks 22-23).
|
||||
- Evidence saved to `.sisyphus/evidence/task-14-ru-post.txt`.
|
||||
|
||||
## Task 21: RelatedPosts Component
|
||||
|
||||
- Created `src/components/RelatedPosts.astro` accepting `post: CollectionEntry<"blog">`.
|
||||
- Tag overlap scoring: `p.data.tags.filter(t => post.data.tags.includes(t)).length`.
|
||||
- Filters out current post, requires `score > 0`, sorts descending, slices top 3.
|
||||
- Edge case: returns `null` (renders nothing) when no related posts — section is completely hidden.
|
||||
- Language isolation: `getCollection("blog", ({ data }) => !data.draft && data.lang === post.data.lang)` ensures EN and RU posts never mix.
|
||||
- Slug derivation: `post.id.replace(/^(en|ru)\//, "").replace(/\.md$/, "")` — Astro content collection `id` includes subdirectory prefix and file extension.
|
||||
- Blog base path computed from `post.data.lang`: `/blog/` for EN, `/ru/blog/` for RU.
|
||||
- Styled with Tailwind matching existing dark theme: `border-zinc-800`, `hover:border-accent`, `line-clamp-2` for description snippets.
|
||||
- Wired into both `src/pages/blog/[slug].astro` and `src/pages/ru/blog/[slug].astro` via `<RelatedPosts post={post} slot="related" />`.
|
||||
- Added `relatedPosts` translation key: EN "Related posts", RU "Похожие статьи".
|
||||
- Build passes with 83 pages and 0 errors.
|
||||
|
||||
## Task 23: Russian Blog Post Templates (8 posts)
|
||||
|
||||
- Created 8 Russian blog post templates in `src/content/blog/ru/`.
|
||||
- All posts include valid frontmatter with `lang: ru`, matching the EN schema.
|
||||
- Frontmatter fields: title, description, pubDate, modDate, draft: false, lang: ru, category: guide, tags, relatedGenerators.
|
||||
- `relatedGenerators` uses English slugs (same convention as EN posts).
|
||||
- Staggered dates used: 2025-01-16 through 2025-01-23.
|
||||
- Body content: 3 paragraphs of Russian placeholder text per post.
|
||||
- Build passes with 93 pages (up from 77), confirming all 8 RU blog posts are statically generated.
|
||||
- RU blog index (`/ru/blog/index.html`) and individual post pages (`/ru/blog/<slug>/index.html`) render correctly.
|
||||
- Note: First build attempt hit a transient Astro module resolution bug on `src/pages/ru/generators/list.astro`; retry succeeded without changes.
|
||||
|
||||
## Task 26: Add Breadcrumbs to GeneratorLayout and BlogLayout
|
||||
|
||||
- Imported `Breadcrumbs` into `src/layouts/GeneratorLayout.astro` and `src/layouts/BlogLayout.astro`.
|
||||
- **GeneratorLayout:** Added `<Breadcrumbs items={...} />` between the existing back-link nav and the `<header>`.
|
||||
- Items: Home → Category → Generator Title.
|
||||
- Category label is locale-aware: maps `generator.category` to translation keys (`catGaming`, `catSecurity`, etc.) for both EN and RU.
|
||||
- Category href is prefixed with `/ru` when `isRu` is true.
|
||||
- **BlogLayout:** Added `<Breadcrumbs items={...} />` inside `<article>`, before the `<header>`.
|
||||
- Items: Home → Blog → Post Title.
|
||||
- Labels and hrefs switch based on `lang === "ru"`.
|
||||
- `npm run build` passes with 93 pages.
|
||||
- Verified breadcrumb text presence in built HTML:
|
||||
- `dist/generators/dice/index.html` contains "Home", "Gaming", "Dice".
|
||||
- `dist/blog/test-post/index.html` contains "Home", "Blog", "Test Post".
|
||||
- Evidence saved to `.sisyphus/evidence/task-26-breadcrumbs.txt`.
|
||||
|
||||
## Task 22: Create 8 English Blog Post Templates
|
||||
|
||||
- Created 8 markdown files in `src/content/blog/en/` with valid frontmatter matching the blog schema.
|
||||
- All posts use `category: guide`, `draft: false`, `lang: en`, and include `relatedGenerators`.
|
||||
- Dates are staggered from 2025-01-15 (newest) down to 2025-01-08 (oldest) for correct newest-first sorting.
|
||||
- Body content is 3 paragraphs of placeholder text ("This article will explore... Stay tuned for the full guide.") per instructions.
|
||||
- Build passes with 85 pages (up from 77), confirming 8 new static blog post pages were generated.
|
||||
- Existing `test-post.md` remains untouched; total EN blog collection now has 9 posts.
|
||||
|
||||
@@ -638,7 +638,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 2)
|
||||
|
||||
- [ ] 11. Install @astrojs/rss, set up content collections config
|
||||
- [x] 11. Install @astrojs/rss, set up content collections config
|
||||
|
||||
**What to do**:
|
||||
- Run `npm install @astrojs/rss`
|
||||
@@ -705,7 +705,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 3)
|
||||
|
||||
- [ ] 12. Create BlogLayout.astro with BlogPosting schema
|
||||
- [x] 12. Create BlogLayout.astro with BlogPosting schema
|
||||
|
||||
**What to do**:
|
||||
- Create `src/layouts/BlogLayout.astro`
|
||||
@@ -752,7 +752,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 3)
|
||||
|
||||
- [ ] 13. Create blog post page EN ([slug].astro)
|
||||
- [x] 13. Create blog post page EN ([slug].astro)
|
||||
|
||||
**What to do**:
|
||||
- Create `src/pages/blog/[slug].astro`
|
||||
@@ -799,7 +799,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 3)
|
||||
|
||||
- [ ] 14. Create blog post page RU ([slug].astro)
|
||||
- [x] 14. Create blog post page RU ([slug].astro)
|
||||
|
||||
**What to do**:
|
||||
- Create `src/pages/ru/blog/[slug].astro`
|
||||
@@ -842,7 +842,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 3)
|
||||
|
||||
- [ ] 15. Create blog index pages (EN + RU)
|
||||
- [x] 15. Create blog index pages (EN + RU)
|
||||
|
||||
**What to do**:
|
||||
- Create `src/pages/blog/index.astro` — list all EN blog posts sorted by date (newest first)
|
||||
@@ -889,7 +889,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 3)
|
||||
|
||||
- [ ] 16. Create RSS feeds (EN + RU)
|
||||
- [x] 16. Create RSS feeds (EN + RU)
|
||||
|
||||
**What to do**:
|
||||
- Create `src/pages/rss.xml.ts` — English RSS feed
|
||||
@@ -941,7 +941,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 3)
|
||||
|
||||
- [ ] 17. Create category pages for generators (EN + RU)
|
||||
- [x] 17. Create category pages for generators (EN + RU)
|
||||
|
||||
**What to do**:
|
||||
- Create `src/pages/generators/category/[category].astro`
|
||||
@@ -991,7 +991,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 4)
|
||||
|
||||
- [ ] 18. Add category navigation to homepage
|
||||
- [x] 18. Add category navigation to homepage
|
||||
|
||||
**What to do**:
|
||||
- Update `src/pages/index.astro` to show category pills/links above or below the generator grid
|
||||
@@ -1037,7 +1037,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 4)
|
||||
|
||||
- [ ] 19. Create Breadcrumbs component
|
||||
- [x] 19. Create Breadcrumbs component
|
||||
|
||||
**What to do**:
|
||||
- Create `src/components/Breadcrumbs.astro`
|
||||
@@ -1084,7 +1084,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 4)
|
||||
|
||||
- [ ] 20. Create RelatedGenerators component
|
||||
- [x] 20. Create RelatedGenerators component
|
||||
|
||||
**What to do**:
|
||||
- Create `src/components/RelatedGenerators.astro`
|
||||
@@ -1133,7 +1133,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 4)
|
||||
|
||||
- [ ] 21. Create RelatedPosts component
|
||||
- [x] 21. Create RelatedPosts component
|
||||
|
||||
**What to do**:
|
||||
- Create `src/components/RelatedPosts.astro`
|
||||
@@ -1190,7 +1190,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 4)
|
||||
|
||||
- [ ] 22. Create 8 blog post templates EN
|
||||
- [x] 22. Create 8 blog post templates EN
|
||||
|
||||
**What to do**:
|
||||
- Create 8 markdown files in `src/content/blog/en/` with correct frontmatter:
|
||||
@@ -1255,7 +1255,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 5)
|
||||
|
||||
- [ ] 23. Create 8 blog post templates RU
|
||||
- [x] 23. Create 8 blog post templates RU
|
||||
|
||||
**What to do**:
|
||||
- Create 8 markdown files in `src/content/blog/ru/` with Russian titles/descriptions
|
||||
@@ -1307,7 +1307,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 5)
|
||||
|
||||
- [ ] 24. Add blog link to navigation
|
||||
- [x] 24. Add blog link to navigation
|
||||
|
||||
**What to do**:
|
||||
- Update `src/components/LanguageSwitcher.astro` or add a new nav component
|
||||
@@ -1355,7 +1355,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 5)
|
||||
|
||||
- [ ] 25. Add related generators to generator pages
|
||||
- [x] 25. Add related generators to generator pages
|
||||
|
||||
**What to do**:
|
||||
- Update `src/layouts/GeneratorLayout.astro` to import and render `<RelatedGenerators />`
|
||||
@@ -1401,7 +1401,7 @@ Max Concurrent: 6 (Wave 1), 4 (Wave 2), 6 (Wave 3), 8 (Wave 4), 2 (Wave 5)
|
||||
|
||||
**Commit**: YES (Wave 5)
|
||||
|
||||
- [ ] 26. Add breadcrumbs to generator and blog layouts
|
||||
- [x] 26. Add breadcrumbs to generator and blog layouts
|
||||
|
||||
**What to do**:
|
||||
- Update `src/layouts/GeneratorLayout.astro` to include `<Breadcrumbs />`
|
||||
|
||||
Reference in New Issue
Block a user