Create flow centralization and cleanup
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Central `/create/...` path builders (Linear CR-92 §2).
|
||||
* Prefer these over string literals so layout, redirects, hooks, and tests stay aligned.
|
||||
*/
|
||||
|
||||
import type { CreateFlowStep } from "../types";
|
||||
import { CREATE_FLOW_REVIEW_RETURN_QUERY_KEY } from "./flowSteps";
|
||||
|
||||
export const CREATE_ROUTES = {
|
||||
root: "/",
|
||||
createRoot: "/create",
|
||||
/** First step resolves via redirect from `/create`. */
|
||||
createFirstStep: "/create",
|
||||
review: "/create/review",
|
||||
finalReview: "/create/final-review",
|
||||
completed: "/create/completed",
|
||||
editRule: "/create/edit-rule",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Post-login return and session-gate paths on wizard steps.
|
||||
* (Also used when `pathname` is unknown but `syncDraft` must be appended.)
|
||||
*/
|
||||
export const CREATE_FLOW_SYNC_DRAFT_QUERY = "syncDraft" as const;
|
||||
export const CREATE_FLOW_SYNC_DRAFT_VALUE = "1" as const;
|
||||
|
||||
export function createFlowStepPathWithSyncDraft(step: CreateFlowStep): string {
|
||||
return createFlowStepPath(step, {
|
||||
[CREATE_FLOW_SYNC_DRAFT_QUERY]: CREATE_FLOW_SYNC_DRAFT_VALUE,
|
||||
});
|
||||
}
|
||||
|
||||
export type CreateFlowPathQuery = Record<
|
||||
string,
|
||||
string | number | boolean | undefined
|
||||
>;
|
||||
|
||||
/**
|
||||
* Path for a wizard step: `/create/{screenId}` with optional query string.
|
||||
*/
|
||||
export function createFlowStepPath(
|
||||
step: CreateFlowStep,
|
||||
query?: CreateFlowPathQuery,
|
||||
): string {
|
||||
const base = `/create/${step}`;
|
||||
if (query == null || Object.keys(query).length === 0) return base;
|
||||
const sp = new URLSearchParams();
|
||||
for (const [k, v] of Object.entries(query)) {
|
||||
if (v === undefined) continue;
|
||||
sp.set(k, String(v));
|
||||
}
|
||||
const q = sp.toString();
|
||||
return q.length > 0 ? `${base}?${q}` : base;
|
||||
}
|
||||
|
||||
export function createCompletedPath(query?: CreateFlowPathQuery): string {
|
||||
return createFlowStepPath("completed", query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate back from a facet step to final-review / edit-rule, dropping
|
||||
* `reviewReturn` from the current query while preserving other params.
|
||||
*/
|
||||
export function createFlowStepPathAfterStrippingReviewReturn(
|
||||
step: CreateFlowStep,
|
||||
searchParams: URLSearchParams | null | undefined,
|
||||
): string {
|
||||
const params = new URLSearchParams(searchParams?.toString() ?? "");
|
||||
params.delete(CREATE_FLOW_REVIEW_RETURN_QUERY_KEY);
|
||||
const query: CreateFlowPathQuery = {};
|
||||
params.forEach((value, key) => {
|
||||
query[key] = value;
|
||||
});
|
||||
return createFlowStepPath(
|
||||
step,
|
||||
Object.keys(query).length > 0 ? query : undefined,
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { CUSTOM_RULE_FACETS } from "../../../../lib/create/customRuleFacets";
|
||||
import type {
|
||||
CreateFlowMethodCardFacetSection,
|
||||
CreateFlowState,
|
||||
@@ -11,9 +12,9 @@ type FooterMessageKey = keyof typeof footerMessages;
|
||||
* Binding for each Custom Rule stage step whose footer primary button
|
||||
* gates the user on "has at least one chip selected?". All five screens
|
||||
* render the same `<Button …>`; only the disable predicate and the
|
||||
* footer message differ — this table is the single source of truth for
|
||||
* both, so `CreateFlowLayoutClient` can render one JSX block for the
|
||||
* whole group.
|
||||
* footer message differ — rows are derived from {@link CUSTOM_RULE_FACETS}
|
||||
* (Linear CR-92) so `CreateFlowLayoutClient` stays aligned with template
|
||||
* prefill, strip keys, and API section ids.
|
||||
*
|
||||
* `selectionIds` returns the currently-selected ids array from flow
|
||||
* state for that step (empty array when nothing has been selected or
|
||||
@@ -39,33 +40,11 @@ export type CustomRuleConfirmFooterStep = {
|
||||
};
|
||||
|
||||
export const CUSTOM_RULE_CONFIRM_FOOTER_STEPS: readonly CustomRuleConfirmFooterStep[] =
|
||||
[
|
||||
{
|
||||
step: "core-values",
|
||||
footerMessageKey: "confirmCoreValues",
|
||||
selectionIds: (s) => s.selectedCoreValueIds ?? [],
|
||||
},
|
||||
{
|
||||
step: "communication-methods",
|
||||
footerMessageKey: "confirmCommunication",
|
||||
selectionIds: (s) => s.selectedCommunicationMethodIds ?? [],
|
||||
},
|
||||
{
|
||||
step: "membership-methods",
|
||||
footerMessageKey: "confirmMembership",
|
||||
selectionIds: (s) => s.selectedMembershipMethodIds ?? [],
|
||||
},
|
||||
{
|
||||
step: "decision-approaches",
|
||||
footerMessageKey: "confirmDecisionApproaches",
|
||||
selectionIds: (s) => s.selectedDecisionApproachIds ?? [],
|
||||
},
|
||||
{
|
||||
step: "conflict-management",
|
||||
footerMessageKey: "confirmConflictManagement",
|
||||
selectionIds: (s) => s.selectedConflictManagementIds ?? [],
|
||||
},
|
||||
] as const;
|
||||
CUSTOM_RULE_FACETS.filter((r) => r.footerMessageKey != null).map((r) => ({
|
||||
step: r.createFlowStep as CustomRuleConfirmFooterStep["step"],
|
||||
footerMessageKey: r.footerMessageKey as FooterMessageKey,
|
||||
selectionIds: r.selectionIds,
|
||||
}));
|
||||
|
||||
export const CUSTOM_RULE_CONFIRM_FOOTER_STEP_BY_STEP: ReadonlyMap<
|
||||
CreateFlowStep,
|
||||
@@ -79,16 +58,9 @@ export const CUSTOM_RULE_CONFIRM_FOOTER_STEP_BY_STEP: ReadonlyMap<
|
||||
export function methodCardFacetSectionForConfirmStep(
|
||||
step: CustomRuleConfirmFooterStep["step"],
|
||||
): CreateFlowMethodCardFacetSection | undefined {
|
||||
switch (step) {
|
||||
case "communication-methods":
|
||||
return "communication";
|
||||
case "membership-methods":
|
||||
return "membership";
|
||||
case "decision-approaches":
|
||||
return "decisionApproaches";
|
||||
case "conflict-management":
|
||||
return "conflictManagement";
|
||||
default:
|
||||
return undefined;
|
||||
const row = CUSTOM_RULE_FACETS.find((r) => r.createFlowStep === step);
|
||||
if (row == null || row.kind !== "method" || row.apiMethodSectionId == null) {
|
||||
return undefined;
|
||||
}
|
||||
return row.apiMethodSectionId;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import type { TemplateFacetGroupKey } from "../../../../lib/create/templateReviewMapping";
|
||||
import { createFlowStepForCustomRuleFacetGroup } from "../../../../lib/create/customRuleFacets";
|
||||
import type { CreateFlowStep } from "../types";
|
||||
|
||||
const MAP: Record<TemplateFacetGroupKey, CreateFlowStep> = {
|
||||
coreValues: "core-values",
|
||||
communication: "communication-methods",
|
||||
membership: "membership-methods",
|
||||
decisionApproaches: "decision-approaches",
|
||||
conflictManagement: "conflict-management",
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom-rule URL segment for a final-review category row (`+` navigation).
|
||||
* Source: {@link CUSTOM_RULE_FACETS} (CR-92).
|
||||
*/
|
||||
export function createFlowStepForFacetGroup(
|
||||
groupKey: TemplateFacetGroupKey,
|
||||
): CreateFlowStep {
|
||||
return MAP[groupKey];
|
||||
return createFlowStepForCustomRuleFacetGroup(groupKey);
|
||||
}
|
||||
|
||||
@@ -117,6 +117,26 @@ export function getStepIndex(step: CreateFlowStep | null | undefined): number {
|
||||
return FLOW_STEP_ORDER.indexOf(step);
|
||||
}
|
||||
|
||||
/**
|
||||
* Steps where below `lg` the main column scrolls with split layout
|
||||
* (`CreateFlowLayoutClient` — Linear CR-92 §4).
|
||||
*/
|
||||
export const CREATE_FLOW_SELECT_SPLIT_SCROLL_STEPS: readonly CreateFlowStep[] = [
|
||||
"community-size",
|
||||
"community-structure",
|
||||
"core-values",
|
||||
"decision-approaches",
|
||||
] as const;
|
||||
|
||||
export function createFlowStepUsesSelectSplitScroll(
|
||||
step: CreateFlowStep | null | undefined,
|
||||
): boolean {
|
||||
if (!step) return false;
|
||||
return (CREATE_FLOW_SELECT_SPLIT_SCROLL_STEPS as readonly string[]).includes(
|
||||
step,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given string is a valid create flow step
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user