From 4bc0cfecfc4f234a2b16547e6d8831afd5e8cf03 Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Fri, 29 May 2026 09:57:46 -0500 Subject: [PATCH] Follow-up vocabulary cleanup and dashboard h1 fix. Correct dashboard title (My Collection overview, not Lists), sweep remaining marketing/auth copy, update system-list seed description, add vocabulary unit tests, and close the convoy record. Co-authored-by: Cursor --- .convoys/rename-collections-vocabulary.md | 98 ++++------------------- lib/collection-vocabulary.js | 2 + pages/api/auth/register.js | 7 +- pages/cards.js | 4 +- pages/collections.js | 2 +- pages/dashboard.js | 10 +-- pages/index.js | 12 +-- pages/login.js | 3 +- pages/my-cards.js | 4 +- pages/scanner.js | 4 +- pages/signup.js | 3 +- test/lib/collection-vocabulary.test.js | 29 +++++++ 12 files changed, 71 insertions(+), 107 deletions(-) create mode 100644 test/lib/collection-vocabulary.test.js diff --git a/.convoys/rename-collections-vocabulary.md b/.convoys/rename-collections-vocabulary.md index 36e4f70..1d10139 100644 --- a/.convoys/rename-collections-vocabulary.md +++ b/.convoys/rename-collections-vocabulary.md @@ -8,14 +8,19 @@ success_metric: | with vocabulary table. skip: - arch -status: open +status: closed created: 2026-05-27 +closed: 2026-05-29 +pr: 54 --- # Convoy: rename-collections-vocabulary Align user-facing copy with the product taxonomy: owned cards vs curated lists. +**Shipped:** PR #54 (squash `fd78114`, 2026-05-29). Follow-up cleanup tracked in +PR after deep review (dashboard h1, marketing copy, seed description). + ## Why The scanner audit and IA review found inconsistent vocabulary: "Owned Cards", @@ -48,95 +53,20 @@ schema migration. - **Schema renames** — table/column names stay; UI copy only. - **URL slug changes** — `/collections` path unchanged in v1. - **Architecture decisions** — `skip: arch`; IA + UX run explicitly. - -## Roles invoked - -1. `role-ia-architect` — vocabulary table + file inventory (**primary owner**). -2. `role-ux-reviewer` — scan flow + nav label consistency. -3. `role-implementer` — 2 briefs (serial: Brief 2 after Brief 1). -4. `role-reviewer` + `role-design-system-auditor` + `role-a11y-auditor` — - copy changes affect screen reader strings. - -Note: **`role-architect` skipped** per `skip: arch`. IA architect owns -taxonomy; implementer briefs written by IA + conductor handoff or parent -agent. +- **Admin UI** — separate pass if needed. ## Todos -- [ ] IA: publish vocabulary table + grep inventory of stale strings -- [ ] UX: review scanner + nav + collection views for consistency -- [ ] Brief 1 — pages/ + components/ copy sweep -- [ ] Brief 2 — AGENTS.md + rules + SCHEMA_MAP glossary -- [ ] Add `forbidden-stale-strings` CI job - -## Operator action required - -**None.** - -## Multitask dispatch - -### Slice dependencies - -```yaml -slice_dependencies: - - brief: 1 - depends_on: [] - files: - - pages/**/*.js - - components/*.js - notes: exclude pages/api/** - - brief: 2 - depends_on: [1] - files: - - AGENTS.md - - .cursor/rules/ui-and-theming.mdc - - docs/SCHEMA_MAP.md - - .github/workflows/ci.yml -``` - -Serial: Brief 2 after Brief 1 (docs reference final copy). - -**Cross-convoy:** parallel with #1+#2 after `secure-scanner-gemini-key` -merges — `/multitask role-implementer` **#5 Brief 1 + #6 + #2 Brief 1** -(disjoint files). - -Post-PR audit: - -``` -/multitask role-reviewer + role-design-system-auditor + role-a11y-auditor -``` - -Group id: `audit-rename-collections-vocabulary-`. - -## CI impact - -| Workflow / job | Behavior | -| --- | --- | -| `forbidden-stale-strings` | **New blocking job** — grep `pages/` + `components/`. | -| `visual-diff.yml` | **Likely fires** — widespread UI string changes in pages/components. | -| `preview-smoke.yml` | Fires; sign-in CTA wording must stay smoke-compatible. | - -**Smoke caveat:** smoke test 2 asserts `/sign in/i` on login page — do not -rename that CTA in this convoy. - -## Decisions to ratify (IA architect) - -1. **"Lists" vs "Binders"** — when to use each term in nav vs empty states. -2. **Scanner button label** — replacement for "Mark Owned" (e.g. "Add to - session" vs "Confirm card"). -3. **Admin UI** — out of copy sweep or separate pass? - -## Acceptance criteria - -1. Zero rendered occurrences of the three forbidden strings in `pages/` + - `components/`. -2. Vocabulary table committed in `AGENTS.md`. -3. `forbidden-stale-strings` CI green. -4. Schema DDL unchanged (grep `migrations/` — no new files). -5. Vitest 21/21; smoke 3/3 (sign-in CTA intact). +- [x] IA: publish vocabulary table + grep inventory of stale strings +- [x] UX: review scanner + nav + collection views for consistency +- [x] Brief 1 — pages/ + components/ copy sweep +- [x] Brief 2 — AGENTS.md + rules + SCHEMA_MAP glossary +- [x] Add `forbidden-stale-strings` CI job ## Out of scope follow-ups - **`schema-cleanup-from-scanner-audit`** — `is_system_collection` vs `user_cards` unification (separate convoy). - **`rename-repo-and-vercel-project`** — infra naming, not UI copy. +- **Existing DB seed descriptions** — users registered pre-#54 retain the old + system-list description until a one-off data migration or manual edit. diff --git a/lib/collection-vocabulary.js b/lib/collection-vocabulary.js index 5eebc91..7e0d2a5 100644 --- a/lib/collection-vocabulary.js +++ b/lib/collection-vocabulary.js @@ -16,6 +16,8 @@ export const VOCAB = { ADD_TO_LISTS: 'Add to Lists', SYNCED_BINDER: 'Synced binder', SYSTEM_COLLECTION_SYNC_HINT: 'Automatically syncs with My Collection', + SYSTEM_COLLECTION_SEED_DESCRIPTION: + 'Automatically syncs with My Collection. This list cannot be deleted or made public.', }; /** User-facing label for a collection row (maps system collection DB name). */ diff --git a/pages/api/auth/register.js b/pages/api/auth/register.js index c89f2e8..c382a5d 100644 --- a/pages/api/auth/register.js +++ b/pages/api/auth/register.js @@ -1,6 +1,7 @@ import bcrypt from 'bcryptjs'; import { sql } from '@vercel/postgres'; import { generateUniqueSlug } from '../../../lib/slug-utils.js'; +import { SYSTEM_COLLECTION_DB_NAME, VOCAB } from '../../../lib/collection-vocabulary.js'; import { generateToken } from '../auth-utils.js'; import { checkAuthRateLimit } from '../../../lib/rate-limit.js'; @@ -90,7 +91,7 @@ export default async function handler(req, res) { const existingSlugs = (existingSlugsData.rows || []).map(row => row.slug); // Generate unique slug for "All My Cards" - const uniqueSlug = await generateUniqueSlug("All My Cards", existingSlugs); + const uniqueSlug = await generateUniqueSlug(SYSTEM_COLLECTION_DB_NAME, existingSlugs); // Create the special collection const collectionResult = await sql` @@ -106,8 +107,8 @@ export default async function handler(req, res) { updated_at ) VALUES ( - 'All My Cards', - 'Automatically contains all cards you mark as owned. This collection cannot be deleted or made public.', + ${SYSTEM_COLLECTION_DB_NAME}, + ${VOCAB.SYSTEM_COLLECTION_SEED_DESCRIPTION}, 'All', false, ${user.id}, diff --git a/pages/cards.js b/pages/cards.js index 9726a27..7274dea 100644 --- a/pages/cards.js +++ b/pages/cards.js @@ -26,7 +26,7 @@ function PublicCardsView() {

