docs: add changelog, roadmap, and feedback design spec
This commit is contained in:
@@ -0,0 +1,205 @@
|
|||||||
|
# Changelog, Roadmap & Feedback Design Spec
|
||||||
|
|
||||||
|
**Date:** 2026-03-30
|
||||||
|
**Status:** Approved
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Add a public `/changelog` page with three tabs (What's New, What's Next, Give Feedback), auth-gated upvoting on roadmap items, a shared feedback form available as a standalone page and a modal triggered from the nav/footer, and an admin view for reviewing submissions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- Publicly share release notes (changelog) and upcoming work (roadmap)
|
||||||
|
- Let authenticated users upvote roadmap items to signal priority
|
||||||
|
- Collect structured user feedback (bug / feature / general) with optional email
|
||||||
|
- Store feedback in Spring Boot DB, reviewable in the admin panel
|
||||||
|
- Add "Send Feedback" entry point to TopNav and homepage footer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Routes
|
||||||
|
|
||||||
|
| Route | Description |
|
||||||
|
|---|---|
|
||||||
|
| `/changelog` | Three-tab page: What's New / What's Next / Give Feedback |
|
||||||
|
| `/feedback` | Standalone feedback page — full-width `FeedbackForm` |
|
||||||
|
| `/admin/feedback` | Admin table of feedback submissions |
|
||||||
|
|
||||||
|
Tab state on `/changelog` is driven by URL hash (`#whats-new`, `#whats-next`, `#give-feedback`) so tabs are deep-linkable. The "Don't see what you need? Give us feedback →" nudge on the roadmap tab links to `#give-feedback`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sanity Schema (two new document types)
|
||||||
|
|
||||||
|
### `changelogEntry`
|
||||||
|
|
||||||
|
| Field | Type | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `title` | string | Display title of the release |
|
||||||
|
| `version` | string | e.g. `v0.4` |
|
||||||
|
| `publishedAt` | datetime | Controls ordering and display date |
|
||||||
|
| `tags` | array of string | Values: `feature`, `fix`, `improvement` |
|
||||||
|
| `body` | Portable Text | Rich text body |
|
||||||
|
|
||||||
|
### `roadmapItem`
|
||||||
|
|
||||||
|
| Field | Type | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `title` | string | Short item title |
|
||||||
|
| `description` | text | Plain-text description |
|
||||||
|
| `status` | string enum | `planned` \| `in-progress` \| `shipped` |
|
||||||
|
| `order` | number | Manual sort order within each status group |
|
||||||
|
|
||||||
|
Both use the existing Sanity project (`zal102rv`, dataset `production`). GROQ queries added to `lib/sanity.ts` alongside existing `postsQuery`. Pages use `revalidate = 60` ISR, consistent with guides.
|
||||||
|
|
||||||
|
The Sanity `_id` of each `roadmapItem` serves as the foreign key in Spring Boot's votes table — no additional ID field required.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Spring Boot Backend
|
||||||
|
|
||||||
|
### New Tables
|
||||||
|
|
||||||
|
**`feedback`**
|
||||||
|
|
||||||
|
| Column | Type | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | UUID, PK | |
|
||||||
|
| `category` | enum | `BUG`, `FEATURE_REQUEST`, `GENERAL` |
|
||||||
|
| `message` | text | Max 1000 chars, enforced at API level |
|
||||||
|
| `email` | varchar, nullable | Optional follow-up email |
|
||||||
|
| `ip_address` | varchar | Captured server-side for spam visibility |
|
||||||
|
| `user_agent` | varchar | Captured server-side for spam visibility |
|
||||||
|
| `reviewed` | boolean, default false | Admin marks reviewed |
|
||||||
|
| `created_at` | timestamp | |
|
||||||
|
|
||||||
|
**`roadmap_votes`**
|
||||||
|
|
||||||
|
| Column | Type | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `user_id` | UUID, FK → users | |
|
||||||
|
| `sanity_item_id` | varchar | Sanity `_id` of the roadmap item |
|
||||||
|
| `created_at` | timestamp | |
|
||||||
|
| | UNIQUE(user_id, sanity_item_id) | DB-enforced one vote per user per item |
|
||||||
|
|
||||||
|
### New API Endpoints (Spring Boot)
|
||||||
|
|
||||||
|
| Method | Path | Auth | Notes |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `POST` | `/api/v1/feedback` | None | Creates feedback record; captures IP + user agent server-side |
|
||||||
|
| `GET` | `/api/v1/roadmap/votes?ids=...` | Optional | Returns vote counts + current user's voted state for a list of Sanity IDs |
|
||||||
|
| `POST` | `/api/v1/roadmap/{sanityId}/vote` | Required | Toggles vote on/off; returns new count and voted state |
|
||||||
|
|
||||||
|
### Next.js Proxy Routes
|
||||||
|
|
||||||
|
All three endpoints are proxied via `app/api/` — client code never calls Spring Boot directly, consistent with the existing proxy pattern.
|
||||||
|
|
||||||
|
| Next.js route | Proxies to |
|
||||||
|
|---|---|
|
||||||
|
| `POST /api/feedback` | `POST /api/v1/feedback` |
|
||||||
|
| `GET /api/roadmap/votes` | `GET /api/v1/roadmap/votes` |
|
||||||
|
| `POST /api/roadmap/[sanityId]/vote` | `POST /api/v1/roadmap/:id/vote` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Frontend Components
|
||||||
|
|
||||||
|
### New Files
|
||||||
|
|
||||||
|
| File | Type | Responsibility |
|
||||||
|
|---|---|---|
|
||||||
|
| `app/(app)/changelog/page.tsx` | RSC | Fetches Sanity changelog + roadmap data, renders tab shell |
|
||||||
|
| `components/ChangelogTabs.tsx` | `"use client"` | Hash-based tab switcher, renders all three tab panels |
|
||||||
|
| `components/RoadmapItem.tsx` | `"use client"` | Single roadmap item with optimistic upvote toggle |
|
||||||
|
| `components/FeedbackForm.tsx` | `"use client"` | Shared form (category + message + email), posts to `/api/feedback` |
|
||||||
|
| `components/FeedbackModal.tsx` | `"use client"` | Backdrop + dialog wrapper around `FeedbackForm` |
|
||||||
|
| `app/(app)/feedback/page.tsx` | RSC | Standalone page rendering `FeedbackForm` full-width |
|
||||||
|
| `app/admin/feedback/page.tsx` | RSC | Admin table of submissions, sortable + filterable |
|
||||||
|
| `app/api/feedback/route.ts` | API route | Proxy to Spring Boot |
|
||||||
|
| `app/api/roadmap/votes/route.ts` | API route | Proxy to Spring Boot |
|
||||||
|
| `app/api/roadmap/[sanityId]/vote/route.ts` | API route | Proxy to Spring Boot |
|
||||||
|
|
||||||
|
### Modified Files
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|---|---|
|
||||||
|
| `lib/sanity.ts` | Add `changelogEntriesQuery` and `roadmapItemsQuery` |
|
||||||
|
| `components/TopNav.tsx` | Add "Send Feedback" ghost button that opens `FeedbackModal` |
|
||||||
|
| `app/page.tsx` | Add "Send Feedback" link to footer alongside Privacy / Terms |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## UI Design
|
||||||
|
|
||||||
|
### `/changelog` Page Header
|
||||||
|
|
||||||
|
```
|
||||||
|
BATTL BUILDERS (small caps label)
|
||||||
|
Updates (h1)
|
||||||
|
What we've shipped and what's coming next. (subhead)
|
||||||
|
|
||||||
|
[ What's New ] [ What's Next ] [ Give Feedback ] (tab bar, amber underline on active)
|
||||||
|
```
|
||||||
|
|
||||||
|
### What's New Tab
|
||||||
|
|
||||||
|
Two-column entry layout: left column is version + date (120px fixed), right column is tags + title + body. Entries separated by bottom border, ordered newest first.
|
||||||
|
|
||||||
|
Tag values rendered as small pill badges:
|
||||||
|
- `feature` → amber
|
||||||
|
- `fix` → green
|
||||||
|
- `improvement` → blue
|
||||||
|
|
||||||
|
### What's Next Tab
|
||||||
|
|
||||||
|
Items grouped under "In Progress" and "Planned" headings (Shipped items not shown — they migrate to the changelog). Each item is a card with:
|
||||||
|
- **Left:** upvote button (chevron up + count) — bordered pill, amber when voted
|
||||||
|
- **Center:** title + description
|
||||||
|
- **Right:** status badge (In Progress = amber, Planned = muted)
|
||||||
|
|
||||||
|
Unauthenticated users clicking upvote are prompted to sign in (toast or redirect). Voted state is toggled optimistically — rolls back on error.
|
||||||
|
|
||||||
|
Footer nudge: *"Don't see what you need? Give us feedback →"* links to `#give-feedback`.
|
||||||
|
|
||||||
|
### Give Feedback Tab & `/feedback` Page
|
||||||
|
|
||||||
|
Full-width form:
|
||||||
|
- Category + email in a two-column row
|
||||||
|
- Message textarea (full width, 1000 char limit with counter)
|
||||||
|
- Submit button bottom-right, privacy note bottom-left
|
||||||
|
|
||||||
|
### Feedback Modal
|
||||||
|
|
||||||
|
Triggered by "Send Feedback" in TopNav and homepage footer. Same fields as the tab form. Full-width submit button inside the modal.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Admin View (`/admin/feedback`)
|
||||||
|
|
||||||
|
Table with columns: Date, Category, Message (truncated), Email, Reviewed. Sortable by date. Filterable by category (Bug / Feature / General) and reviewed status. Follows existing admin page patterns.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Feedback Form Fields
|
||||||
|
|
||||||
|
| Field | Required | Validation |
|
||||||
|
|---|---|---|
|
||||||
|
| Category | Yes | One of: Bug report, Feature request, General feedback |
|
||||||
|
| Message | Yes | 1–1000 characters |
|
||||||
|
| Email | No | Valid email format if provided |
|
||||||
|
|
||||||
|
`ip_address` and `user_agent` are captured server-side in the Next.js proxy route — never sent from the client form.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Email notifications when feedback is submitted (separate task)
|
||||||
|
- Sorting roadmap items by vote count (manual `order` field only for now)
|
||||||
|
- Public display of vote counts on the changelog tab
|
||||||
|
- Sanity Studio schema deployment (handled separately via Sanity CLI)
|
||||||
|
- Spring Boot entity/migration code (lives in `ballistic-builder-spring` repo, tracked separately)
|
||||||
Reference in New Issue
Block a user