Template navigation and review/complete cleanup

This commit is contained in:
adilallo
2026-04-20 19:00:30 -06:00
parent 707d08642c
commit 2438c6f707
15 changed files with 836 additions and 355 deletions
@@ -0,0 +1,38 @@
import type footerMessages from "../../../../messages/en/create/footer.json";
import type { CreateFlowStep } from "../types";
type FooterMessages = typeof footerMessages;
/**
* Per-step label override for the default "next-step" primary footer
* button (the catch-all branch in `CreateFlowLayoutClient`'s footer that
* 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 (`finalizeButtonPublishing`
* vs `finalizeCommunityRule`).
*/
const DEFAULT_FOOTER_LABEL_BY_STEP: ReadonlyMap<
CreateFlowStep,
keyof FooterMessages
> = new Map<CreateFlowStep, keyof FooterMessages>([
["confirm-stakeholders", "confirmStakeholders"],
["community-context", "confirmDescription"],
["community-structure", "confirmDetails"],
["community-size", "confirmMembers"],
]);
/**
* 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.
*/
export function getDefaultFooterLabel(
step: CreateFlowStep | null | undefined,
footer: FooterMessages,
): string {
if (step == null) return footer.next;
const key = DEFAULT_FOOTER_LABEL_BY_STEP.get(step);
return key != null ? footer[key] : footer.next;
}
+45
View File
@@ -79,6 +79,32 @@ export function getPreviousStep(
return prev;
}
/**
* Where the create-flow footer Back action should go. Usually the previous
* step in {@link FLOW_STEP_ORDER}; when the user reached `confirm-stakeholders`
* via template **Use without changes**, Back returns to template review instead
* of `conflict-management` (that segment was skipped).
*/
export type CreateFlowBackTarget =
| { kind: "step"; step: CreateFlowStep }
| { kind: "templateReview"; slug: string };
export function resolveCreateFlowBackTarget(
currentStep: CreateFlowStep | null | undefined,
options: CreateFlowNavigationOptions | undefined,
templateReviewBackSlug: string | undefined | null,
): CreateFlowBackTarget | null {
const slug =
typeof templateReviewBackSlug === "string"
? templateReviewBackSlug.trim()
: "";
if (currentStep === "confirm-stakeholders" && slug.length > 0) {
return { kind: "templateReview", slug };
}
const prev = getPreviousStep(currentStep, options);
return prev != null ? { kind: "step", step: prev } : null;
}
/**
* Returns the index of the step (0-based), or -1 if invalid
*/
@@ -118,3 +144,22 @@ export function parseCreateFlowScreenFromPathname(
return isValidStep(segment) ? segment : null;
}
/** Same query as `/templates?fromFlow=1` — template was picked after `/create/review`. */
export const TEMPLATE_REVIEW_FROM_CREATE_FLOW_QUERY = "fromFlow" as const;
export const TEMPLATE_REVIEW_FROM_CREATE_FLOW_VALUE = "1" as const;
/**
* `/create/review-template/{slug}` with optional marker so chrome can send
* footer Back to `/create/review` instead of marketing home.
*/
export function buildTemplateReviewHref(
slug: string,
options?: { fromCreateWizard?: boolean },
): string {
const path = `/create/review-template/${encodeURIComponent(slug)}`;
if (options?.fromCreateWizard) {
return `${path}?${TEMPLATE_REVIEW_FROM_CREATE_FLOW_QUERY}=${TEMPLATE_REVIEW_FROM_CREATE_FLOW_VALUE}`;
}
return path;
}