Skip to content

feat: Configurable site content — hero, stats, about text editable from admin - #6

Open
jerry-shimizutech wants to merge 1 commit into
mainfrom
feature/configurable-content
Open

feat: Configurable site content — hero, stats, about text editable from admin#6
jerry-shimizutech wants to merge 1 commit into
mainfrom
feature/configurable-content

Conversation

@jerry-shimizutech

@jerry-shimizutech jerry-shimizutech commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Summary

New SiteContent model with multi-language support. All homepage text now configurable from admin.

Backend

  • SiteContent model — flexible key-value store with 5 language columns (en, ja, ko, tl, zh)
  • Public APIGET /api/v1/site-contents returns flat key→translations map (single request)
  • Admin API — Full CRUD at /api/v1/admin/site-contents behind require_staff!
  • 29 seeded entries across hero, stats, featured, and about sections

Frontend

  • useSiteContent hook — module-level cache, language-aware t(key, fallback) helper
  • HomePage — hero badge, subtitle, title lines, stats, featured section all from API
  • Graceful fallback — i18n hardcoded values shown while API loads (no blank flash)
  • Content Admin page — grouped by section, inline editing, all 5 languages per entry
  • Admin nav — Content item added with FileText icon

Architecture

  • SiteContent handles PAGE CONTENT (hero text, stats, about paragraphs)
  • i18n locale files continue handling UI CHROME (buttons, nav, form labels)
  • npm run build passes with zero errors

Greptile Overview

Greptile Summary

Adds a flexible SiteContent model for managing multi-language page content across the site. The implementation cleanly separates page content (hero text, stats, about paragraphs) from UI chrome (buttons, nav labels), allowing non-technical staff to update homepage and about page text without code changes.

Key changes:

  • Backend: SiteContent model with 5 language columns (en, ja, ko, tl, zh), public read-only API, admin CRUD behind require_staff!, 29 seeded entries
  • Frontend: useSiteContent hook with module-level caching, graceful fallbacks to i18n hardcoded values during load, admin UI with section grouping and inline editing
  • Architecture: Clean separation of concerns — SiteContent for page content, i18n files for UI chrome

Issue found:

  • Typo in HomePage.tsx:36prizePpool should be prizePool (causes fallback to hardcoded i18n value)

Confidence Score: 4/5

  • Safe to merge after fixing the typo — well-architected feature with proper auth and caching
  • Confidence reduced from 5 to 4 due to typo in translation key that will prevent prize pool stat from loading from API (falls back to i18n). Otherwise, excellent implementation with proper security (staff-only admin), clean architecture (model/controller/hook separation), and good UX (module caching, graceful fallbacks)
  • Fix the typo in web/src/pages/HomePage.tsx:36 before merging

Important Files Changed

Filename Overview
api/app/models/site_content.rb Simple model with validations and fallback language helper - well designed
api/app/controllers/api/v1/admin/site_contents_controller.rb Full CRUD admin controller with staff authentication - properly secured
api/db/seeds/site_contents.rb Comprehensive seed data for 29 entries across hero, stats, featured, about sections
web/src/hooks/useSiteContent.ts Module-level cache with language-aware translation helper and cache invalidation
web/src/pages/HomePage.tsx Hero, stats, featured sections now use site content API with fallbacks - one typo in translation key
web/src/pages/admin/ContentAdmin.tsx Full admin UI with section grouping, inline editing, and CRUD operations

Sequence Diagram

