Implement core-values screen
This commit is contained in:
@@ -302,6 +302,11 @@ function CreateFlowLayoutContent({
|
||||
const isRightRailStep = currentStep === "right-rail";
|
||||
const isFinalReviewStep = currentStep === "final-review";
|
||||
const isCardsStep = currentStep === "cards";
|
||||
/** Two-column select steps: at `lg+` only the right column scrolls; main must not scroll the full page. */
|
||||
const isSelectSplitScrollStep =
|
||||
currentStep === "community-size" ||
|
||||
currentStep === "community-structure" ||
|
||||
currentStep === "core-values";
|
||||
const stepIdx = currentStep != null ? getStepIndex(currentStep) : -1;
|
||||
|
||||
/** At `md+`, main cross-axis: center by default; exceptions stay top-aligned (see product spec). */
|
||||
@@ -309,9 +314,11 @@ function CreateFlowLayoutContent({
|
||||
? "items-stretch overflow-y-auto md:overflow-hidden"
|
||||
: isRightRailStep
|
||||
? "items-stretch overflow-hidden"
|
||||
: isFinalReviewStep || isCardsStep || isTemplateReviewRoute
|
||||
? "items-start justify-center overflow-y-auto"
|
||||
: "items-start justify-center overflow-y-auto md:items-center";
|
||||
: isSelectSplitScrollStep
|
||||
? "items-start justify-start overflow-y-auto max-lg:overflow-y-auto lg:min-h-0 lg:items-stretch lg:overflow-hidden"
|
||||
: isFinalReviewStep || isCardsStep || isTemplateReviewRoute
|
||||
? "items-start justify-center overflow-y-auto"
|
||||
: "items-start justify-center overflow-y-auto md:items-center";
|
||||
|
||||
const isTextStep = createFlowStepUsesCenteredTextLayout(currentStep);
|
||||
const mainMaxMdJustify =
|
||||
@@ -479,32 +486,18 @@ function CreateFlowLayoutContent({
|
||||
</Button>
|
||||
</div>
|
||||
) : currentStep === "community-name" && nextStep ? (
|
||||
<div className="flex flex-shrink-0 items-center gap-3 md:gap-4">
|
||||
<Button
|
||||
buttonType="outline"
|
||||
palette="inverse"
|
||||
size="xsmall"
|
||||
disabled={isPublishing}
|
||||
className={footerPrimaryButtonClass}
|
||||
onClick={() => {
|
||||
goToNextStep();
|
||||
}}
|
||||
>
|
||||
{footer.next}
|
||||
</Button>
|
||||
<Button
|
||||
buttonType="filled"
|
||||
palette="default"
|
||||
size="xsmall"
|
||||
disabled={isPublishing}
|
||||
className={footerPrimaryButtonClass}
|
||||
onClick={() => {
|
||||
goToNextStep();
|
||||
}}
|
||||
>
|
||||
{footer.confirmName}
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
buttonType="filled"
|
||||
palette="default"
|
||||
size="xsmall"
|
||||
disabled={isPublishing}
|
||||
className={footerPrimaryButtonClass}
|
||||
onClick={() => {
|
||||
goToNextStep();
|
||||
}}
|
||||
>
|
||||
{footer.confirmName}
|
||||
</Button>
|
||||
) : currentStep === "community-save" && nextStep ? (
|
||||
<div className="flex flex-shrink-0 items-center gap-3 md:gap-4">
|
||||
<Button
|
||||
@@ -566,6 +559,22 @@ function CreateFlowLayoutContent({
|
||||
{footer.createFromTemplate}
|
||||
</Button>
|
||||
</div>
|
||||
) : currentStep === "core-values" && nextStep ? (
|
||||
<Button
|
||||
buttonType="filled"
|
||||
palette="default"
|
||||
size="xsmall"
|
||||
disabled={
|
||||
isPublishing ||
|
||||
(state.selectedCoreValueIds?.length ?? 0) === 0
|
||||
}
|
||||
className={footerPrimaryButtonClass}
|
||||
onClick={() => {
|
||||
goToNextStep();
|
||||
}}
|
||||
>
|
||||
{footer.confirmCoreValues}
|
||||
</Button>
|
||||
) : nextStep ? (
|
||||
<Button
|
||||
buttonType="filled"
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { CreateFlowStepShell } from "./CreateFlowStepShell";
|
||||
import { CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS } from "./createFlowLayoutTokens";
|
||||
|
||||
export type CreateFlowSelectShellLgVerticalAlign = "center" | "start";
|
||||
|
||||
interface CreateFlowTwoColumnSelectShellProps {
|
||||
header: ReactNode;
|
||||
children: ReactNode;
|
||||
/**
|
||||
* At `lg+`, layout variant: `"center"` = vertically centered pair (community size/structure).
|
||||
* `"start"` = top-weighted layout with a scrollable right column (core values): uses `items-stretch`
|
||||
* so the right column gets a bounded height; `items-start` would grow with content and break scroll.
|
||||
*/
|
||||
lgVerticalAlign?: CreateFlowSelectShellLgVerticalAlign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Two-column layout for create-flow select steps (community size/structure, core values).
|
||||
* Below `lg`, layout and scrolling match the previous single-column behavior (full page scroll).
|
||||
* At `lg+`, mirrors {@link CompletedScreen}: static header column + scrollable controls column
|
||||
* (`min-h-0` + `overflow-y-auto` height chain; see completed page right rail).
|
||||
*/
|
||||
export function CreateFlowTwoColumnSelectShell({
|
||||
header,
|
||||
children,
|
||||
lgVerticalAlign = "center",
|
||||
}: CreateFlowTwoColumnSelectShellProps) {
|
||||
/** `stretch` is required for `min-h-0` + `overflow-y-auto` on the right column. */
|
||||
const rowLgCrossAlignClass =
|
||||
lgVerticalAlign === "start" ? "lg:items-stretch" : "lg:items-center";
|
||||
|
||||
const leftLgMainJustifyClass =
|
||||
lgVerticalAlign === "start" ? "lg:justify-start" : "lg:justify-center";
|
||||
|
||||
return (
|
||||
<CreateFlowStepShell
|
||||
variant="centeredNarrow"
|
||||
contentTopBelowMd="space-1400"
|
||||
className={
|
||||
/* Below `lg`: natural height — same as legacy select screens (main scrolls). */
|
||||
/* At `lg+`: fill main + clip so only the right column scrolls (CompletedScreen pattern). */
|
||||
"w-full min-w-0 max-lg:flex-none lg:min-h-0 lg:h-full lg:max-h-full lg:flex-1 lg:overflow-hidden lg:items-stretch lg:self-stretch"
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
"flex w-full min-w-0 flex-col items-start gap-[var(--measures-spacing-400,16px)] md:max-w-[640px] " +
|
||||
"max-lg:flex-none lg:max-h-full lg:max-w-[1328px] lg:min-h-0 lg:flex-1 lg:flex-row lg:flex-nowrap " +
|
||||
`${rowLgCrossAlignClass} lg:justify-center lg:gap-[var(--measures-spacing-1200,48px)] lg:overflow-hidden`
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
`flex w-full min-w-0 shrink-0 flex-col items-start gap-[var(--measures-spacing-200,8px)] ` +
|
||||
`lg:flex-1 ${leftLgMainJustifyClass} lg:py-[12px] lg:max-w-[640px] ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`
|
||||
}
|
||||
>
|
||||
{header}
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
`scrollbar-hide relative flex w-full min-w-0 flex-col items-start gap-[var(--measures-spacing-800,32px)] ` +
|
||||
`overflow-x-hidden lg:min-h-0 lg:flex-1 lg:overflow-y-auto lg:pb-[var(--measures-spacing-300,12px)] ` +
|
||||
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</CreateFlowStepShell>
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { InformationalScreen } from "./informational/InformationalScreen";
|
||||
import { CreateFlowTextFieldScreen } from "./text/CreateFlowTextFieldScreen";
|
||||
import { CommunitySizeSelectScreen } from "./select/CommunitySizeSelectScreen";
|
||||
import { CommunityStructureSelectScreen } from "./select/CommunityStructureSelectScreen";
|
||||
import { CoreValuesSelectScreen } from "./select/CoreValuesSelectScreen";
|
||||
import { ConfirmStakeholdersScreen } from "./select/ConfirmStakeholdersScreen";
|
||||
import { CommunityUploadScreen } from "./upload/CommunityUploadScreen";
|
||||
import { CommunityReviewScreen } from "./review/CommunityReviewScreen";
|
||||
@@ -62,6 +63,8 @@ export function CreateFlowScreenView({
|
||||
);
|
||||
case "review":
|
||||
return <CommunityReviewScreen />;
|
||||
case "core-values":
|
||||
return <CoreValuesSelectScreen />;
|
||||
case "cards":
|
||||
return <CardsScreen />;
|
||||
case "right-rail":
|
||||
|
||||
@@ -6,8 +6,7 @@ import type { ChipOption } from "../../../components/controls/MultiSelect/MultiS
|
||||
import { useMessages } from "../../../contexts/MessagesContext";
|
||||
import { useCreateFlow } from "../../context/CreateFlowContext";
|
||||
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
|
||||
import { CreateFlowStepShell } from "../../components/CreateFlowStepShell";
|
||||
import { CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS } from "../../components/createFlowLayoutTokens";
|
||||
import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoColumnSelectShell";
|
||||
|
||||
function chipRowsFromLabels(
|
||||
rows: readonly { label: string }[],
|
||||
@@ -92,26 +91,16 @@ export function CommunitySizeSelectScreen() {
|
||||
);
|
||||
|
||||
return (
|
||||
<CreateFlowStepShell
|
||||
variant="centeredNarrow"
|
||||
contentTopBelowMd="space-1400"
|
||||
<CreateFlowTwoColumnSelectShell
|
||||
header={
|
||||
<CreateFlowHeaderLockup
|
||||
title={cs.header.title}
|
||||
description={cs.header.description}
|
||||
justification="left"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className="flex w-full min-w-0 flex-col items-start gap-[var(--measures-spacing-400,16px)] md:max-w-[640px] lg:max-w-[1328px] lg:flex-row lg:flex-nowrap lg:items-center lg:justify-center lg:gap-[var(--measures-spacing-1200,48px)]">
|
||||
<div
|
||||
className={`flex flex-col items-start gap-[var(--measures-spacing-200,8px)] lg:flex-1 lg:justify-center lg:py-[12px] ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`}
|
||||
>
|
||||
<CreateFlowHeaderLockup
|
||||
title={cs.header.title}
|
||||
description={cs.header.description}
|
||||
justification="left"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`flex flex-col items-start gap-[var(--measures-spacing-800,32px)] lg:flex-1 ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`}
|
||||
>
|
||||
{multiSelectBlock}
|
||||
</div>
|
||||
</div>
|
||||
</CreateFlowStepShell>
|
||||
{multiSelectBlock}
|
||||
</CreateFlowTwoColumnSelectShell>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ import { useMessages } from "../../../contexts/MessagesContext";
|
||||
import { useCreateFlow } from "../../context/CreateFlowContext";
|
||||
import type { CommunityStructureChipSnapshotRow } from "../../types";
|
||||
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
|
||||
import { CreateFlowStepShell } from "../../components/CreateFlowStepShell";
|
||||
import { CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS } from "../../components/createFlowLayoutTokens";
|
||||
import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoColumnSelectShell";
|
||||
|
||||
function createListCustomHandlers(
|
||||
setList: Dispatch<SetStateAction<ChipOption[]>>,
|
||||
@@ -336,26 +335,16 @@ export function CommunityStructureSelectScreen() {
|
||||
);
|
||||
|
||||
return (
|
||||
<CreateFlowStepShell
|
||||
variant="centeredNarrow"
|
||||
contentTopBelowMd="space-1400"
|
||||
<CreateFlowTwoColumnSelectShell
|
||||
header={
|
||||
<CreateFlowHeaderLockup
|
||||
title={cs.header.title}
|
||||
description={cs.header.description}
|
||||
justification="left"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className="flex w-full min-w-0 flex-col items-start gap-[var(--measures-spacing-400,16px)] md:max-w-[640px] lg:max-w-[1328px] lg:flex-row lg:flex-nowrap lg:items-center lg:justify-center lg:gap-[var(--measures-spacing-1200,48px)]">
|
||||
<div
|
||||
className={`flex flex-col items-start gap-[var(--measures-spacing-200,8px)] lg:flex-1 lg:justify-center lg:py-[12px] ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`}
|
||||
>
|
||||
<CreateFlowHeaderLockup
|
||||
title={cs.header.title}
|
||||
description={cs.header.description}
|
||||
justification="left"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`flex flex-col items-start gap-[var(--measures-spacing-800,32px)] lg:flex-1 ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`}
|
||||
>
|
||||
{multiSelectBlock}
|
||||
</div>
|
||||
</div>
|
||||
</CreateFlowStepShell>
|
||||
{multiSelectBlock}
|
||||
</CreateFlowTwoColumnSelectShell>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import MultiSelect from "../../../components/controls/MultiSelect";
|
||||
import type { ChipOption } from "../../../components/controls/MultiSelect/MultiSelect.types";
|
||||
import { useMessages } from "../../../contexts/MessagesContext";
|
||||
import { useCreateFlow } from "../../context/CreateFlowContext";
|
||||
import type { CommunityStructureChipSnapshotRow } from "../../types";
|
||||
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
|
||||
import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoColumnSelectShell";
|
||||
|
||||
const MAX_CORE_VALUES = 5;
|
||||
|
||||
function chipRowsFromLabels(rows: readonly string[]): ChipOption[] {
|
||||
return rows.map((label, i) => ({
|
||||
id: String(i + 1),
|
||||
label,
|
||||
state: "Unselected" as const,
|
||||
}));
|
||||
}
|
||||
|
||||
function applySavedSelection(
|
||||
options: ChipOption[],
|
||||
saved: string[] | undefined,
|
||||
): ChipOption[] {
|
||||
const selected = new Set(saved ?? []);
|
||||
return options.map((opt) =>
|
||||
opt.state === "Custom"
|
||||
? opt
|
||||
: {
|
||||
...opt,
|
||||
state: selected.has(opt.id)
|
||||
? ("Selected" as const)
|
||||
: ("Unselected" as const),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function selectedIdsFromOptions(options: ChipOption[]): string[] {
|
||||
return options
|
||||
.filter((o) => o.state === "Selected")
|
||||
.map((o) => o.id);
|
||||
}
|
||||
|
||||
function chipOptionsToSnapshotRows(
|
||||
options: ChipOption[],
|
||||
): CommunityStructureChipSnapshotRow[] {
|
||||
return options.map((o) => ({
|
||||
id: o.id,
|
||||
label: o.label,
|
||||
...(o.state !== undefined ? { state: o.state } : {}),
|
||||
}));
|
||||
}
|
||||
|
||||
function snapshotRowsToChipOptions(
|
||||
rows: CommunityStructureChipSnapshotRow[] | undefined,
|
||||
): ChipOption[] | null {
|
||||
if (!Array.isArray(rows) || rows.length === 0) return null;
|
||||
return rows.map((r) => ({
|
||||
id: r.id,
|
||||
label: r.label,
|
||||
...(r.state !== undefined
|
||||
? { state: r.state as ChipOption["state"] }
|
||||
: {}),
|
||||
}));
|
||||
}
|
||||
|
||||
/** Create Custom — Core Values (Figma `20264:68378`). Up to five selections; preset list + custom chips. */
|
||||
export function CoreValuesSelectScreen() {
|
||||
const m = useMessages();
|
||||
const cv = m.create.coreValues;
|
||||
const presetLabels = cv.values;
|
||||
const { markCreateFlowInteraction, updateState, state } = useCreateFlow();
|
||||
|
||||
const [coreValueOptions, setCoreValueOptions] = useState<ChipOption[]>(
|
||||
() => {
|
||||
const fromSnap = snapshotRowsToChipOptions(state.coreValuesChipsSnapshot);
|
||||
if (fromSnap) return fromSnap;
|
||||
return applySavedSelection(
|
||||
chipRowsFromLabels(presetLabels),
|
||||
state.selectedCoreValueIds,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const fromSnap = snapshotRowsToChipOptions(state.coreValuesChipsSnapshot);
|
||||
if (fromSnap) {
|
||||
setCoreValueOptions(fromSnap);
|
||||
return;
|
||||
}
|
||||
setCoreValueOptions((prev) =>
|
||||
applySavedSelection(prev, state.selectedCoreValueIds),
|
||||
);
|
||||
}, [state.coreValuesChipsSnapshot, state.selectedCoreValueIds]);
|
||||
|
||||
const persistCoreValues = useCallback(
|
||||
(next: ChipOption[]) => {
|
||||
markCreateFlowInteraction();
|
||||
setCoreValueOptions(next);
|
||||
updateState({
|
||||
selectedCoreValueIds: selectedIdsFromOptions(next),
|
||||
coreValuesChipsSnapshot: chipOptionsToSnapshotRows(next),
|
||||
});
|
||||
},
|
||||
[markCreateFlowInteraction, updateState],
|
||||
);
|
||||
|
||||
const handleChipClick = (chipId: string) => {
|
||||
const target = coreValueOptions.find((o) => o.id === chipId);
|
||||
if (!target || target.state === "Custom") return;
|
||||
|
||||
const willSelect = target.state !== "Selected";
|
||||
const selectedCount = coreValueOptions.filter(
|
||||
(o) => o.state === "Selected",
|
||||
).length;
|
||||
if (willSelect && selectedCount >= MAX_CORE_VALUES) return;
|
||||
|
||||
const next: ChipOption[] = coreValueOptions.map((opt) =>
|
||||
opt.id === chipId
|
||||
? {
|
||||
...opt,
|
||||
state:
|
||||
opt.state === "Selected"
|
||||
? ("Unselected" as const)
|
||||
: ("Selected" as const),
|
||||
}
|
||||
: opt,
|
||||
);
|
||||
persistCoreValues(next);
|
||||
};
|
||||
|
||||
const addHandlers = {
|
||||
onAddClick: () => {
|
||||
markCreateFlowInteraction();
|
||||
setCoreValueOptions((prev) => {
|
||||
const next: ChipOption[] = [
|
||||
...prev,
|
||||
{ id: crypto.randomUUID(), label: "", state: "Custom" },
|
||||
];
|
||||
updateState({
|
||||
selectedCoreValueIds: selectedIdsFromOptions(next),
|
||||
coreValuesChipsSnapshot: chipOptionsToSnapshotRows(next),
|
||||
});
|
||||
return next;
|
||||
});
|
||||
},
|
||||
onCustomChipConfirm: (chipId: string, value: string) => {
|
||||
markCreateFlowInteraction();
|
||||
setCoreValueOptions((prev) => {
|
||||
const next = prev.map((opt) =>
|
||||
opt.id === chipId
|
||||
? { ...opt, label: value, state: "Unselected" as const }
|
||||
: opt,
|
||||
);
|
||||
updateState({
|
||||
selectedCoreValueIds: selectedIdsFromOptions(next),
|
||||
coreValuesChipsSnapshot: chipOptionsToSnapshotRows(next),
|
||||
});
|
||||
return next;
|
||||
});
|
||||
},
|
||||
onCustomChipClose: (chipId: string) => {
|
||||
markCreateFlowInteraction();
|
||||
setCoreValueOptions((prev) => {
|
||||
const next = prev.filter((o) => o.id !== chipId);
|
||||
updateState({
|
||||
selectedCoreValueIds: selectedIdsFromOptions(next),
|
||||
coreValuesChipsSnapshot: chipOptionsToSnapshotRows(next),
|
||||
});
|
||||
return next;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const description = (
|
||||
<>
|
||||
<span className="leading-[1.3] text-[color:var(--color-content-default-tertiary,#b4b4b4)]">
|
||||
{cv.header.descriptionLead}{" "}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addHandlers.onAddClick}
|
||||
className="cursor-pointer font-inter font-normal leading-[1.3] text-[color:var(--color-content-default-tertiary,#b4b4b4)] underline decoration-solid underline-offset-[3px] hover:opacity-90"
|
||||
>
|
||||
{cv.header.addLink}
|
||||
</button>
|
||||
<span className="leading-[1.3] text-[color:var(--color-content-default-tertiary,#b4b4b4)]">
|
||||
{" "}
|
||||
{cv.header.descriptionTrail}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<CreateFlowTwoColumnSelectShell
|
||||
lgVerticalAlign="start"
|
||||
header={
|
||||
<CreateFlowHeaderLockup
|
||||
title={cv.header.title}
|
||||
description={description}
|
||||
justification="left"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MultiSelect
|
||||
formHeader={false}
|
||||
size="M"
|
||||
options={coreValueOptions}
|
||||
onChipClick={handleChipClick}
|
||||
onAddClick={addHandlers.onAddClick}
|
||||
onCustomChipConfirm={addHandlers.onCustomChipConfirm}
|
||||
onCustomChipClose={addHandlers.onCustomChipClose}
|
||||
addButton
|
||||
addButtonText={cv.multiSelect.addButtonText}
|
||||
/>
|
||||
</CreateFlowTwoColumnSelectShell>
|
||||
);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export type CreateFlowStep =
|
||||
| "community-upload"
|
||||
| "community-save"
|
||||
| "review"
|
||||
| "core-values"
|
||||
| "cards"
|
||||
| "right-rail"
|
||||
| "confirm-stakeholders"
|
||||
@@ -70,6 +71,10 @@ export interface CreateFlowState {
|
||||
scale?: CommunityStructureChipSnapshotRow[];
|
||||
maturity?: CommunityStructureChipSnapshotRow[];
|
||||
};
|
||||
/** Create Custom — core values step (max five `selectedCoreValueIds`). */
|
||||
selectedCoreValueIds?: string[];
|
||||
/** Full chip rows for core values (custom labels). */
|
||||
coreValuesChipsSnapshot?: CommunityStructureChipSnapshotRow[];
|
||||
currentStep?: CreateFlowStep;
|
||||
/** Section drafts; structure will tighten as steps persist real shapes. */
|
||||
sections?: Record<string, unknown>[];
|
||||
|
||||
@@ -15,6 +15,7 @@ const PROPORTION_BY_STEP_INDEX: readonly ProportionBarState[] = [
|
||||
"1-5", // community-upload
|
||||
"2-0", // community-save
|
||||
"2-0", // review (Figma Flow — Review `19706:12135`: same segment fill as end of Create Community)
|
||||
"2-0", // core-values (same segment as review / end of Create Community)
|
||||
"2-2", // cards
|
||||
"3-0", // right-rail
|
||||
"3-1", // confirm-stakeholders
|
||||
|
||||
@@ -84,6 +84,12 @@ export const CREATE_FLOW_SCREEN_REGISTRY: Record<
|
||||
messageNamespace: "create.review",
|
||||
centeredBodyBelowMd: false,
|
||||
},
|
||||
"core-values": {
|
||||
layoutKind: "select",
|
||||
figmaNodeId: "20264-68378",
|
||||
messageNamespace: "create.coreValues",
|
||||
centeredBodyBelowMd: false,
|
||||
},
|
||||
cards: {
|
||||
layoutKind: "card",
|
||||
figmaNodeId: "TBD-cards",
|
||||
|
||||
@@ -20,6 +20,7 @@ export const FLOW_STEP_ORDER: readonly CreateFlowStep[] = [
|
||||
"community-upload",
|
||||
"community-save",
|
||||
"review",
|
||||
"core-values",
|
||||
"cards",
|
||||
"right-rail",
|
||||
"confirm-stakeholders",
|
||||
|
||||
@@ -51,6 +51,10 @@ export const createFlowStateSchema = z
|
||||
selectedMaturityIds: z.array(z.string()).optional(),
|
||||
communityStructureChipSnapshots:
|
||||
communityStructureChipSnapshotsSchema.optional(),
|
||||
selectedCoreValueIds: z.array(z.string()).max(200).optional(),
|
||||
coreValuesChipsSnapshot: z
|
||||
.array(communityStructureChipSnapshotRowSchema)
|
||||
.optional(),
|
||||
currentStep: createFlowStepSchema.optional(),
|
||||
sections: z.array(z.unknown()).optional(),
|
||||
stakeholders: z.array(z.unknown()).optional(),
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"header": {
|
||||
"title": "Choose up to five Core Values",
|
||||
"descriptionLead": "What does the community hold most dear? You can also combine or",
|
||||
"addLink": "add",
|
||||
"descriptionTrail": "new values to the list."
|
||||
},
|
||||
"multiSelect": {
|
||||
"addButtonText": "Add value"
|
||||
},
|
||||
"values": [
|
||||
"Accessibility",
|
||||
"Accountability",
|
||||
"Adaptability",
|
||||
"Agency",
|
||||
"Altruism",
|
||||
"Anti-oppression",
|
||||
"Autonomy",
|
||||
"Capacity Building",
|
||||
"Collaboration",
|
||||
"Common Ownership",
|
||||
"Community Care",
|
||||
"Conflict Resolution",
|
||||
"Consent",
|
||||
"Consensus",
|
||||
"Constructive Feedback",
|
||||
"Cooperation",
|
||||
"Copyleft",
|
||||
"Decentralization",
|
||||
"Direct Action",
|
||||
"Diversity",
|
||||
"Documentation",
|
||||
"Education",
|
||||
"Empathy",
|
||||
"Empowerment",
|
||||
"Equity",
|
||||
"Experimentation",
|
||||
"Fairness",
|
||||
"Fair Remuneration",
|
||||
"Flexibility",
|
||||
"Forkability",
|
||||
"Freedom",
|
||||
"Freedom of Information",
|
||||
"Generosity",
|
||||
"Harm Reduction",
|
||||
"Holism",
|
||||
"Holocracy",
|
||||
"Honesty",
|
||||
"Horizontalism",
|
||||
"Humility",
|
||||
"Inclusion",
|
||||
"Inclusivity",
|
||||
"Independence",
|
||||
"Innovation",
|
||||
"Integrity",
|
||||
"Interdependence",
|
||||
"Interoperability",
|
||||
"Intersectionality",
|
||||
"Justice",
|
||||
"Knowledge Sharing",
|
||||
"Labor Rights",
|
||||
"Leadership",
|
||||
"Learning",
|
||||
"Liberty",
|
||||
"Localism",
|
||||
"Maintenance",
|
||||
"Mentorship",
|
||||
"Meritocracy",
|
||||
"Mutual Aid",
|
||||
"Non-violence",
|
||||
"Open Source",
|
||||
"Openness",
|
||||
"Participation"
|
||||
]
|
||||
}
|
||||
@@ -10,5 +10,6 @@
|
||||
"confirmDescription": "Confirm description",
|
||||
"confirmMembers": "Confirm members",
|
||||
"finalizeCommunityRule": "Finalize CommunityRule",
|
||||
"confirmStakeholders": "Confirm Stakeholders"
|
||||
"confirmStakeholders": "Confirm Stakeholders",
|
||||
"confirmCoreValues": "Confirm values"
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import createCommunityStructure from "./create/communityStructure.json";
|
||||
import createCommunityUpload from "./create/communityUpload.json";
|
||||
import createCommunitySave from "./create/communitySave.json";
|
||||
import createReview from "./create/review.json";
|
||||
import createCoreValues from "./create/coreValues.json";
|
||||
import createConfirmStakeholders from "./create/confirmStakeholders.json";
|
||||
import createFinalReview from "./create/finalReview.json";
|
||||
import createCompleted from "./create/completed.json";
|
||||
@@ -68,6 +69,7 @@ export default {
|
||||
communityUpload: createCommunityUpload,
|
||||
communitySave: createCommunitySave,
|
||||
review: createReview,
|
||||
coreValues: createCoreValues,
|
||||
confirmStakeholders: createConfirmStakeholders,
|
||||
finalReview: createFinalReview,
|
||||
completed: createCompleted,
|
||||
|
||||
@@ -17,10 +17,13 @@ describe("getProportionBarProgressForCreateFlowStep", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("uses 2-0 on community-save and review (end of Create Community segment)", () => {
|
||||
it("uses 2-0 on community-save, review, and core-values (Create Community segment / same fill)", () => {
|
||||
expect(getProportionBarProgressForCreateFlowStep("community-save")).toBe(
|
||||
"2-0",
|
||||
);
|
||||
expect(getProportionBarProgressForCreateFlowStep("review")).toBe("2-0");
|
||||
expect(getProportionBarProgressForCreateFlowStep("core-values")).toBe(
|
||||
"2-0",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,7 @@ describe("flowSteps", () => {
|
||||
it("isValidStep reflects FLOW_STEP_ORDER membership", () => {
|
||||
expect(isValidStep("community-size")).toBe(true);
|
||||
expect(isValidStep("confirm-stakeholders")).toBe(true);
|
||||
expect(isValidStep("core-values")).toBe(true);
|
||||
expect(isValidStep("nope")).toBe(false);
|
||||
expect(isValidStep(null)).toBe(false);
|
||||
});
|
||||
@@ -65,7 +66,7 @@ describe("flowSteps", () => {
|
||||
it("skipCommunitySave does not change steps outside the save segment", () => {
|
||||
const opts = { skipCommunitySave: true } as const;
|
||||
expect(getNextStep("community-size", opts)).toBe("community-upload");
|
||||
expect(getNextStep("review", opts)).toBe("cards");
|
||||
expect(getPreviousStep("cards", opts)).toBe("review");
|
||||
expect(getNextStep("review", opts)).toBe("core-values");
|
||||
expect(getPreviousStep("cards", opts)).toBe("core-values");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user