From 45bbbb8a355aa63b08475b095c5a6503e4710d45 Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:41:10 -0600 Subject: [PATCH] Implement create custom recommendations --- .cursor/rules/create-flow.mdc | 13 +- .cursor/rules/localization.mdc | 18 +- app/(app)/create/CreateFlowLayoutClient.tsx | 40 +- app/(app)/create/SignedInDraftHydration.tsx | 55 +- .../create/context/CreateFlowContext.tsx | 68 +- .../create/hooks/useFacetRecommendations.ts | 232 ++++ .../create/screens/CreateFlowScreenView.tsx | 6 +- .../card/CommunicationMethodsScreen.tsx | 95 +- .../screens/card/ConflictManagementScreen.tsx | 85 +- .../screens/card/MembershipMethodsScreen.tsx | 92 +- .../screens/completed/CompletedScreen.tsx | 2 +- .../informational/InformationalScreen.tsx | 2 +- .../screens/review/CommunityReviewScreen.tsx | 2 +- .../screens/review/FinalReviewScreen.tsx | 6 +- .../right-rail/DecisionApproachesScreen.tsx | 105 +- .../select/CommunitySizeSelectScreen.tsx | 2 +- .../select/CommunityStructureSelectScreen.tsx | 2 +- .../select/ConfirmStakeholdersScreen.tsx | 2 +- .../screens/select/CoreValuesSelectScreen.tsx | 2 +- .../screens/upload/CommunityUploadScreen.tsx | 2 +- app/(app)/create/types.ts | 8 +- .../create/utils/createFlowScreenRegistry.ts | 36 +- app/api/create-flow/methods/route.ts | 56 + app/api/drafts/me/route.ts | 17 + app/api/templates/route.ts | 26 +- .../utility/CardStack/CardStack.container.tsx | 2 + .../utility/CardStack/CardStack.types.ts | 11 + .../utility/CardStack/CardStack.view.tsx | 21 +- data/create/customRule/_facetGroups.json | 79 ++ data/create/customRule/communication.json | 321 +++++ .../create/customRule/conflictManagement.json | 553 ++++++++ .../create/customRule/decisionApproaches.json | 930 +++++++++++++ data/create/customRule/membership.json | 553 ++++++++ docs/guides/template-recommendation-matrix.md | 1189 +++++++++-------- lib/create/api.ts | 17 + lib/server/methodRecommendations.ts | 171 +++ lib/server/ruleTemplates.ts | 106 +- lib/server/templateMethods.ts | 76 ++ lib/server/validation/methodFacetsSchemas.ts | 273 ++++ messages/en/create/communication.json | 120 -- .../{ => community}/communityContext.json | 0 .../create/{ => community}/communityName.json | 0 .../create/{ => community}/communitySave.json | 0 .../create/{ => community}/communitySize.json | 0 .../{ => community}/communityStructure.json | 0 .../{ => community}/communityUpload.json | 0 .../create/{ => community}/informational.json | 0 .../en/create/{ => community}/review.json | 0 messages/en/create/conflictManagement.json | 143 -- .../en/create/customRule/communication.json | 137 ++ .../create/customRule/conflictManagement.json | 305 +++++ .../create/{ => customRule}/coreValues.json | 0 .../create/customRule/decisionApproaches.json | 535 ++++++++ messages/en/create/customRule/membership.json | 217 +++ messages/en/create/footer.json | 2 +- messages/en/create/membership.json | 133 -- .../{ => reviewAndComplete}/completed.json | 0 .../confirmStakeholders.json | 0 .../{ => reviewAndComplete}/finalReview.json | 0 .../{ => reviewAndComplete}/publish.json | 0 messages/en/create/rightRail.json | 147 -- messages/en/index.ts | 82 +- .../migration.sql | 21 + prisma/schema.prisma | 24 + prisma/seed.ts | 9 + prisma/seed/methodFacets.ts | 117 ++ stories/pages/TextPage.stories.js | 2 +- tests/components/TextPage.test.tsx | 4 +- tests/pages/decision-approaches.test.jsx | 11 +- tests/unit/createFlowMethodsRoute.test.ts | 69 + tests/unit/deriveCompactCards.test.ts | 93 ++ tests/unit/methodFacets.test.ts | 88 ++ tests/unit/methodFacetsSchemas.test.ts | 83 ++ tests/unit/methodRecommendations.test.ts | 160 +++ tests/unit/templateMethods.test.ts | 77 ++ 75 files changed, 6403 insertions(+), 1452 deletions(-) create mode 100644 app/(app)/create/hooks/useFacetRecommendations.ts create mode 100644 app/api/create-flow/methods/route.ts create mode 100644 data/create/customRule/_facetGroups.json create mode 100644 data/create/customRule/communication.json create mode 100644 data/create/customRule/conflictManagement.json create mode 100644 data/create/customRule/decisionApproaches.json create mode 100644 data/create/customRule/membership.json create mode 100644 lib/server/methodRecommendations.ts create mode 100644 lib/server/templateMethods.ts create mode 100644 lib/server/validation/methodFacetsSchemas.ts delete mode 100644 messages/en/create/communication.json rename messages/en/create/{ => community}/communityContext.json (100%) rename messages/en/create/{ => community}/communityName.json (100%) rename messages/en/create/{ => community}/communitySave.json (100%) rename messages/en/create/{ => community}/communitySize.json (100%) rename messages/en/create/{ => community}/communityStructure.json (100%) rename messages/en/create/{ => community}/communityUpload.json (100%) rename messages/en/create/{ => community}/informational.json (100%) rename messages/en/create/{ => community}/review.json (100%) delete mode 100644 messages/en/create/conflictManagement.json create mode 100644 messages/en/create/customRule/communication.json create mode 100644 messages/en/create/customRule/conflictManagement.json rename messages/en/create/{ => customRule}/coreValues.json (100%) create mode 100644 messages/en/create/customRule/decisionApproaches.json create mode 100644 messages/en/create/customRule/membership.json delete mode 100644 messages/en/create/membership.json rename messages/en/create/{ => reviewAndComplete}/completed.json (100%) rename messages/en/create/{ => reviewAndComplete}/confirmStakeholders.json (100%) rename messages/en/create/{ => reviewAndComplete}/finalReview.json (100%) rename messages/en/create/{ => reviewAndComplete}/publish.json (100%) delete mode 100644 messages/en/create/rightRail.json create mode 100644 prisma/migrations/20260418170000_add_method_facet/migration.sql create mode 100644 prisma/seed/methodFacets.ts create mode 100644 tests/unit/createFlowMethodsRoute.test.ts create mode 100644 tests/unit/deriveCompactCards.test.ts create mode 100644 tests/unit/methodFacets.test.ts create mode 100644 tests/unit/methodFacetsSchemas.test.ts create mode 100644 tests/unit/methodRecommendations.test.ts create mode 100644 tests/unit/templateMethods.test.ts diff --git a/.cursor/rules/create-flow.mdc b/.cursor/rules/create-flow.mdc index 9c9a417..fb76949 100644 --- a/.cursor/rules/create-flow.mdc +++ b/.cursor/rules/create-flow.mdc @@ -39,9 +39,16 @@ file are a smell once they're used more than once. ## Copy & data -- Step copy lives in `messages/en/create/.json`, wired into - `messages/en/index.ts` under the `create:` namespace (see - `localization.mdc` for the standard pattern). +- Step copy lives in `messages/en/create//.json` where + `` is one of `community`, `customRule`, `reviewAndComplete` + (matches Figma stages — see `docs/create-flow.md`). Cross-cutting chrome + (`footer.json`, `topNav.json`, `draftHydration.json`, + `templateReview.json`) and shared layout-shell strings (`select.json`, + `text.json`, `upload.json`) live at the `create/` root. Wire each new + JSON into `messages/en/index.ts` under the matching `create..*` + namespace (see `localization.mdc`). +- Modal `sections` defaults are DB-shaped seed placeholders, not UI + constants — expect replacement with live data. - Modal `sections` defaults are DB-shaped seed placeholders, not UI constants — expect replacement with live data. diff --git a/.cursor/rules/localization.mdc b/.cursor/rules/localization.mdc index 467d5bd..b141dd1 100644 --- a/.cursor/rules/localization.mdc +++ b/.cursor/rules/localization.mdc @@ -15,9 +15,13 @@ notation). Never hard-code user-facing strings in components. - `messages/en/.json` for single-file areas (`common.json`, `navigation.json`, `metadata.json`). - `messages/en//.json` for areas with multiple buckets: - `components/*.json`, `pages/*.json`, `create/*.json`. One JSON per - component / page / create-flow step — don't shoehorn unrelated copy into - a shared file. + `components/*.json`, `pages/*.json`. One JSON per component / page — + don't shoehorn unrelated copy into a shared file. +- `messages/en/create//.json` — wizard steps grouped by Figma + stage (`community`, `customRule`, `reviewAndComplete`). Cross-cutting + chrome (footer, top nav, draft hydration, template review) and shared + layout-shell strings (`select.json`, `text.json`, `upload.json`) live at + the `create/` root. - Optional `"_comment"` at the top of a JSON documents the bundle's purpose. ## Registration — required @@ -25,12 +29,14 @@ notation). Never hard-code user-facing strings in components. Every new JSON must be wired into `messages/en/index.ts`: ```typescript -import createConflictManagement from "./create/conflictManagement.json"; +import createConflictManagement from "./create/customRule/conflictManagement.json"; export default { // … create: { - conflictManagement: createConflictManagement, + customRule: { + conflictManagement: createConflictManagement, + }, }, }; ``` @@ -44,7 +50,7 @@ step means consumers can't read your strings and TypeScript won't flag the gap. import { useMessages } from "../contexts/MessagesContext"; const m = useMessages(); -const title = m.create.conflictManagement.page.compactTitle; // fully typed +const title = m.create.customRule.conflictManagement.page.compactTitle; // fully typed ``` Use `useTranslation(namespace)` only when you need dot-path lookup by dynamic diff --git a/app/(app)/create/CreateFlowLayoutClient.tsx b/app/(app)/create/CreateFlowLayoutClient.tsx index 6448898..36d6f24 100644 --- a/app/(app)/create/CreateFlowLayoutClient.tsx +++ b/app/(app)/create/CreateFlowLayoutClient.tsx @@ -28,7 +28,11 @@ import { requestMagicLink, } from "../../../lib/create/api"; import { safeInternalPath } from "../../../lib/safeInternalPath"; -import { setTransferPendingFlag } from "./utils/anonymousDraftStorage"; +import { + clearAnonymousCreateFlowStorage, + setTransferPendingFlag, +} from "./utils/anonymousDraftStorage"; +import { deleteServerDraft } from "../../../lib/create/api"; import { writeLastPublishedRule } from "../../../lib/create/lastPublishedRule"; import { fetchTemplateBySlug, @@ -64,10 +68,14 @@ function CreateFlowSessionShell({ children }: { children: ReactNode }) { }, []); const sessionResolved = sessionUser !== undefined; - const enableAnonymousPersistence = sessionResolved && sessionUser === null; + // Mirror in-progress draft to localStorage for ALL visitors once we know who + // they are. Refresh-survival is the same UX for guest and signed-in users; + // signed-in users additionally get an explicit "Save & Exit" that PUTs to + // the server (handled in `useCreateFlowExit`). + const enableLocalDraftMirroring = sessionResolved; return ( - + setPublishBannerMessage(null)} className="w-full" @@ -622,7 +644,7 @@ function CreateFlowLayoutContent({ goToNextStep(); }} > - {footer.confirmRightRail} + {footer.confirmDecisionApproaches} ) : currentStep === "conflict-management" && nextStep ? (