Sign Up to Unlock Full Features

- Create collections, mark favorites, add cards to your inventory, and more! + Create lists, add cards to {VOCAB.MY_COLLECTION}, mark favorites, and more!

Full Card Browser Coming Soon

- Sign up now to get early access to our complete card database and collection tools. + Sign up now to get early access to our complete card database and list tools.

diff --git a/pages/collections.js b/pages/collections.js index 1b93f50..b0af6b9 100644 --- a/pages/collections.js +++ b/pages/collections.js @@ -634,7 +634,7 @@ export default function Collections() { )} - {/* Create Collection Modal */} + {/* Create List Modal */} {showCreateModal && (
diff --git a/pages/dashboard.js b/pages/dashboard.js index a8cef08..113504d 100644 --- a/pages/dashboard.js +++ b/pages/dashboard.js @@ -4,7 +4,7 @@ import Layout from '../components/Layout'; import PermissionIndicator from '../components/PermissionIndicator'; import { useAuth } from '../lib/use-auth'; import Link from 'next/link'; -import { VOCAB } from '../lib/collection-vocabulary.js'; +import { VOCAB, collectionDisplayName } from '../lib/collection-vocabulary.js'; export default function Dashboard() { const router = useRouter(); @@ -80,10 +80,10 @@ export default function Dashboard() {

- {VOCAB.LISTS} + {VOCAB.MY_COLLECTION}

- Manage your collection of trading cards and decks + Overview of your lists and owned cards

@@ -170,7 +170,7 @@ export default function Dashboard() {

No Lists Yet

- Create your first collection to start organizing your cards + Create your first list to start organizing your cards

- {collection.name} + {collectionDisplayName(collection)}

{collection.cardCount || 0} cards diff --git a/pages/index.js b/pages/index.js index c2f0f9f..cefcb6ef 100644 --- a/pages/index.js +++ b/pages/index.js @@ -96,7 +96,7 @@ export default function Home() { Deck Hearth

- The ultimate hub for trading card collectors. Organize your collection, + The ultimate hub for trading card collectors. Organize your cards into lists, discover rare cards, and connect with fellow enthusiasts in one beautiful platform.

@@ -139,7 +139,7 @@ export default function Home() {

Organize Lists

- Create custom collections, track card values, and organize by sets, rarity, or any system that works for you. + Create custom lists, track card values, and organize by sets, rarity, or any system that works for you.

@@ -150,7 +150,7 @@ export default function Home() {

Discover Cards

- Search through thousands of cards across multiple TCGs. Find that missing piece for your collection. + Search through thousands of cards across multiple TCGs. Find that missing piece for your binder.

@@ -161,7 +161,7 @@ export default function Home() {

Connect & Share

- Share your collections with the community, collaborate with friends, and discover amazing collections from other collectors. + Share your lists with the community, collaborate with friends, and discover amazing lists from other collectors.

@@ -176,7 +176,7 @@ export default function Home() { Featured Lists

- Discover amazing collections from our community + Discover amazing lists from our community

@@ -263,7 +263,7 @@ export default function Home() { Ready to Start Your Journey?

- Join thousands of collectors who trust Deck Hearth to manage their trading card collections. + Join thousands of collectors who trust Deck Hearth to manage their cards and lists.

- Sign in to access your trading card collection + Sign in to access {VOCAB.MY_COLLECTION}

diff --git a/pages/my-cards.js b/pages/my-cards.js index 9bc9b24..5b6cf1b 100644 --- a/pages/my-cards.js +++ b/pages/my-cards.js @@ -265,7 +265,7 @@ export default function MyCards() { {VOCAB.MY_COLLECTION}

- Browse and manage cards in your collection + Browse and manage cards in {VOCAB.MY_COLLECTION}

@@ -420,7 +420,7 @@ export default function MyCards() { {!hasMore && cards.length > 0 && (

- You've reached the end of your collection! + You've reached the end of {VOCAB.MY_COLLECTION}!

)} diff --git a/pages/scanner.js b/pages/scanner.js index 3ba5787..a578cb0 100644 --- a/pages/scanner.js +++ b/pages/scanner.js @@ -383,7 +383,7 @@ export default function Scanner() { }); if (!response.ok) { - throw new Error('Failed to add to collection'); + throw new Error(`Failed to add to ${VOCAB.LIST}`); } }; @@ -758,7 +758,7 @@ export default function Scanner() { {/* Bulk Actions Modal */} {/* This modal is no longer needed as bulk actions are in a floating toolbar */} - {/* Create Collection Modal */} + {/* Create List Modal */} {showCreateCollection && (

- Create your account to start building your collection + Create your account to start building {VOCAB.MY_COLLECTION}

diff --git a/test/lib/collection-vocabulary.test.js b/test/lib/collection-vocabulary.test.js new file mode 100644 index 0000000..3113ca5 --- /dev/null +++ b/test/lib/collection-vocabulary.test.js @@ -0,0 +1,29 @@ +import { describe, expect, it } from 'vitest'; + +import { + SYSTEM_COLLECTION_DB_NAME, + VOCAB, + collectionDisplayName, + formatProcessedDestination, +} from '../../lib/collection-vocabulary.js'; + +describe('collection-vocabulary', () => { + it('maps system collection rows to Synced binder', () => { + expect(collectionDisplayName({ name: SYSTEM_COLLECTION_DB_NAME, isSystemCollection: true })).toBe( + VOCAB.SYNCED_BINDER + ); + expect(collectionDisplayName({ name: SYSTEM_COLLECTION_DB_NAME, is_system_collection: true })).toBe( + VOCAB.SYNCED_BINDER + ); + }); + + it('passes through user list names', () => { + expect(collectionDisplayName({ name: 'Modern Staples' })).toBe('Modern Staples'); + }); + + it('formats scanner processed destinations', () => { + expect(formatProcessedDestination('owned')).toBe(VOCAB.MY_COLLECTION); + expect(formatProcessedDestination('collection')).toBe(VOCAB.LIST); + expect(formatProcessedDestination('deck')).toBe('deck'); + }); +});