Template flow cleaned up
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import RuleCard from "../../../../components/cards/RuleCard";
|
||||
import { useTranslation } from "../../../../contexts/MessagesContext";
|
||||
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
|
||||
@@ -11,21 +13,65 @@ import {
|
||||
CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
|
||||
} from "../../components/createFlowLayoutTokens";
|
||||
|
||||
/**
|
||||
* Targets for a `pendingTemplateAction` redirect. Customize resumes the
|
||||
* custom-rule stage with chips already prefilled; useWithoutChanges jumps to
|
||||
* the review-and-complete stage since the template body is already in state.
|
||||
*/
|
||||
const PENDING_TEMPLATE_REDIRECT_TARGET: Record<
|
||||
"customize" | "useWithoutChanges",
|
||||
string
|
||||
> = {
|
||||
customize: "/create/core-values",
|
||||
useWithoutChanges: "/create/confirm-stakeholders",
|
||||
};
|
||||
|
||||
/** Create Community review — Figma `19706:12135` (`/create/review`; two columns from `lg:`; column caps in `createFlowLayoutTokens`). */
|
||||
export function CommunityReviewScreen() {
|
||||
const router = useRouter();
|
||||
const lgUp = useCreateFlowLgUp();
|
||||
const t = useTranslation("create.community.review");
|
||||
const { state } = useCreateFlow();
|
||||
const { state, updateState } = useCreateFlow();
|
||||
|
||||
/**
|
||||
* If the user picked "Customize" or "Use without changes" from a template
|
||||
* before entering community stage, we pinned `pendingTemplateAction` so
|
||||
* this screen can skip itself — they already expressed their intent, no
|
||||
* reason to make them re-pick from the review footer. We `replace` (not
|
||||
* `push`) so Back from the destination goes to `community-save` instead of
|
||||
* bouncing through here again. The action is cleared synchronously via
|
||||
* `updateState` to guarantee the redirect only fires once: later visits to
|
||||
* `/create/review` (e.g. navigating here directly) render normally.
|
||||
*
|
||||
* Ref guard covers React 18 StrictMode's double-mount in dev so we don't
|
||||
* fire `router.replace` twice on the same transition.
|
||||
*/
|
||||
const firedRedirectRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (firedRedirectRef.current) return;
|
||||
const pending = state.pendingTemplateAction;
|
||||
if (!pending) return;
|
||||
const target = PENDING_TEMPLATE_REDIRECT_TARGET[pending.mode];
|
||||
if (!target) return;
|
||||
firedRedirectRef.current = true;
|
||||
updateState({ pendingTemplateAction: undefined });
|
||||
router.replace(target);
|
||||
}, [router, state.pendingTemplateAction, updateState]);
|
||||
|
||||
const cardTitle =
|
||||
typeof state.title === "string" && state.title.trim().length > 0
|
||||
? state.title.trim()
|
||||
: t("ruleCard.title");
|
||||
/**
|
||||
* No placeholder fallback: if the user skipped `community-context`, leave
|
||||
* the card description off rather than render the old "Mutual Aid Monday
|
||||
* is a grassroots community…" sample, which read as real user copy.
|
||||
*/
|
||||
const cardDescription =
|
||||
typeof state.communityContext === "string" &&
|
||||
state.communityContext.trim().length > 0
|
||||
? state.communityContext.trim()
|
||||
: t("ruleCard.description");
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<CreateFlowStepShell
|
||||
|
||||
@@ -10,9 +10,13 @@ import {
|
||||
CREATE_FLOW_REVIEW_RULE_CARD_LAYOUT_CLASS,
|
||||
CreateFlowLockupCardStepShell,
|
||||
} from "../../components/CreateFlowLockupCardStepShell";
|
||||
import {
|
||||
buildFinalReviewCategoriesFromState,
|
||||
type FinalReviewCategoryRow,
|
||||
} from "../../../../../lib/create/buildFinalReviewCategories";
|
||||
|
||||
function buildFinalReviewCategories(
|
||||
rows: { name: string; chips: string[] }[],
|
||||
rows: readonly FinalReviewCategoryRow[],
|
||||
): Category[] {
|
||||
return rows.map((cat) => ({
|
||||
name: cat.name,
|
||||
@@ -24,16 +28,58 @@ function buildFinalReviewCategories(
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* `finalReview.json.categories` ships a demo ordering + localized names
|
||||
* (Values / Communication / Membership / Decision-making / Conflict
|
||||
* management). We reuse that ordering for the state-derived rows so the
|
||||
* RuleCard layout stays stable across customize / use-without-changes /
|
||||
* plain-custom flows, and fall back to the demo chips when state resolves
|
||||
* to nothing selected.
|
||||
*/
|
||||
function readFallbackCategoryNames(
|
||||
categories: readonly { name: string; chips: readonly string[] }[],
|
||||
): {
|
||||
names: {
|
||||
values: string;
|
||||
communication: string;
|
||||
membership: string;
|
||||
decisions: string;
|
||||
conflict: string;
|
||||
};
|
||||
rows: FinalReviewCategoryRow[];
|
||||
} {
|
||||
const get = (i: number): string =>
|
||||
typeof categories[i]?.name === "string" ? categories[i].name : "";
|
||||
return {
|
||||
names: {
|
||||
values: get(0),
|
||||
communication: get(1),
|
||||
membership: get(2),
|
||||
decisions: get(3),
|
||||
conflict: get(4),
|
||||
},
|
||||
rows: categories.map((c) => ({ name: c.name, chips: [...c.chips] })),
|
||||
};
|
||||
}
|
||||
|
||||
export function FinalReviewScreen() {
|
||||
const { state } = useCreateFlow();
|
||||
const mdUp = useCreateFlowMdUp();
|
||||
const t = useTranslation("create.reviewAndComplete.finalReview");
|
||||
const m = useMessages();
|
||||
|
||||
const finalReviewCategories = useMemo(
|
||||
() => buildFinalReviewCategories(m.create.reviewAndComplete.finalReview.categories),
|
||||
[m.create.reviewAndComplete.finalReview.categories],
|
||||
);
|
||||
const finalReviewCategories = useMemo(() => {
|
||||
const { names, rows: fallbackRows } = readFallbackCategoryNames(
|
||||
m.create.reviewAndComplete.finalReview.categories,
|
||||
);
|
||||
const derived = buildFinalReviewCategoriesFromState(state, names);
|
||||
// When a user lands on final review with nothing actually selected (e.g.
|
||||
// direct-nav during dev), keep the shipped demo chips rather than render
|
||||
// an empty card — matches prior behavior for that edge case.
|
||||
return buildFinalReviewCategories(
|
||||
derived.length > 0 ? derived : fallbackRows,
|
||||
);
|
||||
}, [m.create.reviewAndComplete.finalReview.categories, state]);
|
||||
|
||||
const ruleCardTitle = useMemo(() => {
|
||||
const raw = typeof state.title === "string" ? state.title.trim() : "";
|
||||
|
||||
Reference in New Issue
Block a user