sequenceDiagram
    participant User
    participant Browser
    participant HomePage
    participant useSiteContent
    participant API
    participant Rails
    participant DB

    Note over User,DB: Public Content Fetch Flow
    User->>Browser: Visit homepage
    Browser->>HomePage: Render
    HomePage->>useSiteContent: useSiteContent()
    
    alt Cache exists
        useSiteContent-->>HomePage: Return cached content
    else No cache
        useSiteContent->>API: GET /api/v1/site-contents
        API->>Rails: SiteContentsController#index
        Rails->>DB: SiteContent.all.order()
        DB-->>Rails: Return all entries
        Rails->>Rails: Build flat key→translations map
        Rails-->>API: JSON {key: {en, ja, ko, tl, zh}}
        API-->>useSiteContent: SiteContentMap
        useSiteContent->>useSiteContent: Cache result
        useSiteContent-->>HomePage: Return content
    end
    
    HomePage->>HomePage: Render with t(key, fallback)
    HomePage-->>Browser: Display content
    Browser-->>User: Show page

    Note over User,DB: Admin Edit Flow
    User->>Browser: Navigate to /admin/content
    Browser->>ContentAdmin: Render (requires staff auth)
    ContentAdmin->>API: GET /api/v1/admin/site-contents
    API->>Rails: Admin::SiteContentsController#index
    Rails->>DB: SiteContent.all (grouped by section)
    DB-->>Rails: Return entries
    Rails-->>API: JSON {site_contents: {section: [entries]}}
    API-->>ContentAdmin: Grouped entries
    ContentAdmin-->>User: Show editable sections

    User->>ContentAdmin: Edit & save entry
    ContentAdmin->>API: PUT /api/v1/admin/site-contents/:id
    API->>Rails: Admin::SiteContentsController#update
    Rails->>DB: Update SiteContent
    DB-->>Rails: Success
    Rails-->>API: Updated entry
    API-->>ContentAdmin: Confirm save
    ContentAdmin->>useSiteContent: invalidateSiteContentCache()
    useSiteContent->>useSiteContent: Clear module cache
    ContentAdmin->>API: Refetch content
    ContentAdmin-->>User: Show updated data
Loading

Last reviewed commit: be39f75

…ge support

- New SiteContent model with key-value pairs for flexible content management
- Public API: GET /api/v1/site-contents returns all content as key→translations map
- Admin API: CRUD endpoints at /api/v1/admin/site-contents (staff-only)
- useSiteContent hook with module-level caching and language-aware t() helper
- HomePage updated: hero badge, subtitle, title, stats, featured section all from API
- Graceful fallback to i18n hardcoded values while API loads
- Content Admin page: grouped by section, inline editing, 5 languages per entry
- Seeded 29 content entries across hero, stats, featured, and about sections
- npm run build passes with zero errors
@jerry-shimizutech

Copy link
Copy Markdown
Contributor Author

@greptile review

@coderabbitai

coderabbitai Bot commented Feb 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/configurable-content

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

13 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

{ value: t('stats.since'), label: t('stats.sinceLabel'), icon: Calendar },
{ value: sc('stat_competitors', t('stats.competitors')), label: sc('stat_competitors_label', t('stats.competitorsLabel')), icon: Users },
{ value: sc('stat_countries', t('stats.countries')), label: sc('stat_countries_label', t('stats.countriesLabel')), icon: Globe },
{ value: sc('stat_prize_pool', t('stats.prizePpool')), label: sc('stat_prize_pool_label', t('stats.prizePoolLabel')), icon: Trophy },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: prizePpool should be prizePool (extra 'p')

Suggested change
{ value: sc('stat_prize_pool', t('stats.prizePpool')), label: sc('stat_prize_pool_label', t('stats.prizePoolLabel')), icon: Trophy },
{ value: sc('stat_prize_pool', t('stats.prizePool')), label: sc('stat_prize_pool_label', t('stats.prizePoolLabel')), icon: Trophy },
Prompt To Fix With AI
This is a comment left during a code review.
Path: web/src/pages/HomePage.tsx
Line: 36:36

Comment:
typo: `prizePpool` should be `prizePool` (extra 'p')

```suggestion
    { value: sc('stat_prize_pool', t('stats.prizePool')), label: sc('stat_prize_pool_label', t('stats.prizePoolLabel')), icon: Trophy },
```

How can I resolve this? If you propose a fix, please make it concise.

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