20 KiB
20 KiB
Buzz Sheet Guide Platform
Summary
Build a Thai-first, mobile-responsive guide CMS that converts the ideas found across the five reference Google Sheets into polished character guide websites. It must feel like a game guide—not a spreadsheet—on both the public and admin sides.
The admin uses a visual Notion-style block editor with forms, cards, drag-and-drop sections, live preview, templates, autosave, and simple arithmetic formula fields. Published pages update from accepted autosaves, with Redis-backed Server-Sent Events (SSE) prompting connected public and admin clients to refetch authoritative state. Executable custom code, HTML, JavaScript, React, and arbitrary Tailwind input are excluded.
Product and UI
- Public routes:
/searchable character directory./[character]redirects to the character’s first visible page./[character]/[page]renders any custom page, including/mizuki/infoor/mizuki/combo.
- Admin routes:
/admin/login/admin/admin/[character]/[page]/admin/templates
- Allow unlimited characters, custom pages, custom page slugs, custom fields, navigation ordering, and a public note on every page.
- New characters and pages start hidden. A page is public only when both its character and the page are visible. Existing published pages update immediately after an accepted autosave; a character with no visible pages returns 404.
- Provide reusable templates derived from all five workbooks without importing or publishing their original content:
- Classic multi-page character guide
- Combo and rotation guide
- Combined one-page build
- Multi-role/reaction build
- Categorized team database
- Calculation-backed comparison guide
- Initialize official shadcn components with the
base-novapreset and use Tailwind v4 semantic tokens. Compose existing primitives before creating custom UI, and use only components appropriate to each interaction—not every component literally. - Use a permanent dark, game-inspired theme with no light-mode switcher or
next-themes. Keep decoration restrained, hierarchy strong, contrast accessible, and controls at least 44 px on touch surfaces. - Use Anuphan as the Thai-first interface font with appropriate system fallbacks. Reserve monospace typography for formulas and technical values, and test wrapping for long Thai labels and mixed-language game terms.
- Use one restrained brand accent, semantic status colors, and a limited chart palette. Element and rarity colors appear as badges or markers and never carry meaning alone. Honor reduced motion and keep animation limited to functional panel, drag, chart, and save-state feedback.
- Public pages use guide-oriented blocks rather than editable grids:
- Profile/hero
- Rich text and headings
- Notes and callouts
- Stat cards and custom key/value fields
- Ranked weapon/artifact/constellation comparisons
- Team compositions
- Combo/rotation steps
- Tables and charts
- Images, GIFs, galleries, tabs, accordions, sections, columns, links, and separators
- Build
/as a responsive visual portrait-card directory with search, compact role/element filters, metadata badges, loading skeletons, image fallbacks, and a useful empty state. Do not add a grid/list mode toggle in v1. - Give public character guides a sticky character header followed by horizontally scrollable route links. Add a sticky desktop “on this page” section rail and an equivalent titled drawer on mobile.
- Keep prose in a readable content column and allow team layouts, tables, galleries, and charts to use wider responsive regions.
- Compose recommendations with full
CardorItemstructures,Badgemetadata,Alertcallouts,Accordionfor secondary explanations,Tabsfor authored variants, andCarouselonly for image galleries. - Show weapon, artifact, and game-term details through
HoverCardon pointer devices and the same content in a titledDraweron touch devices. Never make hover the only way to access information. - Keep public comparisons author-curated and read-only. Use horizontal bars for ranked weapons/artifacts, grouped or stacked bars for team totals and member contributions, and cumulative or incremental bars/lines for constellations. Always show exact values and an accessible table fallback; do not chart qualitative recommendations or metrics with incompatible units.
- Admin editing uses a clean three-pane desktop workspace: a collapsible character/page/template
Sidebar, an editable center canvas, and a resizable right panel with Inspector and Live Preview tabs. On mobile, keep the canvas primary and move navigation, block insertion, and inspection into titled drawers. - Add a sticky admin toolbar with breadcrumbs, undo/redo, persistent autosave status, visibility, viewport
ToggleGroup, and preview actions. UseBadgeandSpinnerfor autosave status; reservesonnerfor failures, recovery, and completed explicit actions. - Edit rich text and simple values inline. Use the inspector for layout, data-source selection, formulas, responsive settings, and advanced properties. Support drag and keyboard reordering with visible selection and focus states.
- Use
CommandDialogfor navigation and block insertion. Use accessible contextual menus for block actions,AlertDialogfor destructive actions,Emptyfor unconfigured blocks, andSkeletonfor loading states. - Edit structured comparison data through a compact admin
Table, opening row creation and complex row editing in structured forms instead of turning the canvas into a spreadsheet. - Build forms with
FieldGroup,Field, andInputGroup. Formula fields use anfxaddon, field-reference autocomplete, calculated preview, and inline typed errors. - Comparison data can be entered efficiently in compact tables, but page construction and public rendering must never resemble Google Sheets.
Architecture and Interfaces
- Use Next.js 16.3.3 App Router, React 19, Bun 1.3.14, TypeScript, PostgreSQL, Drizzle, Better Auth, and Bun’s native S3 client.
- Follow the installed Next.js documentation for Server Components, Server Actions, caching, authentication, and deployment.
- Use one generic content model:
Character: slug, localized identity fields, media, ordering, visibility.Page: character, slug, title, navigation order, visibility, public note, version.Block: stable ID, type, schema version, validated configuration, responsive layout.DataSource: globally reusable structured rows/fields that can feed tables, rankings, cards, and charts.DataSourceVersion: immutable data snapshot used for dependency tracking, public rendering, and historical restoration.Template: reusable character/page/block structure without reference-sheet content.Revision: page snapshot, label, author, timestamp, and source version.SlugAlias: permanent redirect after character or page slug changes.Media: private object key, metadata, current references, and revision references.OutboxEvent: retryable post-commit cache invalidation and SSE notification.
- Add validation and migration handlers for every block schema. Unknown or future block types render a safe unsupported-block notice instead of breaking the page.
- Add a constrained
ComparisonChartblock configuration:dataSourceId: globally reusable structured data source.categoryField: label/category field used by the axis and table fallback.series: one to six numeric fields with authored labels and semantic chart color tokens.presentation:horizontal-bar,grouped-bar,stacked-bar, orline.normalization:raw,percent-of-total, orbaseline-100.sort:authored,ascending, ordescending.showValuesandshowLegend: authored display controls.tableFallback: always enabled and not removable by authors.- Validate a maximum of 20 visible categories and six series. Invalid or missing data renders a typed error or empty state without breaking the page.
- Render charts as isolated client components inside otherwise server-rendered public pages. Use shadcn
ChartContainer,ChartTooltip, andChartLegendwith Recharts v3, semantic chart tokens, responsive minimum dimensions, visible touch-friendly values, andaccessibilityLayer. - Calculated fields use a restricted arithmetic expression language—not executable code:
- Supports
+,-,*,/, parentheses, percentages, unary signs, field references, dependency ordering, circular-reference detection, and clear error states. - Admins access formulas only through an optional advanced setting on individual fields, with field-name autocomplete and a calculated preview.
- Imported regression fixtures may retain A1-style references internally, but normal admin editing uses stable named field references.
- Use deterministic decimal arithmetic without intermediate rounding. Store canonical decimal strings and apply configurable display precision with half-up rounding only for presentation.
- Treat blank or missing references, cycles, and division by zero as typed field errors. Invalid fields and their dependents render safe public error placeholders while unrelated valid changes publish normally.
- Hide formula expressions and internal error details from public visitors.
- Place evaluation behind a
FormulaEngineinterface so a larger engine can be added later without changing page or block schemas.
- Supports
- Store raw values and formulas as the source of truth; calculated results and public page DTOs are derived snapshots.
- Autosave 750 ms after the last edit, serialize mutations per page, and protect every mutation with versions. Each mutation sends
expectedVersion; conflicting changes are rejected and shown in a merge/reload dialog rather than silently overwriting work. Retry transient failures with capped exponential backoff, but do not retry validation or conflict responses automatically. - Version global data sources independently. Normal references follow the latest version. Revisions capture exact data-source versions; restoring a revision creates a new current page version pinned to those historical versions without changing other consumers. Provide an explicit “use latest” action to remove a pin.
- Keep named revisions indefinitely and hourly coalesced checkpoints for 30 days. Revision snapshots retain exact page, block, data-source-version, and media references. Restoring creates a new current version rather than deleting history.
- Cache validated public page snapshots by their page and data-source version vector. Use an external Redis-backed Next.js remote cache and tag handler so both application replicas share cache state and invalidations. Keep admin routes dynamic.
- Write cache invalidation and SSE work to a transactional outbox with the content mutation. A retrying worker invalidates affected page, directory, and data-source tags only after the database commit, then publishes typed Redis events.
- Provide Redis-backed, invalidation-only SSE streams for public pages, the character directory, and authenticated admin sessions. Events carry only opaque IDs and versions; clients refetch authoritative state on connection, reconnection, or notification. Send 90-second heartbeats, close streams after 30 minutes so clients reconnect, and disable Traefik response buffering. SSE is never a source of correctness and never carries page content.
- Authenticate through Google using Better Auth. Permit admin access only when the verified Google email equals
ADMIN_EMAIL; repeat authorization checks in every mutation, media, and administrative endpoint. - Upload PNG, JPEG, WebP, and GIF assets up to 20 MB through short-lived presigned requests. Serve them through same-origin
/media/[id]responses. Allow public access only while an asset is referenced by a currently visible snapshot; otherwise require admin authentication. Retain objects while referenced by either current content or retained revisions. - Applying a template clones independent pages, blocks, and data sources. Later template edits affect only future applications.
- Do not provide custom HTML, JavaScript, TypeScript, React, CSS, code blocks that execute, external scripts, arbitrary npm packages, or network-capable extensions.
Content lifecycle
flowchart LR
E[Admin editor] -->|autosave + expectedVersion| A[Authenticated mutation]
A --> V{Validate version,<br/>blocks and formulas}
V -->|conflict| C[Merge or reload dialog]
V -->|accepted| T[(PostgreSQL transaction)]
T --> P[Page and blocks]
T --> D[Immutable DataSource version]
T --> R[Revision and public snapshot]
T --> O[Outbox event]
O --> W[Retrying outbox worker]
W --> X[(Redis)]
X --> K[Shared cache and tag invalidation]
X --> S[SSE invalidation topics]
S --> U[Public and admin clients]
U -->|refetch authoritative version| Q[Public snapshot query]
Q --> K
K --> B[Guide block renderer]
V -->|field error| F[Typed field error]
F --> T
F --> B
Delivery and Deployment
- Implement internally in four gates:
- Database model, immutable data-source versions, revisions, transactional outbox, formula engine, and workbook-derived fixtures.
- Public renderer, templates, and responsive shadcn admin editor.
- Google authentication, reference-aware S3 media handling, Redis-backed caching and SSE, and conflict recovery.
- Production builds, browser tests, Docker, migrations, Kubernetes, and CI.
- Release to production once all four gates pass.
- During the public/editor gate, initialize shadcn with the
base-novapreset before adding components. Add only the official components required by the implemented surface, and review generated component source and Base UI composition after each addition. - Implement and commit each completed feature separately. Do not accumulate the project into one large commit.
- Keep every commit focused, independently reviewable, and limited to one coherent feature or supporting change.
- Run the feature’s relevant checks before committing it, and do not mix unrelated cleanup or formatting into feature commits.
- Use clear conventional commit messages such as
feat(editor): add page block orderingortest(formulas): add workbook regression corpus. - Provide native Kubernetes manifests through Kustomize, following the referenced Buzz Kubernetes conventions:
- Namespace
buzz-sheet - Two application replicas with rolling updates
- ClusterIP service on port 3000
- Traefik ingress for
sheet.sudloh.com - Externally provisioned
sheet-sudloh-com-tlssecret - Readiness/liveness health endpoint
- Requests of 500m CPU/512 MiB and limits of 1 CPU/1 GiB
- HPA from 2–6 replicas at 70% CPU
- PDB with minimum one available replica
- Least-privilege CI deployer RBAC
- Namespace
- Use the existing external PostgreSQL, Redis, and S3-compatible services through Kubernetes secrets; do not provision an in-cluster database or Redis instance.
- Share a stable
NEXT_SERVER_ACTIONS_ENCRYPTION_KEYacross replicas and set a deployment ID from the immutable image revision to protect Server Actions and client navigation during rolling updates. - Build immutable application and migration images in Gitea Actions, defaulting to
registry.neko-piranha.ts.net/astral/buzz-sheet. - Run a versioned Drizzle migration Job before rollout. Use expand/contract migrations so the previous application image remains compatible during rolling deployment.
- Produce deployment manifests and CI configuration but do not contact or mutate the production cluster during implementation.
Test Plan and Acceptance Criteria
- Commit sanitized fixtures containing workbook and sheet identity, A1-to-field mappings, inputs, formulas, and expected results without original guide text or media. Assert that the corpus contains exactly all 379 extracted formula cells: 314 use addition, 335 use division, 43 contain decimal literals, and none use functions, subtraction, multiplication, percentages, unary signs, cross-sheet references, or absolute references.
- Verify those 379 formulas with deterministic decimal results, then separately test precedence, subtraction, multiplication, percentages, unary signs, blank and missing references, cycles, dependency-propagated failures, rounding, and division by zero.
- Test block validation, schema migration, unknown-block fallback, templates, custom slugs, redirects, public notes, visibility, ordering, and revision restore.
- Test autosave debounce, serialization, transient retries, concurrent conflicts, checkpoint coalescing, named revisions, 30-day expiry, global data-source dependency updates, historical version pinning, “use latest,” and media retention.
- Test Google admin restrictions and authorization on every write/media endpoint.
- Test upload limits, file validation, failed upload recovery, and same-origin media delivery.
- Test cached public snapshots and immediate dependency-aware invalidation following accepted autosaves.
- Test SSE authorization, public-event privacy, heartbeat, reconnection and authoritative refetch, duplicate or missed notifications, and directory/page/admin topics.
- Run integration tests against two application instances sharing Redis to verify cross-replica cache invalidation and SSE delivery.
- Test character directory search and all generic route combinations.
- Test directory search/filter states, card image failures, empty results, sticky guide navigation, section navigation, long Thai labels, and characters with many custom pages.
- Test each comparison presentation with empty, invalid, negative, very large, and crowded datasets. Verify normalization, sorting, visible exact values, touch behavior, and the accessible table fallback.
- Test desktop panel resizing and collapse state, Inspector/Preview switching, inline editing, inspector synchronization, command insertion, mobile drawers, keyboard block movement, and autosave/conflict states.
- Run responsive browser and visual regression tests for common mobile, tablet, desktop, and wide-desktop sizes, including touch editing and Thai text overflow.
- Validate accessibility for keyboard navigation, focus states, charts, drawers, dialogs, menus, command search, drag alternatives, form labels, contrast, and reduced motion.
- Pass unit/integration tests,
bunx tsc --noEmit, linting, production application and migration image builds, andkubectl kustomize k8s/. - Acceptance requires that all reference guide structures can be reproduced using visual blocks and formula fields without custom code or a spreadsheet-style public interface.
Assumptions
- The interface and authored guide content are Thai-first; official character, weapon, and game terms may remain in their entered language.
- Public comparisons are authored and read-only in v1; visitors cannot construct custom comparison sets.
- Decorative artwork is optional and content-driven. The application shell remains restrained so guide imagery and comparison data stay dominant.
- The five supplied spreadsheets are design and formula references only. Their content is not automatically seeded or published.
- Content entry and migration are performed through the new admin editor.
- There is one administrator at launch.
- Formulas are permitted only as restricted arithmetic expressions; “no code” means no executable or presentation code.
- SSE is a live-refresh enhancement. PostgreSQL versions, public snapshots, and authoritative refetches remain the source of correctness.
- Named revisions remain indefinitely. Expired checkpoints release their media references only when no current content or other retained revision still references those assets.
- Advanced spreadsheet functions, collaborative cursor editing, comments, arbitrary plugins, and public user accounts are outside the initial release.