Show the whole Rule on one review card, with quiet flags for missing cards and Continue once each question has one.

This commit is contained in:
adilallo committed 2026-11-06 08:36:34 -07:00
1 parent cc878e3c19
commit 6e2e1915ae
30 files changed
+571 -218

No files matched your search

@@ -1,5 +1,10 @@
import type footerMessages from "../../../../messages/en/create/footer.json";
import type { CreateFlowStep } from "../types";
import {
buildFinalReviewCategoryRowsDetailed,
type FinalReviewCategoryNames,
} from "../../../../lib/create/buildFinalReviewCategories";
import type { TemplateFacetGroupKey } from "../../../../lib/create/templateReviewMapping";
import type { CreateFlowState, CreateFlowStep } from "../types";
type FooterMessages = typeof footerMessages;
@@ -9,9 +14,10 @@ type FooterMessages = typeof footerMessages;
* fires `goToNextStep` for steps without a bespoke footer). Steps absent
* from this map fall back to `footer.next`.
*
* `final-review` is handled separately by the caller because its label
* also depends on the in-flight publish flag (`publishButtonPublishing`
* vs `publishCommunityRule`).
* `final-review` uses Continue (gated by {@link isFinalReviewContinueBlocked}).
* `edit-rule` is handled separately by the caller because its label also
* depends on the in-flight publish flag (`publishButtonPublishing` vs
* `publishCommunityRule`).
*/
const DEFAULT_FOOTER_LABEL_BY_STEP: ReadonlyMap<
CreateFlowStep,
@@ -53,10 +59,42 @@ export function isCommunitySizeStepBlocked(
return !hasNonEmptyId(selectedCommunitySizeIds);
}
const FINAL_REVIEW_BLOCK_NAMES: FinalReviewCategoryNames = {
values: "values",
communication: "communication",
membership: "membership",
decisions: "decisions",
conflict: "conflict",
};
const FINAL_REVIEW_REQUIRED_GROUPS: readonly TemplateFacetGroupKey[] = [
"communication",
"membership",
"decisionApproaches",
"conflictManagement",
] as const;
/**
* Continue on final review waits for a community name and at least one card
* on each of the four questions. Skipped values do not block.
*/
export function isFinalReviewContinueBlocked(state: CreateFlowState): boolean {
const title = typeof state.title === "string" ? state.title.trim() : "";
if (title.length === 0) return true;
const rows = buildFinalReviewCategoryRowsDetailed(
state,
FINAL_REVIEW_BLOCK_NAMES,
);
return FINAL_REVIEW_REQUIRED_GROUPS.some((groupKey) => {
const row = rows.find((candidate) => candidate.groupKey === groupKey);
return !row || row.entries.length === 0;
});
}
/**
* Resolve the localized label for the default "next-step" footer button.
* Returns the per-step override when one is registered, otherwise
* `footer.next`. Caller still owns the `final-review` special case.
* `footer.next`. Caller still owns the `final-review` / `edit-rule` special case.
*/
export function getDefaultFooterLabel(
step: CreateFlowStep | null | undefined,
@@ -21,8 +21,8 @@ const PROPORTION_BY_STEP_INDEX: readonly ProportionBarState[] = [
"2-2", // membership-methods (Figma — Compact Card Stack `20858:13947`)
"2-3", // decision-approaches (Figma Flow — Right Rail `20523:23509`)
"3-0", // conflict-management (Figma Flow — Compact Card Stack `20879:15979`; start of Review segment)
"3-2", // confirm-stakeholders (same fill as final-review)
"3-2", // final-review
"3-2", // confirm-stakeholders (same fill as final-review)
"3-2", // completed
] as const;
@@ -123,18 +123,18 @@ export const CREATE_FLOW_SCREEN_REGISTRY: Record<
messageNamespace: "create.customRule.conflictManagement",
centeredBodyBelowMd: false,
},
"final-review": {
layoutKind: "review",
figmaNodeId: "20907-213155",
messageNamespace: "create.reviewAndComplete.finalReview",
centeredBodyBelowMd: false,
},
"confirm-stakeholders": {
layoutKind: "select",
figmaNodeId: "21104-46594",
messageNamespace: "create.reviewAndComplete.confirmStakeholders",
centeredBodyBelowMd: false,
},
"final-review": {
layoutKind: "review",
figmaNodeId: "20907-212767",
messageNamespace: "create.reviewAndComplete.finalReview",
centeredBodyBelowMd: false,
},
"edit-rule": {
layoutKind: "review",
figmaNodeId: "20907-212767",
+5 -5
View File
@@ -27,8 +27,8 @@ export const FLOW_STEP_ORDER: readonly CreateFlowStep[] = [
"membership-methods",
"decision-approaches",
"conflict-management",
"confirm-stakeholders",
"final-review",
"confirm-stakeholders",
"completed",
] as const;
@@ -61,7 +61,7 @@ export type CreateFlowNavigationOptions = {
* Community + custom-rule steps skipped when
* {@link CreateFlowNavigationOptions.useWithoutChangesIdentityOnly} is set.
* Remaining identity steps: `community-name` → `community-context` →
* `community-upload`, then `confirm-stakeholders`.
* `community-upload`, then `final-review`.
*/
const USE_WITHOUT_CHANGES_IDENTITY_SKIP = new Set<CreateFlowStep>([
"informational",
@@ -131,9 +131,9 @@ export function getPreviousStep(
* Template **Use without changes** exceptions:
* - Direct from a template (`useWithoutChangesIdentityOnly`): Back from
* `community-name` returns to template review; Back from
* `confirm-stakeholders` is `community-upload`.
* `final-review` is `community-upload`.
* - In-flow (`?fromFlow=1`, community identity already collected): Back
* from `confirm-stakeholders` returns to template review instead of
* from `final-review` returns to template review instead of
* `conflict-management`.
*/
export type CreateFlowBackTarget =
@@ -157,7 +157,7 @@ export function resolveCreateFlowBackTarget(
return { kind: "templateReview", slug };
}
if (
currentStep === "confirm-stakeholders" &&
currentStep === "final-review" &&
slug.length > 0 &&
!options?.useWithoutChangesIdentityOnly
) {