Component cleanup

This commit is contained in:
adilallo
2026-04-29 07:20:16 -06:00
parent 252848eba9
commit e6127f1a3f
267 changed files with 2087 additions and 2196 deletions
+3
View File
@@ -8,6 +8,9 @@ User-facing docs. Implementation conventions live in `.cursor/rules/`.
URLs, persistence. Source of truth for product/eng alignment.
- [testing-guide.md](./testing-guide.md) — Testing philosophy, layer
coverage, and Prisma migration smoke before merge.
- [figma-component-registry.md](./figma-component-registry.md) — Figma
Community Rule System file ↔ `app/components/` (and route-colocated
admin UI) map.
## Author guides (`guides/`)
+2 -2
View File
@@ -43,7 +43,7 @@ Order is defined in code by [`FLOW_STEP_ORDER`](../app/(app)/create/utils/flowSt
| 15 | Review and complete | `final-review` | `/create/final-review` |
| 16 | Review and complete | `completed` | `/create/completed` |
**Primary entry:** marketing header “Create rule” navigates to **`/create`**, which redirects to **`/create/informational`** (see [`TopNav.container.tsx`](../app/components/navigation/TopNav/TopNav.container.tsx)).
**Primary entry:** marketing header “Create rule” navigates to **`/create`**, which redirects to **`/create/informational`** (see [`Top.container.tsx`](../app/components/navigation/Top/Top.container.tsx)).
Active step for chrome and navigation is resolved from the pathname via [`parseCreateFlowScreenFromPathname`](../app/(app)/create/utils/flowSteps.ts) inside [`useCreateFlowNavigation`](../app/(app)/create/hooks/useCreateFlowNavigation.ts).
@@ -73,7 +73,7 @@ Only one `?fromFlow=1` marker exists, on one hop (`/create/review` → `/templat
Server drafts (`/api/drafts/me`) are **not** touched here. Per product plan they are not auto-hydrated into the create flow; users select and load a specific saved draft from the profile page. So wiping `localStorage` is sufficient for the "fresh slate" invariant.
**Final-review RuleCard category chips** are derived from `CreateFlowState` via [`buildFinalReviewCategoriesFromState`](../lib/create/buildFinalReviewCategories.ts): for the Customize / plain custom-rule path it resolves `selected{Communication,Membership,DecisionApproach,ConflictManagement}MethodIds` against the curated method presets in `messages/en/create/customRule/*.json`, and `buildCoreValuesForDocument` supplies the `Values` row from `coreValuesChipsSnapshot` + `selectedCoreValueIds`. For the Use-without-changes path the template body lives in `state.sections`; the helper renders `categoryName` + entry titles directly. The demo chips shipped in `finalReview.json` remain the fallback only when nothing in state resolves to any chip (e.g. direct navigation for development).
**Final-review Rule category chips** are derived from `CreateFlowState` via [`buildFinalReviewCategoriesFromState`](../lib/create/buildFinalReviewCategories.ts): for the Customize / plain custom-rule path it resolves `selected{Communication,Membership,DecisionApproach,ConflictManagement}MethodIds` against the curated method presets in `messages/en/create/customRule/*.json`, and `buildCoreValuesForDocument` supplies the `Values` row from `coreValuesChipsSnapshot` + `selectedCoreValueIds`. For the Use-without-changes path the template body lives in `state.sections`; the helper renders `categoryName` + entry titles directly. The demo chips shipped in `finalReview.json` remain the fallback only when nothing in state resolves to any chip (e.g. direct navigation for development).
**Starting the wizard from a template at `final-review` directly** is out of scope until a dedicated product ticket ships. A **full create-from-template** experience will **likely use separate route(s)** when product and eng define it (may still align conceptually with the same three stages where behavior overlaps the custom path).
+181 -11
View File
@@ -4,18 +4,188 @@ Quick map from the Figma file **Community Rule System** (`agv0VBLiBlcnSAaiAORgPR
| Figma (page) | Code | Notes |
| --- | --- | --- |
| [Utility](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=20515-15809) | `utility/` | Create chrome, modals header/footer, tag, scroll, sidebar, dividers, etc. |
| [Asset](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=1240-9089) | `asset/` + `icons/` | Icons, logos; Avatar component currently under `icons/Avatar.tsx` |
| [Button](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=497-3016) | `buttons/` | Figma `Button/`; code uses plural folder name. |
| [Card](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=17865-24349) | `cards/` | Step / rule / icon / selection style cards. |
| [Control](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=5944-58611) | `controls/` | Inputs, toggles, select, switch, upload, etc. |
| [Layout](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21836-20542) | `layout/` | List / list entry / list edit. |
| [Modals](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=5944-47704) | `modals/` | Alert, create, dialog, login, tooltip, context menu, … |
| [Navigation](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=5944-69518) | `navigation/` | Top nav, footer, menu bar, link. |
| [Progress](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21163-24443) | `progress/` | Stepper, proportion bar. |
| [Sections](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=17865-24546) | `sections/` | Page-level / marketing-style compositions. |
| [Type](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21473-29498) | `type/` | Some “section header” / lockup patterns also live in `sections/`; check both. |
| [Utility](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=20515-15809) | `utility/` | Create chrome, tag, scroll, sidebar, dividers, etc. |
| [Asset](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=1240-9089) | **`app/components/asset/`**, **`public/assets/template-mark/`**, **`public/assets/vector/`** | Components under **`asset/`**; flat kebab **`*.svg`** in **`template-mark/`** & **`vector/`** (see conventions below). |
| [Button](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=497-3016) | `buttons/` | PascalCase package per primitive — **`Button/`**, **`InlineTextButton/`** (see conventions below). |
| [Card](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=17865-24349) | `cards/` | One PascalCase package per surface—**`Selection/`** (Figma **Card / CardSelection**), **`CardStack/`**, **`Rule/`** (Figma **Card / Rule**), **`Icon/`**, **`Mini/`**, **`Step/`** (Figma **Card / Step**), **`TemplateReviewCard/`** (see conventions below). |
| [Control](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=5944-58611) | `controls/` | Checkbox, radio, text field, select, toggle, switch, incrementer, upload, multi-select, chip, … (see **Control conventions** below). **`InfoMessageBox`** canonical here. |
| [Layout](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21836-20542) | `layout/` | **`List/`**, **`ListEntry/`** + **`listSizeLayout.ts`**. **Tabs** / **Accordion** are in Figma only—**not** in code yet (see **Layout conventions**). |
| [Modals](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=5944-47704) | `modals/` | Alert, Create, Dialog, Login, Tooltip, **`ModalHeader`** / **`ModalFooter`** (see **Modals conventions**). |
| [Navigation](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=5944-69518) | `navigation/` | **Footer**, **Top**, **`Menu`** + **`MenuItem`**, **Link** matrix — plus create-flow chrome (see **Navigation conventions**, [**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §8). |
| [Progress](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21163-24443) | `progress/` | **`Stepper`**, **`ProportionBar`** — see **Progress conventions**. |
| [Sections](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=17865-24546) | `sections/` | Marketing / page compositions — see **Sections conventions** ([**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §10). |
| [Type](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21473-29498) | `type/` | Section header, lockups, numbered list, input label, published rule tree — see **Type conventions** ([**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §11). |
| — | `content/` | Not a Figma DS page; app content shells / thumbnails. |
| — | `localization/` | Not a Figma DS page; i18n UI. |
| — | `app/(admin)/**/_components/` | Admin-only UI colocated with a route (e.g. **`WebVitalsDashboard`** under **`monitor/_components/`** for **`/monitor`** and **`/api/web-vitals`**). Outside **`app/components/`** buckets; document here when its effectively part of the design-system map. |
## Utility conventions (Figma “Utility” canvas)
- **Figma `.utility/Input`** (field primitive) maps to **`controls/TextInput`** (and related control atoms). No separate `utility/Input` component — **Control** is canonical.
- **`InputLabel`** (standalone label + optional help asterisk/helpers) lives under **`type/InputLabel`** (Figma may still nest it under Utility in the file).
- **Lines / rules:** use **`utility/Divider`** only (`content` \| `menu` × horizontal \| vertical). The old **`Separator`** component was removed; Figma aligns with Divider, not a second primitive.
- **Create-flow chrome (`CreateFlowFooter`, `CreateFlowTopNav`)** lives next to **`Top`** / **`Footer`** under **`navigation/`** — wizard shell / step navigation, grouped with other nav-like components even though Figma files it under Utility.
- **Scrims / number indicators:** promote to shared **`utility`** primitives only when reuse is justified; otherwise keep local to screens.
## Button conventions (Figma “Button” canvas)
- **`buttons/Button/`** — **`Button.tsx`** + **`index.tsx`**. Variants (**filled**, **outline**, **ghost**, **danger**) and **`palette`** / **`size`** align with **`lib/propNormalization`**. **`href`** renders an `<a>` when not disabled.
- **`buttons/InlineTextButton/`** — tertiary inline **`<button>`** (underline, inherited typography) for in-paragraph controls—**not** primary actions.
## Control conventions (Figma [“Control”](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=5944-58611) canvas)
Inventory aligns with [**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §5 (audit only—confirm with product before new primitives).
| Figma (typical control) | Code (`app/components/controls/`) | Notes |
| --- | --- | --- |
| Checkbox + group | **`Checkbox/`**, **`CheckboxGroup/`** | |
| Radio + group | **`RadioButton/`**, **`RadioGroup/`** | |
| Text input (single-line) | **`TextInput/`** | Same primitive as Figma `.utility/Input` matrix (**Utility** calls it out; **Control** is canonical). |
| Select | **`SelectInput/`**, **`SelectOption/`** | `SelectDropdown` lives beside **`SelectInput`** as an implementation detail. |
| Text area | **`TextArea/`** | |
| Toggle (single) | **`Toggle/`** | Not the same as **Toggle Group**. |
| Toggle group | **`ToggleGroup/`** | |
| Switch | **`Switch/`** | |
| Proportion / incrementer | **`Incrementer/`**, **`IncrementerBlock/`** | The **wizard progress bar** is **`progress/ProportionBar`** (Figma **Progress**, not `controls/`). Naming split is intentional until docs/product reconcile—see **CR-104** Naming table + [**CR-60**](https://linear.app/community-rule/issue/CR-60/control-incrementer). |
| Upload | **`Upload/`** | |
| Multi-select | **`MultiSelect/`** | |
| Chip | **`Chip/`** | |
| Field + counter | **`InputWithCounter/`** | Composite. |
| Info / guidance strip | **`InfoMessageBox/`** | May be nested under Utility in the file; **code home** is **`controls/`**. |
**Gaps / TBD:** date picker and other checklist-style items on the Control canvas—**not** assumed shipped; confirm with design.
- **Pattern:** **`container` / `view` / `types`** for heavier controls (**`TextInput`**, **`MultiSelect`**, …); **`index.tsx`** entry per package; enum slices in **`lib/propNormalization`** where listed in **`testing.mdc`**.
## Layout conventions (Figma [“Layout”](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21836-20542) canvas)
Tracks [**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §6: **inventory only**. **Do not** add **`Tabs`**, **`Accordion`**, or other Layout primitives until a shipped surface needs them and design is agreed—**no scaffold components** for Figma-only patterns.
| Figma (typical) | Code (`app/components/layout/`) | Notes |
| --- | --- | --- |
| List / list container | **`List/`** | **`List.container.tsx`**, **`List.view.tsx`**, **`List.types.ts`**. |
| List item / entry row | **`ListEntry/`** | **`ListEntry.container.tsx`**, **`ListEntry.view.tsx`**, **`ListEntry.types.ts`** (re-exports **`LIST_SIZE_OPTIONS`** consumed by **`List`**). |
| Shared list sizing | **`listSizeLayout.ts`** | Layout constants / classes shared by **`List`** and **`ListEntry`**. |
| List edit | — | No **`ListEdit`** package in this repo today; editing flows may be screen-local or future work—confirm in Figma vs product before introducing a shared primitive. |
| Tabs | — | **Not implemented.** |
| Accordion | — | **Not implemented.** |
**Coverage note:** Figmas Base / List matrix may be larger than **`List`** / **`ListEntry`** props—parity is **incremental**, not assumed 1:1.
## Modals conventions (Figma [“Modals”](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=5944-47704) canvas)
Inventory aligns with [**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §7. **No new modal packages** in this pass—only documentation of what already lives in **`app/components/modals/`**.
| Figma (typical) | Code (`app/components/modals/`) | Notes |
| --- | --- | --- |
| Modal / Alert | **`Alert/`** | Toast + banner variants; details in **`alerts.mdc`**. |
| Modal / Create | **`Create/`** | Create-flow modal; **`CreateModalFrame.view.tsx`** + **`useCreateModalA11y.ts`** are **shared** with **`Dialog`**. |
| Modal / Login | **`Login/`** | **`LoginForm.tsx`** is also wired from **`AuthModalContext`** and **`/login`**. |
| Modal / Tooltip | **`Tooltip/`** | |
| Dialog (generic overlay) | **`Dialog/`** | Reuses **`CreateModalFrameView`** and **`useCreateModalA11y`**. |
| Context menu | — | **Not implemented** — removed unused primitives; reintroduce when a shipped surface needs it. |
| Headers / footers (often under Utility in Figma) | **`ModalHeader/`**, **`ModalFooter/`** | Composed chrome; **canonical code** is under **`modals/`**. |
**Gaps / TBD:** **Modal / Share** — product may use share callbacks elsewhere; no dedicated **`Share/`** modal package yet (**CR-104**).
- **Pattern:** **`container` / `view` / `types`** for **`Alert`**, **`Create`**, **`Dialog`**, **`Login`**, **`Tooltip`**, **`ModalHeader`**, **`ModalFooter`**.
## Navigation conventions (Figma [“Navigation”](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=5944-69518) canvas)
Inventory aligns with [**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §8: **audit / documentation only****no new navigation packages** and **no renames** without product/design sign-off.
**Figma (Navigation canvas)** lists **Footer**, "**Top**", **Menu** plus **`.utility/Menu Item`**, and the **Link** property matrix. **Code** (per §8): **`Footer`**, **`Top`** (package **`Top/`**), **`Menu.tsx`** + **`MenuItem/`**, **`NavigationItem`**, **`Link`** — paths below.
| Figma (typical) | Code (`app/components/navigation/`) | Notes |
| --- | --- | --- |
| Navigation / Footer | **`Footer.tsx`** | Marketing site footer; single module at bucket root. |
| Navigation / Top | **`Top/`** | **`Top.container.tsx`**, **`Top.view.tsx`**, **`Top.types.ts`**, **`index.tsx`**. **`TopWithPathname.tsx`** wraps **`Top`**: **`folderTop`** from pathname, session sync (**`initialSignedIn`** from server). |
| Navigation / **Menu**; Figma **`.utility/Menu Item`** | **`Menu.tsx`** + **`MenuItem/`** | **`Menu`** — root **`nav`** (**`role="menubar"`**). **`MenuItem/`** — split package for entries. |
| (same Figma family; composable row) | **`NavigationItem/`** | Split package; rows inside **`Top`** and related shells. Overlap with “menu item” frames is **document-only** — do not assume 1:1 Figma node ↔ file without design. |
| **Link** matrix | **`Link/`** | Next.js **`Link`** wrapper + Figma “Link, CTA” styling; used in nav and content (e.g. **`Rule`**). |
| Create-flow top chrome (often **Utility** in Figma) | **`CreateFlowTopNav/`** | Wizard header; **`CreateFlowLayoutClient`**. |
| Create-flow bottom chrome (often **Utility** in Figma) | **`CreateFlowFooter/`** | Wizard footer + **`ProportionBar`**; **`CreateFlowLayoutClient`**. |
| App shell (not a DS atom) | **`ConditionalNavigation.tsx`**, **`ConditionalNavigationClient.tsx`** | Server: session for first paint. Client: hide global **`Top`** on **`/create/*`** and **`/login`**; else **`TopWithPathname`**. **Tolerated `usePathname()`** — no new pathname-conditional chrome (**`routes.mdc`**). |
**Also under Utility in Figma:** **`CreateFlowTopNav`** / **`CreateFlowFooter`** are filed under Utility but **canonical code** is here with **`Top`** / **`Footer`** (see **Utility conventions**).
**Gaps / TBD (§8):** **`Link`** — Figma carries **many state variants**; the repo may need a **visual parity pass**, not necessarily new files. Confirm with design before treating current props as complete.
**Naming (historical CR-104 note):** Figma **Navigation / Menu** now matches code **`Menu`** + **`MenuItem`** (replacing the older **`MenuBar`** / **`MenuBarItem`** names).
- **Pattern:** **`container` / `view` / `types`** + **`index.tsx`** for **`Top`**, **`CreateFlowTopNav`**, **`CreateFlowFooter`**, **`NavigationItem`**, **`Link`**, **`MenuItem`**. Root **`Menu.tsx`** and **`Footer.tsx`** stay single-file until a split is justified.
## Progress conventions (Figma [“Progress”](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21163-24443) canvas)
Inventory aligns with [**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §9.
| Figma (typical) | Code (`app/components/progress/`) | Notes |
| --- | --- | --- |
| Progress / Stepper | **`Stepper/`** | **`Stepper.container.tsx`**, **`Stepper.view.tsx`**, **`Stepper.types.ts`**, **`index.tsx`**. Dots use **`role="progressbar"`** with **`active`** (1-based) and **`totalSteps`**. |
| Progress / Bar (multi-segment) | **`ProportionBar/`** | Three background segments + fill layer. **`progress`** is **`ProportionBarState`** (`1-0``3-2`; see **`ProportionBar.types.ts`**). Middle-segment ratios for **`2-*`** match Figma (non-uniform; see **`ProportionBar.view.tsx`**). |
| Figma **Control / Proportion** (incrementer) | **`controls/Incrementer/`**, **`IncrementerBlock/`** | **Not** the wizard bar — numeric control. Wizard chrome uses **`ProportionBar`** (see **Control conventions** + [**CR-60**](https://linear.app/community-rule/issue/CR-60/control-incrementer)). |
**Composition:** **`CreateFlowFooter`** (under **`navigation/`**) embeds **`ProportionBar`** in the create-flow shell (Figma often files that chrome under **Utility**).
**Gaps / TBD:** **`ProportionBar` `variant`** (`default` \| `segmented`, from **`lib/propNormalization`**) is **reserved** — both values render the **same** fill today. Extend visually only when design distinguishes them. **`aria-label`** strings in **`ProportionBar.view.tsx`** are **hardcoded English**; move to **`messages/`** if product wants i18n here.
## Sections conventions (Figma [“Sections”](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=17865-24546) canvas)
Inventory aligns with [**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §10: **audit / documentation** — many Figma blocks are **marketing-only**; confirm roadmap before treating gaps as committed build work.
**Cross-bucket:** Figma **Type / SectionHeader** ([17411:10981](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=17411-10981)) is implemented as **`type/SectionHeader/`** and composed by **`sections/CardSteps`**, **`sections/RuleStack`**, etc. The **Community Rule** long-form body ([16489:4507](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=16489-4507)) is filed under **Sections** in Figma but implemented in **`type/CommunityRule/`** — see **Type conventions**.
| Figma / product (typical) | Code (`app/components/sections/`) | Notes |
| --- | --- | --- |
| Hero | **`HeroBanner/`** | **`HeroBanner.tsx`** + **`HeroDecor.tsx`** + **`index.tsx`** (presentational package). |
| Content banner | **`ContentBanner.tsx`** | Single module at bucket root. |
| Logo wall | **`LogoWall/`** | **`container` / `view` / `types`** + **`index.tsx`**. |
| Card steps (SectionCardSteps) | **`CardSteps/`** | Composes **`cards/Step`** (Figma **Card / Step** — not **`progress/Stepper`**). |
| Rule stack | **`RuleStack/`** | |
| Feature grid | **`FeatureGrid/`** | |
| Quote block | **`QuoteBlock/`** | |
| Ask organizer | **`AskOrganizer/`** | |
| Related content | **`RelatedArticles/`** | Article list / cards — confirm naming vs Figma “related slider” frames. |
| Template grid (governance) | **`GovernanceTemplateGrid/`** | **`GovernanceTemplateGridSkeleton`** colocated. |
| Section index / number | **`SectionNumber.tsx`** | Single module. |
**Gaps / TBD (§10, confirm with design / roadmap):** **PageHeader**, **CardGroup**, **Section Accordion**, **Section / Stats** (hero metrics distinct from **`cards/Step`** — [**CR-59**](https://linear.app/community-rule/issue/CR-59/card-stat)), **Related slider** (vs **`RelatedArticles`** parity), **About header**, **triple-step** / text blocks, **orgs** strip, and other Figma-only compositions.
- **Pattern:** Prefer **`container` / `view` / `types`** + **`index.tsx`** for **new** section composites. Older or small surfaces may stay a **single `*.tsx`** at **`sections/`** root (**`ContentBanner`**, **`SectionNumber`**) — match neighbors when extending.
## Type conventions (Figma [“Type”](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21473-29498) canvas)
Inventory aligns with [**CR-104**](https://linear.app/community-rule/issue/CR-104/backlog-design-system-component-cleanup) §11. Figma **Type** covers section headers (1 vs 3 lines, size tiers), **Header / Content lockups**, **Type / Numbered List** (+ list item), long-form text exemplars, and related typography. In code, those surfaces live under **`app/components/type/`** (plus **`InputLabel`**, which Figma sometimes nests under Utility — see **Utility conventions**).
**Cross-bucket:** **`CommunityRule/`** implements the **Sections** canvas frame **Community Rule** ([**16489:4507**](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=16489-4507)) — typography-first long-form body; **canonical code** is under **`type/`** (not **`sections/`**).
| Figma (typical) | Code (`app/components/type/`) | Notes |
| --- | --- | --- |
| Section header (1 vs 3 lines, responsive sizes) | **`SectionHeader/`** | Figma [**17411:10981**](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=17411-10981). **`SectionHeader.tsx`** + **`index.tsx`**; **`default`** / **`multi-line`**; optional **`stackedDesktopLines`**. Composed by **`sections/CardSteps`**, **`sections/RuleStack`**, etc. |
| Header lockup / content lockup | **`HeaderLockup/`**, **`ContentLockup/`** | **`container` / `view` / `types`** + **`index.tsx`** where split. |
| Type / Numbered List (+ item) | **`NumberedList/`** | **`container` / `view` / `types`** + **`index.tsx`**. |
| `.utility/Input label` (often filed under Utility in Figma) | **`InputLabel/`** | See also **Utility conventions****`InputLabel`** is canonical under **`type/`**. |
| “Community Rule” published body (Sections canvas) | **`CommunityRule/`** | Composes **`Section`** + **`TextBlock`**. Category + entries; optional entry **`blocks`**; plain **`body`** splits on blank lines. |
| Community Rule **Section** (rail + category + stack) | **`Section/`** | Figma [**22002:30757**](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=22002-30757). Optional vertical rail (`showRail`). |
| Community Rule **Text Block** (title + paragraphs or labeled rows) | **`TextBlock/`** | Figma Utility [**22001:29793**](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=22001-29793). **`rows`** for label + body stacks (12px / 8px gaps). |
**Gaps / TBD (§11, confirm with design / roadmap):** Additional large **Type**-canvas text compositions or exemplars that do not map cleanly to **`CommunityRule`**, **`TextBlock`**, or the lockup components may stay **Figma-only** until a shipped surface needs them — same rule as other CR-104 inventories (no scaffold purely for parity).
## Card conventions (Figma “Card” canvas)
- **Pattern:** follow the **container / view / types** split (**`Selection/`**, **`CardStack/`**, **`Rule/`**, **`Icon/`**, **`Mini/`**) unless a component stays a single module (**`Step/`** uses one **`Step.tsx`** + **`index.tsx`** only).
- **`Rule/`** — Figma **Card / Rule**. **`Rule.container.tsx`**, **`Rule.view.tsx`**, **`Rule.types.ts`**.
- **`Selection/`** — Figma **Card / CardSelection** (e.g. `16775:28762`): optional recommended/selected **`Tag`**, label, support text. Stacked layout uses `orientation="horizontal"`; row + info icon + tag right uses `orientation="vertical"`.
- **`Step/`** — Figma **Card / Step** (numbered step tile + text). Shipped on the home page via **`sections/CardSteps`**. Not the **Progress / Stepper** wizard control.
- **`CardStack/`** — selectable stacks + expand affordance for create-flow method pickers (**Figma may still say “Utility / CardStack”;** code lives here).
- **`TemplateReviewCard/`** — template review grid + chip detail modal (**`TemplateChipDetailModal`** colocated in the package).
## Asset conventions (Figma “Asset” canvas)
- **Imports:** use **`asset/icon`** (named **`Icon`** component), **`asset/Logo`**, **`asset/Avatar`**, etc.—same rule as **`buttons/Button`**, no top-level **`asset/index.tsx`** barrel.
- **`asset/icon/`** — **`Icon.tsx`** maps icon names to SVG modules beside it (`arrow_back.svg`, …); **`index.tsx`** re-exports **`Icon`** and **`ICON_NAME_OPTIONS`** / types.
- **`public/assets/vector/<slug>.svg`** — Figma Asset / Vector marks (same kebab **`slug`** convention as **`public/assets/template-mark/`**). Use **`vectorMarkPath(slug)`** in **`lib/assetUtils.ts`**.
- **`asset/Logo`** — Community Rule **`Logo`** component (folder PascalCase, like **`Avatar/`**).
- **`asset/Avatar`** + **`asset/AvatarContainer`** — paired circular image stacks (e.g. top nav). Fuller DS Avatar behavior (**initials**, upload routing, …) tracked as **[CR-58](https://linear.app/community-rule/issue/CR-58)**.
*Update this when you add a new top-level `app/components/*` package or a new Figma canvas.*
+10 -10
View File
@@ -138,7 +138,7 @@ Optional: **Docker image deploy** using the repo [Dockerfile](Dockerfile)—admi
3. Surface API errors: invalid email, 429 `retryAfterMs`, expired/invalid token, network failure (accessible copy).
4. Ensure `fetch` calls use `credentials: "include"` where needed (see [lib/create/api.ts](lib/create/api.ts)).
5. **Dev:** without `SMTP_URL`, verify URL is logged; with Mailhog, use [docker-compose.yml](docker-compose.yml) and `SMTP_URL=smtp://localhost:1025`.
6. **Marketing header:** When signed in (`fetchAuthSession`), **Log in** becomes **Profile** linking to [`/profile`](app/(app)/profile/page.tsx) (placeholder until Ticket 15 / CR-86). Implemented in [TopNavWithPathname.tsx](app/components/navigation/TopNav/TopNavWithPathname.tsx) + [TopNav.container.tsx](app/components/navigation/TopNav/TopNav.container.tsx).
6. **Marketing header:** When signed in (`fetchAuthSession`), **Log in** becomes **Profile** linking to [`/profile`](app/(app)/profile/page.tsx) (placeholder until Ticket 15 / CR-86). Implemented in [TopWithPathname.tsx](app/components/navigation/Top/TopWithPathname.tsx) + [Top.container.tsx](app/components/navigation/Top/Top.container.tsx).
**Acceptance criteria:**
@@ -149,7 +149,7 @@ Optional: **Docker image deploy** using the repo [Dockerfile](Dockerfile)—admi
**Status:** [CR-74](https://linear.app/community-rule/issue/CR-74/backend-magic-link-sign-in-ui-apis-ticket-3-cr-75-done) **Done** for shipped UI/APIs. **Residual checklist** below: repo doc items are **done**; use Linear (CR-74 or child issue) to track **per-environment** staging URL checks.
**Files:** [app/(app)/login/](app/(app)/login/), [app/(app)/profile/](app/(app)/profile/) (placeholder), [app/components/modals/Login/](app/components/modals/Login/), [messages/en/pages/login.json](messages/en/pages/login.json), [messages/en/pages/profile.json](messages/en/pages/profile.json), [messages/en/components/header.json](messages/en/components/header.json), [app/components/navigation/TopNav/TopNav.container.tsx](app/components/navigation/TopNav/TopNav.container.tsx), [app/components/navigation/TopNav/TopNavWithPathname.tsx](app/components/navigation/TopNav/TopNavWithPathname.tsx), [lib/create/api.ts](lib/create/api.ts), [app/api/auth/magic-link/request/route.ts](app/api/auth/magic-link/request/route.ts), [app/api/auth/magic-link/verify/route.ts](app/api/auth/magic-link/verify/route.ts), [prisma/schema.prisma](prisma/schema.prisma) (`MagicLinkToken`), [lib/server/mail.ts](lib/server/mail.ts). Onboarding: [CONTRIBUTING.md](CONTRIBUTING.md), [`.env.example`](.env.example).
**Files:** [app/(app)/login/](app/(app)/login/), [app/(app)/profile/](app/(app)/profile/) (placeholder), [app/components/modals/Login/](app/components/modals/Login/), [messages/en/pages/login.json](messages/en/pages/login.json), [messages/en/pages/profile.json](messages/en/pages/profile.json), [messages/en/components/header.json](messages/en/components/header.json), [app/components/navigation/Top/Top.container.tsx](app/components/navigation/Top/Top.container.tsx), [app/components/navigation/Top/TopWithPathname.tsx](app/components/navigation/Top/TopWithPathname.tsx), [lib/create/api.ts](lib/create/api.ts), [app/api/auth/magic-link/request/route.ts](app/api/auth/magic-link/request/route.ts), [app/api/auth/magic-link/verify/route.ts](app/api/auth/magic-link/verify/route.ts), [prisma/schema.prisma](prisma/schema.prisma) (`MagicLinkToken`), [lib/server/mail.ts](lib/server/mail.ts). Onboarding: [CONTRIBUTING.md](CONTRIBUTING.md), [`.env.example`](.env.example).
### Residual / before CR-75 (create-flow session UI)
@@ -173,7 +173,7 @@ Optional: **Docker image deploy** using the repo [Dockerfile](Dockerfile)—admi
**Implementation (repo):**
1. [app/(app)/create/layout.tsx](app/(app)/create/layout.tsx): session + `enableAnonymousPersistence`; anonymous exit → `openLogin({ variant: 'saveProgress', nextPath })`; signed-in exit → `useCreateFlowExit`.
2. [CreateFlowTopNav](app/components/utility/CreateFlowTopNav/): i18n [`messages/en/create/topNav.json`](messages/en/create/topNav.json); logo + Share/Export/Edit (completed) + Exit/Save & Exit only.
2. [CreateFlowTopNav](app/components/navigation/CreateFlowTopNav/): i18n [`messages/en/create/topNav.json`](messages/en/create/topNav.json); logo + Share/Export/Edit (completed) + Exit/Save & Exit only.
3. [useCreateFlowExit](app/(app)/create/hooks/useCreateFlowExit.ts): `saveDraftToServer` when sync + signed in; `clearState` + home.
4. [CreateFlowContext](app/(app)/create/context/CreateFlowContext.tsx): optional anonymous localStorage mirror via `enableAnonymousPersistence`.
5. **QA:** [ProfilePageClient](app/(app)/profile/ProfilePageClient.tsx) Sign out when session present.
@@ -183,7 +183,7 @@ Optional: **Docker image deploy** using the repo [Dockerfile](Dockerfile)—admi
- [x] Completed step still works; **Save & Exit** gating uses session + step (not conflated with `completed` only).
- [x] Signed in + sync: Save & Exit persists server-side; anonymous: localStorage + exit modal + transfer after magic link. Sign out on profile clears session. _(Re-verify on staging/prod as needed.)_
**Files:** [app/(app)/create/layout.tsx](app/(app)/create/layout.tsx), [app/(app)/create/hooks/useCreateFlowExit.ts](app/(app)/create/hooks/useCreateFlowExit.ts), [app/components/utility/CreateFlowTopNav/](app/components/utility/CreateFlowTopNav/), [app/(app)/create/context/CreateFlowContext.tsx](app/(app)/create/context/CreateFlowContext.tsx), [messages/en/create/topNav.json](messages/en/create/topNav.json), [app/(app)/profile/ProfilePageClient.tsx](app/(app)/profile/ProfilePageClient.tsx).
**Files:** [app/(app)/create/layout.tsx](app/(app)/create/layout.tsx), [app/(app)/create/hooks/useCreateFlowExit.ts](app/(app)/create/hooks/useCreateFlowExit.ts), [app/components/navigation/CreateFlowTopNav/](app/components/navigation/CreateFlowTopNav/), [app/(app)/create/context/CreateFlowContext.tsx](app/(app)/create/context/CreateFlowContext.tsx), [messages/en/create/topNav.json](messages/en/create/topNav.json), [app/(app)/profile/ProfilePageClient.tsx](app/(app)/profile/ProfilePageClient.tsx).
---
@@ -332,7 +332,7 @@ Optional: **Docker image deploy** using the repo [Dockerfile](Dockerfile)—admi
1. Keep [`docs/create-flow.md`](create-flow.md) in sync with product/Figma (stage ↔ step mapping, future template routes).
2. ~~Remove legacy [`app/(app)/create/[step]/page.tsx`](app/(app)/create/[step]/page.tsx)~~ — replaced by [`app/(app)/create/[screenId]/page.tsx`](app/(app)/create/[screenId]/page.tsx) with real screens; unknown slugs `notFound()`.
3. ~~Unify **step source of truth** / **resume after `SignedInDraftHydration`~~ — moved to **[CR-86](https://linear.app/community-rule/issue/CR-86/backend-profile-dashboard-account-figma-profile)** (profile lists drafts, continue at last step, new rule vs server draft; see `docs/create-flow.md` known gaps).
4. Wire [`CreateFlowFooter`](app/components/utility/CreateFlowFooter/) `ProportionBar` to step progress from `FLOW_STEP_ORDER` (and `review-template` / `completed` exceptions per design); optional **two-level progress** (stage + step within stage) when design specifies.
4. Wire [`CreateFlowFooter`](app/components/navigation/CreateFlowFooter/) `ProportionBar` to step progress from `FLOW_STEP_ORDER` (and `review-template` / `completed` exceptions per design); optional **two-level progress** (stage + step within stage) when design specifies.
5. When Figma hands off, surface **stage labels** in create shell (top nav, footer, or step chrome) using the mapping in `create-flow.md`.
**Acceptance criteria:**
@@ -342,7 +342,7 @@ Optional: **Docker image deploy** using the repo [Dockerfile](Dockerfile)—admi
- [x] Footer progress reflects step index via `getProportionBarProgressForCreateFlowStep` (optional stage labels still Figma-dependent).
- [x] Template **Customize** prefill is documented (maps template body to `selected*Ids` + `coreValuesChipsSnapshot`, routes to `core-values` when Community has data else `informational`); full template-customize-from-mid-wizard entry beyond `core-values` stays deferred.
**Files:** [`docs/create-flow.md`](create-flow.md), [`app/(app)/create/`](app/(app)/create/), [`app/components/utility/CreateFlowFooter/`](app/components/utility/CreateFlowFooter/), optionally [`docs/backend-roadmap.md`](backend-roadmap.md) §12 cross-links.
**Files:** [`docs/create-flow.md`](create-flow.md), [`app/(app)/create/`](app/(app)/create/), [`app/components/navigation/CreateFlowFooter/`](app/components/navigation/CreateFlowFooter/), optionally [`docs/backend-roadmap.md`](backend-roadmap.md) §12 cross-links.
**Linear:** [CR-89](https://linear.app/community-rule/issue/CR-89/product-canon-custom-create-rule-wizard-routes-resume-progress-repo) **Done**. Open-ended draft/hydration work: **[CR-86](https://linear.app/community-rule/issue/CR-86/backend-profile-dashboard-account-figma-profile)**. **Parallel** to templates (78) and publish (6); not part of **CR-72 → CR-83**.
@@ -411,7 +411,7 @@ Optional: **Docker image deploy** using the repo [Dockerfile](Dockerfile)—admi
- **Communication / Membership / Conflict management / Decision approaches** (card-style screens, e.g. [`CommunicationMethodsScreen.tsx`](../../app/(app)/create/screens/card/CommunicationMethodsScreen.tsx)) — there is **no `Add custom method` affordance**. The inline `add` link in the page description (`messages/en/create/customRule/*.json`, `compactDescriptionLinkLabel: "add"`) only toggles `setExpanded(true)` on the card stack — it shows more preset cards, it does **not** open a creation modal.
- **Confirm stakeholders** — multiselect-style add (free-text chip), pending real invite work in **Ticket 18 / CR-90**.
- **Community structure** — multiselect-style add (free-text chip).
- **Final Review** ([`FinalReviewScreen.tsx`](../../app/(app)/create/screens/review/FinalReviewScreen.tsx)) — renders `<RuleCard categories=…>` and only wires `onChipClick`. `category.onAddClick` is **not provided**, so the `+` button on each MultiSelect category renders by default (`addButton={!hideCategoryAddButton}` in [`RuleCard.view.tsx`](../../app/components/cards/RuleCard/RuleCard.view.tsx)) but **does nothing** when clicked. Dead control we are shipping today.
- **Final Review** ([`FinalReviewScreen.tsx`](../../app/(app)/create/screens/review/FinalReviewScreen.tsx)) — renders `<Rule categories=…>` and only wires `onChipClick`. `category.onAddClick` is **not provided**, so the `+` button on each MultiSelect category renders by default (`addButton={!hideCategoryAddButton}` in [`Rule.view.tsx`](../../app/components/cards/Rule/Rule.view.tsx)) but **does nothing** when clicked. Dead control we are shipping today.
**Open product questions (block implementation until resolved):**
@@ -426,7 +426,7 @@ _Section B — Final Review screen `+` button per category:_
1. Pick one:
- **Option 1 — fresh modal in place.** Clicking `+` opens [`FinalReviewChipEditModal`](../../app/(app)/create/components/FinalReviewChipEditModal.tsx) seeded empty for the category. On Save, append a new chip and write through to the matching `*MethodDetailsById` (or `coreValueDetailsByChipId` + snapshot) map, matching the editable chip flow that just shipped.
- **Option 2 — bounce back to the source step.** Navigate the user to the corresponding custom-rule step (e.g. communication chip `+``/create/communication`) so they add via the existing card-page flow, then return to Final Review.
- **Option 3 — hide the button.** Pass `hideCategoryAddButton` to the Final Review `<RuleCard>` so the dead control disappears until product knows what they want.
- **Option 3 — hide the button.** Pass `hideCategoryAddButton` to the Final Review `<Rule>` so the dead control disappears until product knows what they want.
2. If Option 1, what is the empty state of the modal (title only, all fields, recommended seeds)?
3. If Option 2, how do we preserve the user's place on Final Review so they can come back without re-confirming everything?
4. Does the answer differ for `coreValues` vs the four method categories? (Different edit-fields components, different state slices.)
@@ -436,7 +436,7 @@ _Section B — Final Review screen `+` button per category:_
1. **Product + design pass** answering Sections A and B; produce Figma for whichever options win on each affected page and on Final Review.
2. **Custom-rule card pages (if Option A1 wins):** add an `Add custom <method>` button alongside the card stack on each of the four pages. Reuse the page's `*EditFields` component inside a `customPending`-style modal (same dismiss-drops / confirm-persists semantics as `CoreValuesSelectScreen`). Persist into the matching `*MethodDetailsById` slice and selected-id list.
3. **Final Review (if Option B1 wins):** wire `category.onAddClick` per category in [`FinalReviewScreen.tsx`](../../app/(app)/create/screens/review/FinalReviewScreen.tsx) → open `FinalReviewChipEditModal` seeded empty for the matching `groupKey`, with a `customPending` mode that drops the new entry on dismiss and persists on Save (parity with the current chip-edit flow).
4. **Final Review (if Option B3 wins, interim):** pass `hideCategoryAddButton` to the Final Review `<RuleCard>` so the no-op `+` is removed.
4. **Final Review (if Option B3 wins, interim):** pass `hideCategoryAddButton` to the Final Review `<Rule>` so the no-op `+` is removed.
5. **i18n:** add copy for the new buttons, modal headers, validation, and confirmation/discard prompts under `messages/en/create/customRule/*.json` and `messages/en/create/reviewAndComplete/finalReview.json`.
6. **Tests:** Vitest component tests covering dismiss-drops vs confirm-persists for each new modal (mirror the new `CoreValuesSelectScreen.test.tsx` `custom chip — confirmed vs dismissed` pair).
@@ -453,7 +453,7 @@ _Section B — Final Review screen `+` button per category:_
- [ ] If Option B3 (interim) ships first: the Final Review `+` is no longer rendered (no dead controls).
- [ ] Once Option A1 / B1 are picked: implementation tickets file under this one with `customPending` parity and tests, no regressions to the existing chip-edit flow.
**Files (reference):** [`app/(app)/create/screens/card/CommunicationMethodsScreen.tsx`](../../app/(app)/create/screens/card/CommunicationMethodsScreen.tsx), [`MembershipMethodsScreen.tsx`](../../app/(app)/create/screens/card/MembershipMethodsScreen.tsx), [`ConflictManagementScreen.tsx`](../../app/(app)/create/screens/card/ConflictManagementScreen.tsx), [`right-rail/DecisionApproachesScreen.tsx`](../../app/(app)/create/screens/right-rail/DecisionApproachesScreen.tsx), [`select/CoreValuesSelectScreen.tsx`](../../app/(app)/create/screens/select/CoreValuesSelectScreen.tsx), [`review/FinalReviewScreen.tsx`](../../app/(app)/create/screens/review/FinalReviewScreen.tsx), [`components/FinalReviewChipEditModal.tsx`](../../app/(app)/create/components/FinalReviewChipEditModal.tsx), [`components/methodEditFields/`](../../app/(app)/create/components/methodEditFields/), [`components/cards/RuleCard/RuleCard.view.tsx`](../../app/components/cards/RuleCard/RuleCard.view.tsx).
**Files (reference):** [`app/(app)/create/screens/card/CommunicationMethodsScreen.tsx`](../../app/(app)/create/screens/card/CommunicationMethodsScreen.tsx), [`MembershipMethodsScreen.tsx`](../../app/(app)/create/screens/card/MembershipMethodsScreen.tsx), [`ConflictManagementScreen.tsx`](../../app/(app)/create/screens/card/ConflictManagementScreen.tsx), [`right-rail/DecisionApproachesScreen.tsx`](../../app/(app)/create/screens/right-rail/DecisionApproachesScreen.tsx), [`select/CoreValuesSelectScreen.tsx`](../../app/(app)/create/screens/select/CoreValuesSelectScreen.tsx), [`review/FinalReviewScreen.tsx`](../../app/(app)/create/screens/review/FinalReviewScreen.tsx), [`components/FinalReviewChipEditModal.tsx`](../../app/(app)/create/components/FinalReviewChipEditModal.tsx), [`components/methodEditFields/`](../../app/(app)/create/components/methodEditFields/), [`components/cards/Rule/Rule.view.tsx`](../../app/components/cards/Rule/Rule.view.tsx).
**Linear:** [CR-91](https://linear.app/community-rule/issue/CR-91/productdesign-add-button-behavior-on-custom-rule-pages-and-final) (**Backlog**). Sibling product/design ticket to **CR-90** (same "copy promises a feature, UI doesn't deliver" pattern). **Parallel** to the CR-72 → CR-83 chain; not on the critical path.