Create flow centralization and cleanup
This commit is contained in:
@@ -1,59 +1,39 @@
|
||||
import type { CreateFlowState } from "../../app/(app)/create/types";
|
||||
import type { FinalReviewChipEditPatch } from "../../app/(app)/create/components/FinalReviewChipEditModal";
|
||||
import { CUSTOM_RULE_FACET_BY_GROUP } from "./customRuleFacets";
|
||||
|
||||
/**
|
||||
* `groupKey` cases mirror {@link CUSTOM_RULE_FACETS} / `TemplateFacetGroupKey`
|
||||
* (Linear CR-92 — keep exhaustiveness when adding a facet row).
|
||||
*
|
||||
* Translate a {@link FinalReviewChipEditPatch} into the `Partial<CreateFlowState>`
|
||||
* patch that {@link CreateFlowState}'s update merger should write back. Each
|
||||
* group key targets its own `*DetailsById` (or `coreValueDetailsByChipId`)
|
||||
* record; the patch always merges the new value onto the existing record so
|
||||
* other chips' overrides are preserved.
|
||||
*
|
||||
* The `switch` is exhaustive because {@link FinalReviewChipEditPatch} is a
|
||||
* discriminated union — adding a new facet group in the modal forces a new
|
||||
* `case` here at compile time, which is the whole reason this lives outside
|
||||
* `FinalReviewScreen` (the screen used to host an identical 5-case switch).
|
||||
*
|
||||
* Exported as a pure function so it's unit-testable without React.
|
||||
*/
|
||||
export function applyFinalReviewChipEditPatch(
|
||||
state: CreateFlowState,
|
||||
patch: FinalReviewChipEditPatch,
|
||||
): Partial<CreateFlowState> {
|
||||
switch (patch.groupKey) {
|
||||
case "coreValues":
|
||||
return {
|
||||
coreValueDetailsByChipId: {
|
||||
...(state.coreValueDetailsByChipId ?? {}),
|
||||
[patch.overrideKey]: patch.value,
|
||||
},
|
||||
};
|
||||
case "communication":
|
||||
return {
|
||||
communicationMethodDetailsById: {
|
||||
...(state.communicationMethodDetailsById ?? {}),
|
||||
[patch.overrideKey]: patch.value,
|
||||
},
|
||||
};
|
||||
case "membership":
|
||||
return {
|
||||
membershipMethodDetailsById: {
|
||||
...(state.membershipMethodDetailsById ?? {}),
|
||||
[patch.overrideKey]: patch.value,
|
||||
},
|
||||
};
|
||||
case "decisionApproaches":
|
||||
return {
|
||||
decisionApproachDetailsById: {
|
||||
...(state.decisionApproachDetailsById ?? {}),
|
||||
[patch.overrideKey]: patch.value,
|
||||
},
|
||||
};
|
||||
case "conflictManagement":
|
||||
return {
|
||||
conflictManagementDetailsById: {
|
||||
...(state.conflictManagementDetailsById ?? {}),
|
||||
[patch.overrideKey]: patch.value,
|
||||
},
|
||||
};
|
||||
const facet = CUSTOM_RULE_FACET_BY_GROUP.get(patch.groupKey);
|
||||
if (!facet) {
|
||||
throw new Error(
|
||||
`applyFinalReviewChipEditPatch: unknown facet group ${patch.groupKey}`,
|
||||
);
|
||||
}
|
||||
const stateKey = facet.detailOverridesStateKey;
|
||||
const current = state[stateKey];
|
||||
const record =
|
||||
current && typeof current === "object"
|
||||
? (current as Record<string, unknown>)
|
||||
: {};
|
||||
return {
|
||||
[stateKey]: {
|
||||
...record,
|
||||
[patch.overrideKey]: patch.value,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
CreateFlowState,
|
||||
} from "../../app/(app)/create/types";
|
||||
import coreValuesMessages from "../../messages/en/create/customRule/coreValues.json";
|
||||
import { assignTemplateMethodSlugsToPrefill } from "./customRuleFacets";
|
||||
import { methodSlugFromTitle } from "./methodSlugFromTitle";
|
||||
|
||||
type TemplateEntry = { title: unknown };
|
||||
@@ -117,28 +118,7 @@ export function buildTemplateCustomizePrefill(
|
||||
const slugs = titles.map(methodSlugFromTitle).filter((s) => s.length > 0);
|
||||
if (slugs.length === 0) continue;
|
||||
|
||||
switch (key) {
|
||||
case "communication":
|
||||
case "communications":
|
||||
prefill.selectedCommunicationMethodIds = slugs;
|
||||
break;
|
||||
case "membership":
|
||||
case "memberships":
|
||||
prefill.selectedMembershipMethodIds = slugs;
|
||||
break;
|
||||
case "decisionmaking":
|
||||
case "decisionapproaches":
|
||||
case "decisions":
|
||||
prefill.selectedDecisionApproachIds = slugs;
|
||||
break;
|
||||
case "conflictmanagement":
|
||||
case "conflict":
|
||||
case "conflictresolution":
|
||||
prefill.selectedConflictManagementIds = slugs;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
assignTemplateMethodSlugsToPrefill(prefill, key, slugs);
|
||||
}
|
||||
|
||||
return prefill;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type { CreateFlowState } from "../../app/(app)/create/types";
|
||||
import communicationMessages from "../../messages/en/create/customRule/communication.json";
|
||||
import conflictManagementMessages from "../../messages/en/create/customRule/conflictManagement.json";
|
||||
import decisionApproachesMessages from "../../messages/en/create/customRule/decisionApproaches.json";
|
||||
import membershipMessages from "../../messages/en/create/customRule/membership.json";
|
||||
import { readMethodPresetsForFacetGroup } from "./customRuleFacets";
|
||||
import {
|
||||
buildCoreValuesForDocument,
|
||||
parseSectionsFromCreateFlowState,
|
||||
@@ -55,21 +52,6 @@ export type FinalReviewCategoryNames = {
|
||||
|
||||
type MethodPreset = { id: string; label: string };
|
||||
|
||||
function readMethodsArray(source: unknown): MethodPreset[] {
|
||||
if (!source || typeof source !== "object") return [];
|
||||
const methods = (source as { methods?: unknown }).methods;
|
||||
if (!Array.isArray(methods)) return [];
|
||||
const out: MethodPreset[] = [];
|
||||
for (const raw of methods) {
|
||||
if (!raw || typeof raw !== "object") continue;
|
||||
const o = raw as Record<string, unknown>;
|
||||
if (typeof o.id === "string" && typeof o.label === "string") {
|
||||
out.push({ id: o.id, label: o.label });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an ordered list of preset ids to `{label, id}` entries, filtering
|
||||
* missing/duplicate labels. The id is returned alongside so callers can key
|
||||
@@ -115,18 +97,8 @@ function overrideKeyForLabel(
|
||||
function methodsForGroup(
|
||||
groupKey: TemplateFacetGroupKey | null,
|
||||
): readonly MethodPreset[] {
|
||||
switch (groupKey) {
|
||||
case "communication":
|
||||
return readMethodsArray(communicationMessages);
|
||||
case "membership":
|
||||
return readMethodsArray(membershipMessages);
|
||||
case "decisionApproaches":
|
||||
return readMethodsArray(decisionApproachesMessages);
|
||||
case "conflictManagement":
|
||||
return readMethodsArray(conflictManagementMessages);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
if (groupKey == null) return [];
|
||||
return readMethodPresetsForFacetGroup(groupKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Single source of truth for custom-rule facet dimensions: URL steps, template
|
||||
* category keys, footer confirm bindings, API method sections, and related
|
||||
* state keys (Linear CR-92 §1 — `CUSTOM_RULE_FACETS`).
|
||||
*
|
||||
* Callers: `applyTemplatePrefill`, `customRuleConfirmFooterSteps`,
|
||||
* `stripCustomRuleSelectionFields`, `buildFinalReviewCategories`,
|
||||
* `facetGroupToCreateFlowStep`, `methodFacetsSchemas` (`SECTION_IDS`),
|
||||
* `publishedDocumentToCreateFlowState` (selection keys), pin lists, etc.
|
||||
*/
|
||||
|
||||
import type { CreateFlowState, CreateFlowStep } from "../../app/(app)/create/types";
|
||||
import type footerMessages from "../../messages/en/create/footer.json";
|
||||
import communicationMessages from "../../messages/en/create/customRule/communication.json";
|
||||
import conflictManagementMessages from "../../messages/en/create/customRule/conflictManagement.json";
|
||||
import decisionApproachesMessages from "../../messages/en/create/customRule/decisionApproaches.json";
|
||||
import membershipMessages from "../../messages/en/create/customRule/membership.json";
|
||||
|
||||
type FooterMessageKey = keyof typeof footerMessages;
|
||||
|
||||
type MethodPreset = { id: string; label: string };
|
||||
|
||||
/**
|
||||
* Known facet groups that template sections map to. Matches the five modals on
|
||||
* the custom-rule create flow (`m.create.customRule.*`).
|
||||
*/
|
||||
export type TemplateFacetGroupKey =
|
||||
| "coreValues"
|
||||
| "communication"
|
||||
| "membership"
|
||||
| "decisionApproaches"
|
||||
| "conflictManagement";
|
||||
|
||||
function readMethodsArray(source: unknown): MethodPreset[] {
|
||||
if (!source || typeof source !== "object") return [];
|
||||
const methods = (source as { methods?: unknown }).methods;
|
||||
if (!Array.isArray(methods)) return [];
|
||||
const out: MethodPreset[] = [];
|
||||
for (const raw of methods) {
|
||||
if (!raw || typeof raw !== "object") continue;
|
||||
const o = raw as Record<string, unknown>;
|
||||
if (typeof o.id === "string" && typeof o.label === "string") {
|
||||
out.push({ id: o.id, label: o.label });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const METHOD_MESSAGES: Record<
|
||||
Exclude<TemplateFacetGroupKey, "coreValues">,
|
||||
unknown
|
||||
> = {
|
||||
communication: communicationMessages,
|
||||
membership: membershipMessages,
|
||||
decisionApproaches: decisionApproachesMessages,
|
||||
conflictManagement: conflictManagementMessages,
|
||||
};
|
||||
|
||||
/** API + recommendation `section` param ids (CR-88); excludes core values. */
|
||||
export const METHOD_FACET_API_SECTION_IDS = [
|
||||
"communication",
|
||||
"membership",
|
||||
"decisionApproaches",
|
||||
"conflictManagement",
|
||||
] as const;
|
||||
|
||||
export type MethodFacetApiSectionId = (typeof METHOD_FACET_API_SECTION_IDS)[number];
|
||||
|
||||
export type CustomRuleFacetKind = "coreValues" | "method";
|
||||
|
||||
export type CustomRuleFacetRow = {
|
||||
readonly facetGroupKey: TemplateFacetGroupKey;
|
||||
readonly kind: CustomRuleFacetKind;
|
||||
readonly createFlowStep: CreateFlowStep;
|
||||
/**
|
||||
* Normalised template `categoryName` keys (see `applyTemplatePrefill` /
|
||||
* `templateCategoryToGroupKey`) — which headers map to this facet.
|
||||
*/
|
||||
readonly templateCategoryNormalizedKeys: readonly string[];
|
||||
/** Footer primary on confirm steps; `null` if this row is not in that table. */
|
||||
readonly footerMessageKey: FooterMessageKey | null;
|
||||
readonly selectionIds: (state: CreateFlowState) => readonly string[];
|
||||
/** Primary selection array on `CreateFlowState` (hydrate + published checks). */
|
||||
readonly selectedIdsStateKey: keyof CreateFlowState;
|
||||
/**
|
||||
* Per-chip edit overrides map (`FinalReviewChipEditPatch` target) keyed by
|
||||
* chip/preset id, e.g. `communicationMethodDetailsById`.
|
||||
*/
|
||||
readonly detailOverridesStateKey: keyof CreateFlowState;
|
||||
/** Keys removed by `stripCustomRuleSelectionFields` for this facet. */
|
||||
readonly stripSelectionKeys: readonly (keyof CreateFlowState)[];
|
||||
/** `GET /api/create-flow/methods?section=` — only for `kind === "method"`. */
|
||||
readonly apiMethodSectionId: MethodFacetApiSectionId | null;
|
||||
};
|
||||
|
||||
const coreValuesRow = {
|
||||
facetGroupKey: "coreValues",
|
||||
kind: "coreValues",
|
||||
createFlowStep: "core-values",
|
||||
templateCategoryNormalizedKeys: ["values", "corevalues"] as const,
|
||||
footerMessageKey: "confirmCoreValues",
|
||||
selectionIds: (s: CreateFlowState) => s.selectedCoreValueIds ?? [],
|
||||
selectedIdsStateKey: "selectedCoreValueIds",
|
||||
detailOverridesStateKey: "coreValueDetailsByChipId",
|
||||
stripSelectionKeys: [
|
||||
"selectedCoreValueIds",
|
||||
"coreValuesChipsSnapshot",
|
||||
"coreValueDetailsByChipId",
|
||||
] as const,
|
||||
apiMethodSectionId: null,
|
||||
} satisfies CustomRuleFacetRow;
|
||||
|
||||
const communicationRow = {
|
||||
facetGroupKey: "communication",
|
||||
kind: "method",
|
||||
createFlowStep: "communication-methods",
|
||||
templateCategoryNormalizedKeys: ["communication", "communications"] as const,
|
||||
footerMessageKey: "confirmCommunication",
|
||||
selectionIds: (s: CreateFlowState) => s.selectedCommunicationMethodIds ?? [],
|
||||
selectedIdsStateKey: "selectedCommunicationMethodIds",
|
||||
detailOverridesStateKey: "communicationMethodDetailsById",
|
||||
stripSelectionKeys: ["selectedCommunicationMethodIds"] as const,
|
||||
apiMethodSectionId: "communication",
|
||||
} satisfies CustomRuleFacetRow;
|
||||
|
||||
const membershipRow = {
|
||||
facetGroupKey: "membership",
|
||||
kind: "method",
|
||||
createFlowStep: "membership-methods",
|
||||
templateCategoryNormalizedKeys: ["membership", "memberships"] as const,
|
||||
footerMessageKey: "confirmMembership",
|
||||
selectionIds: (s: CreateFlowState) => s.selectedMembershipMethodIds ?? [],
|
||||
selectedIdsStateKey: "selectedMembershipMethodIds",
|
||||
detailOverridesStateKey: "membershipMethodDetailsById",
|
||||
stripSelectionKeys: ["selectedMembershipMethodIds"] as const,
|
||||
apiMethodSectionId: "membership",
|
||||
} satisfies CustomRuleFacetRow;
|
||||
|
||||
const decisionRow = {
|
||||
facetGroupKey: "decisionApproaches",
|
||||
kind: "method",
|
||||
createFlowStep: "decision-approaches",
|
||||
templateCategoryNormalizedKeys: [
|
||||
"decisionmaking",
|
||||
"decisionapproaches",
|
||||
"decisions",
|
||||
] as const,
|
||||
footerMessageKey: "confirmDecisionApproaches",
|
||||
selectionIds: (s: CreateFlowState) => s.selectedDecisionApproachIds ?? [],
|
||||
selectedIdsStateKey: "selectedDecisionApproachIds",
|
||||
detailOverridesStateKey: "decisionApproachDetailsById",
|
||||
stripSelectionKeys: ["selectedDecisionApproachIds"] as const,
|
||||
apiMethodSectionId: "decisionApproaches",
|
||||
} satisfies CustomRuleFacetRow;
|
||||
|
||||
const conflictRow = {
|
||||
facetGroupKey: "conflictManagement",
|
||||
kind: "method",
|
||||
createFlowStep: "conflict-management",
|
||||
templateCategoryNormalizedKeys: [
|
||||
"conflictmanagement",
|
||||
"conflict",
|
||||
"conflictresolution",
|
||||
] as const,
|
||||
footerMessageKey: "confirmConflictManagement",
|
||||
selectionIds: (s: CreateFlowState) => s.selectedConflictManagementIds ?? [],
|
||||
selectedIdsStateKey: "selectedConflictManagementIds",
|
||||
detailOverridesStateKey: "conflictManagementDetailsById",
|
||||
stripSelectionKeys: ["selectedConflictManagementIds"] as const,
|
||||
apiMethodSectionId: "conflictManagement",
|
||||
} satisfies CustomRuleFacetRow;
|
||||
|
||||
/**
|
||||
* Ordered facet rows: core values first, then the four method groups (matches
|
||||
* footer confirm order and typical wizard progression).
|
||||
*/
|
||||
export const CUSTOM_RULE_FACETS: readonly CustomRuleFacetRow[] = [
|
||||
coreValuesRow,
|
||||
communicationRow,
|
||||
membershipRow,
|
||||
decisionRow,
|
||||
conflictRow,
|
||||
] as const;
|
||||
|
||||
export const CUSTOM_RULE_FACET_BY_GROUP: ReadonlyMap<
|
||||
TemplateFacetGroupKey,
|
||||
CustomRuleFacetRow
|
||||
> = new Map(CUSTOM_RULE_FACETS.map((r) => [r.facetGroupKey, r]));
|
||||
|
||||
/** Keys cleared by {@link stripCustomRuleSelectionFields} (plus pin map). */
|
||||
export const STRIP_CUSTOM_RULE_SELECTION_STATE_KEYS: readonly (keyof CreateFlowState)[] =
|
||||
[
|
||||
...CUSTOM_RULE_FACETS.flatMap((r) => [...r.stripSelectionKeys]),
|
||||
"methodSectionsPinCommitted",
|
||||
];
|
||||
|
||||
/** `selected*` keys used when merging published rule selections into draft. */
|
||||
export const PUBLISHED_CUSTOM_RULE_SELECTION_KEYS: readonly (keyof CreateFlowState)[] =
|
||||
CUSTOM_RULE_FACETS.map((r) => r.selectedIdsStateKey);
|
||||
|
||||
export function readMethodPresetsForFacetGroup(
|
||||
groupKey: TemplateFacetGroupKey,
|
||||
): readonly MethodPreset[] {
|
||||
if (groupKey === "coreValues") return [];
|
||||
return readMethodsArray(METHOD_MESSAGES[groupKey]);
|
||||
}
|
||||
|
||||
export function assignTemplateMethodSlugsToPrefill(
|
||||
prefill: Partial<CreateFlowState>,
|
||||
normalizedCategoryKey: string,
|
||||
slugs: string[],
|
||||
): boolean {
|
||||
for (const row of CUSTOM_RULE_FACETS) {
|
||||
if (row.kind !== "method") continue;
|
||||
if (!row.templateCategoryNormalizedKeys.includes(normalizedCategoryKey)) {
|
||||
continue;
|
||||
}
|
||||
const k = row.selectedIdsStateKey;
|
||||
(prefill as Record<string, unknown>)[k] = slugs;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function createFlowStepForCustomRuleFacetGroup(
|
||||
groupKey: TemplateFacetGroupKey,
|
||||
): CreateFlowStep {
|
||||
const row = CUSTOM_RULE_FACET_BY_GROUP.get(groupKey);
|
||||
if (!row) {
|
||||
throw new Error(`customRuleFacets: unknown group ${groupKey}`);
|
||||
}
|
||||
return row.createFlowStep;
|
||||
}
|
||||
|
||||
export function templateCategoryToFacetGroupKey(
|
||||
categoryName: string,
|
||||
): TemplateFacetGroupKey | null {
|
||||
const key = categoryName.toLowerCase().replace(/[^a-z]+/g, "");
|
||||
for (const row of CUSTOM_RULE_FACETS) {
|
||||
if (row.templateCategoryNormalizedKeys.includes(key)) {
|
||||
return row.facetGroupKey;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -2,17 +2,13 @@ import type {
|
||||
CreateFlowMethodCardFacetSection,
|
||||
CreateFlowState,
|
||||
} from "../../app/(app)/create/types";
|
||||
import {
|
||||
CUSTOM_RULE_FACETS,
|
||||
PUBLISHED_CUSTOM_RULE_SELECTION_KEYS,
|
||||
} from "./customRuleFacets";
|
||||
import type { PublishedMethodSelections } from "./buildPublishPayload";
|
||||
import type { StoredLastPublishedRule } from "./lastPublishedRule";
|
||||
|
||||
const PUBLISHED_SELECTION_FIELD_KEYS: readonly (keyof CreateFlowState)[] = [
|
||||
"selectedCoreValueIds",
|
||||
"selectedCommunicationMethodIds",
|
||||
"selectedMembershipMethodIds",
|
||||
"selectedDecisionApproachIds",
|
||||
"selectedConflictManagementIds",
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* True when `patch` (from {@link createFlowStateFromPublishedRule}) expects
|
||||
* non-empty facet selections but `state` still has none for that facet.
|
||||
@@ -25,7 +21,7 @@ export function isPublishedRuleSelectionMissing(
|
||||
state: CreateFlowState,
|
||||
patch: Partial<CreateFlowState>,
|
||||
): boolean {
|
||||
for (const k of PUBLISHED_SELECTION_FIELD_KEYS) {
|
||||
for (const k of PUBLISHED_CUSTOM_RULE_SELECTION_KEYS) {
|
||||
const desired = patch[k];
|
||||
if (!Array.isArray(desired) || desired.length === 0) continue;
|
||||
const actualRaw = state[k];
|
||||
@@ -49,17 +45,12 @@ export function methodSectionsPinsForHydratedSelections(
|
||||
patch: Partial<CreateFlowState>,
|
||||
): Partial<Record<CreateFlowMethodCardFacetSection, boolean>> {
|
||||
const out: Partial<Record<CreateFlowMethodCardFacetSection, boolean>> = {};
|
||||
if ((patch.selectedCommunicationMethodIds?.length ?? 0) > 0) {
|
||||
out.communication = true;
|
||||
}
|
||||
if ((patch.selectedMembershipMethodIds?.length ?? 0) > 0) {
|
||||
out.membership = true;
|
||||
}
|
||||
if ((patch.selectedDecisionApproachIds?.length ?? 0) > 0) {
|
||||
out.decisionApproaches = true;
|
||||
}
|
||||
if ((patch.selectedConflictManagementIds?.length ?? 0) > 0) {
|
||||
out.conflictManagement = true;
|
||||
for (const row of CUSTOM_RULE_FACETS) {
|
||||
if (row.kind !== "method" || row.apiMethodSectionId == null) continue;
|
||||
const sel = patch[row.selectedIdsStateKey];
|
||||
if (Array.isArray(sel) && sel.length > 0) {
|
||||
out[row.apiMethodSectionId] = true;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import type { CreateFlowState } from "../../app/(app)/create/types";
|
||||
import { STRIP_CUSTOM_RULE_SELECTION_STATE_KEYS } from "./customRuleFacets";
|
||||
|
||||
/**
|
||||
* Same field removal as {@link resetCustomRuleSelections} in CreateFlowProvider.
|
||||
* Used to apply template "Use without changes" in one atomic replaceState updater.
|
||||
*
|
||||
* Keys come from {@link CUSTOM_RULE_FACETS} / {@link STRIP_CUSTOM_RULE_SELECTION_STATE_KEYS}
|
||||
* (Linear CR-92).
|
||||
*/
|
||||
export function stripCustomRuleSelectionFields(
|
||||
prev: CreateFlowState,
|
||||
): CreateFlowState {
|
||||
const {
|
||||
selectedCoreValueIds: _a,
|
||||
coreValuesChipsSnapshot: _b,
|
||||
coreValueDetailsByChipId: _c,
|
||||
selectedCommunicationMethodIds: _d,
|
||||
selectedMembershipMethodIds: _e,
|
||||
selectedDecisionApproachIds: _f,
|
||||
selectedConflictManagementIds: _g,
|
||||
methodSectionsPinCommitted: _h,
|
||||
...rest
|
||||
} = prev;
|
||||
return rest;
|
||||
const out: CreateFlowState = { ...prev };
|
||||
for (const key of STRIP_CUSTOM_RULE_SELECTION_STATE_KEYS) {
|
||||
delete (out as Record<string, unknown>)[key as string];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import type { Category } from "../../app/components/cards/Rule";
|
||||
import type { ChipOption } from "../../app/components/controls/MultiSelect/MultiSelect.types";
|
||||
import type { CommunityRuleSection } from "../../app/components/type/CommunityRule/CommunityRule.types";
|
||||
import type { TemplateFacetGroupKey } from "./customRuleFacets";
|
||||
import { templateCategoryToFacetGroupKey } from "./customRuleFacets";
|
||||
import { isDocumentEntry } from "./documentEntryGuards";
|
||||
|
||||
export type { TemplateFacetGroupKey } from "./customRuleFacets";
|
||||
|
||||
function isDocumentSection(x: unknown): x is CommunityRuleSection {
|
||||
if (!x || typeof x !== "object") return false;
|
||||
const o = x as Record<string, unknown>;
|
||||
@@ -11,17 +15,6 @@ function isDocumentSection(x: unknown): x is CommunityRuleSection {
|
||||
return o.entries.every(isDocumentEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Known facet groups that template sections map to. Matches the five modals on
|
||||
* the custom-rule create flow (`m.create.customRule.*`).
|
||||
*/
|
||||
export type TemplateFacetGroupKey =
|
||||
| "coreValues"
|
||||
| "communication"
|
||||
| "membership"
|
||||
| "decisionApproaches"
|
||||
| "conflictManagement";
|
||||
|
||||
/**
|
||||
* Normalize a section `categoryName` (as it appears in a template's `body`)
|
||||
* to the custom-rule facet-group key. Returns `null` for unknown categories.
|
||||
@@ -31,28 +24,7 @@ export type TemplateFacetGroupKey =
|
||||
export function templateCategoryToGroupKey(
|
||||
categoryName: string,
|
||||
): TemplateFacetGroupKey | null {
|
||||
const key = categoryName.toLowerCase().replace(/[^a-z]+/g, "");
|
||||
switch (key) {
|
||||
case "values":
|
||||
case "corevalues":
|
||||
return "coreValues";
|
||||
case "communication":
|
||||
case "communications":
|
||||
return "communication";
|
||||
case "membership":
|
||||
case "memberships":
|
||||
return "membership";
|
||||
case "decisionmaking":
|
||||
case "decisionapproaches":
|
||||
case "decisions":
|
||||
return "decisionApproaches";
|
||||
case "conflictmanagement":
|
||||
case "conflict":
|
||||
case "conflictresolution":
|
||||
return "conflictManagement";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return templateCategoryToFacetGroupKey(categoryName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user