Offer the engine's five size bands as exclusive ranges, so a count of 100 has one home and a group cannot continue without a size.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilalloandCursor committed 2026-11-04 16:13:10 -07:00
1 parent 821690201e
commit db278ab67d
11 files changed
+184 -61

No files matched your search

+10 -2
View File
@@ -85,7 +85,10 @@ import {
methodCardFacetSectionForConfirmStep,
type CustomRuleConfirmFooterStep,
} from "./utils/customRuleConfirmFooterSteps";
import { getDefaultFooterLabel } from "./utils/createFlowFooterLabels";
import {
getDefaultFooterLabel,
isCommunitySizeStepBlocked,
} from "./utils/createFlowFooterLabels";
import { useAuthModal } from "../../contexts/AuthModalContext";
import { useMessages, useTranslation } from "../../contexts/MessagesContext";
import { PostLoginDraftTransfer } from "./PostLoginDraftTransfer";
@@ -985,7 +988,12 @@ function CreateFlowLayoutContent({
palette="default"
size="xsmall"
disabled={
isPublishing || (isFinalReviewLike && !sessionResolved)
isPublishing ||
(isFinalReviewLike && !sessionResolved) ||
isCommunitySizeStepBlocked(
currentStep,
state.selectedCommunitySizeIds,
)
}
className={CREATE_FLOW_FOOTER_BUTTON_CLASS}
onClick={() => {
@@ -1,6 +1,6 @@
"use client";
import { useState, useEffect } from "react";
import { useState, useEffect, useLayoutEffect } from "react";
import MultiSelect from "../../../../components/controls/MultiSelect";
import type { ChipOption } from "../../../../components/controls/MultiSelect/MultiSelect.types";
import { useMessages } from "../../../../contexts/MessagesContext";
@@ -18,13 +18,32 @@ function chipRowsFromLabels(
}));
}
function selectedIdsFromOptions(options: ChipOption[]): string[] {
return options
.filter((o) => o.state === "selected")
.map((o) => o.id);
/** The first saved id. A count belongs to one band, so later ids are dropped. */
function soleSizeId(ids: readonly string[] | undefined): string | null {
for (const id of ids ?? []) {
if (id.length > 0) return id;
}
return null;
}
/** Create Community — Figma `20094:41317`, chips only (layout tokens shared with structure select). */
function withSoleSelection(
options: ChipOption[],
selectedId: string | null,
): ChipOption[] {
return options.map((opt) =>
opt.state === "custom"
? opt
: {
...opt,
state:
opt.id === selectedId
? ("selected" as const)
: ("unselected" as const),
},
);
}
/** Create Community — Figma `24715:26346`, chips size M (one question). */
export function CommunitySizeSelectScreen() {
const m = useMessages();
const cs = m.create.community.communitySize;
@@ -32,52 +51,40 @@ export function CommunitySizeSelectScreen() {
const [communitySizeOptions, setCommunitySizeOptions] = useState<
ChipOption[]
>(() => {
const base = chipRowsFromLabels(cs.communitySizes);
const selected = new Set(state.selectedCommunitySizeIds ?? []);
return base.map((opt) => ({
...opt,
state: selected.has(opt.id) ? ("selected" as const) : ("unselected" as const),
}));
});
>(() =>
withSoleSelection(
chipRowsFromLabels(cs.communitySizes),
soleSizeId(state.selectedCommunitySizeIds),
),
);
useLayoutEffect(() => {
const ids = state.selectedCommunitySizeIds;
if (!ids || ids.length === 0) return;
const first = soleSizeId(ids);
if (ids.length === 1 && ids[0] === first) return;
updateState({ selectedCommunitySizeIds: first ? [first] : [] });
}, [state.selectedCommunitySizeIds, updateState]);
useEffect(() => {
const selected = new Set(state.selectedCommunitySizeIds ?? []);
setCommunitySizeOptions((prev) =>
prev.map((opt) =>
opt.state === "custom"
? opt
: {
...opt,
state: selected.has(opt.id)
? ("selected" as const)
: ("unselected" as const),
},
),
);
const selectedId = soleSizeId(state.selectedCommunitySizeIds);
setCommunitySizeOptions((prev) => withSoleSelection(prev, selectedId));
}, [state.selectedCommunitySizeIds]);
const persistSelection = (next: ChipOption[]) => {
markCreateFlowInteraction();
setCommunitySizeOptions(next);
updateState({
selectedCommunitySizeIds: selectedIdsFromOptions(next),
selectedCommunitySizeIds: next
.filter((o) => o.state === "selected")
.map((o) => o.id),
});
};
const handleCommunitySizeClick = (chipId: string) => {
const next: ChipOption[] = communitySizeOptions.map((opt) =>
opt.id === chipId
? {
...opt,
state:
opt.state === "selected"
? ("unselected" as const)
: ("selected" as const),
}
: opt,
);
persistSelection(next);
const current = communitySizeOptions.find((opt) => opt.state === "selected");
const nextId = current?.id === chipId ? null : chipId;
persistSelection(withSoleSelection(communitySizeOptions, nextId));
};
const multiSelectBlock = (
+1 -1
View File
@@ -123,7 +123,7 @@ export interface CreateFlowState {
* Picked once and kept on the draft so reload does not change it.
*/
previewSurfaceColor?: string;
/** Selected chip ids from `community-size` (MultiSelect). */
/** The size band from `community-size`. One id; 100 is `thirteenToOneHundred`. */
selectedCommunitySizeIds?: string[];
/** Selected chip ids from `community-structure` (organization types). */
selectedOrganizationTypeIds?: string[];
@@ -20,9 +20,17 @@ const DEFAULT_FOOTER_LABEL_BY_STEP: ReadonlyMap<
["confirm-stakeholders", "confirmStakeholders"],
["community-context", "confirmDescription"],
["community-structure", "confirmDetails"],
["community-size", "confirmMembers"],
["community-size", "confirmSize"],
]);
export function isCommunitySizeStepBlocked(
step: CreateFlowStep | null | undefined,
selectedCommunitySizeIds: readonly string[] | undefined,
): boolean {
if (step !== "community-size") return false;
return !(selectedCommunitySizeIds ?? []).some((id) => id.length > 0);
}
/**
* Resolve the localized label for the default "next-step" footer button.
* Returns the per-step override when one is registered, otherwise
@@ -53,7 +53,7 @@ export const CREATE_FLOW_SCREEN_REGISTRY: Record<
},
"community-size": {
layoutKind: "select",
figmaNodeId: "20094-41317",
figmaNodeId: "24715-26346",
messageNamespace: "create.community.communitySize",
centeredBodyBelowMd: false,
},