Show the whole Rule on one review card, with quiet flags for missing cards and Continue once each question has one.
This commit is contained in:
1 parent
cc878e3c19
commit
6e2e1915ae
30 files changed
+571
-218
No files matched your search
@@ -14,8 +14,11 @@ import {
|
||||
} from "../../components/CreateFlowLockupCardStepShell";
|
||||
import {
|
||||
buildFinalReviewCategoryRowsDetailed,
|
||||
type FinalReviewCategoryNames,
|
||||
type FinalReviewCategoryRowDetailed,
|
||||
} from "../../../../../lib/create/buildFinalReviewCategories";
|
||||
import { communityInitialsFromTitle } from "../../../../../lib/create/communityInitials";
|
||||
import { finalReviewChipHasWarning } from "../../../../../lib/create/finalReviewChipWarnings";
|
||||
import { applyFinalReviewChipEditPatch } from "../../../../../lib/create/applyFinalReviewChipEditPatch";
|
||||
import type {
|
||||
TemplateChipDetail,
|
||||
@@ -32,10 +35,6 @@ import { useCommunityAvatarDisplayUrl } from "../../hooks/useCommunityAvatarDisp
|
||||
import { usePreviewSurfaceColor } from "../../hooks/usePreviewSurfaceColor";
|
||||
import { useCreateFlowNavigation } from "../../hooks/useCreateFlowNavigation";
|
||||
import { createFlowStepForFacetGroup } from "../../utils/facetGroupToCreateFlowStep";
|
||||
import {
|
||||
getAssetPath,
|
||||
vectorMarkPath,
|
||||
} from "../../../../../lib/assetUtils";
|
||||
import { isPreviewSurfaceColor } from "../../../../../lib/create/previewSurfaceColor";
|
||||
import {
|
||||
persistDraftPreviewSurfaceColor,
|
||||
@@ -51,53 +50,17 @@ function bindingArbitrationNeedsSeparateYes(state: CreateFlowState): boolean {
|
||||
return joinsWithoutAYes && conflict.includes("binding-arbitration");
|
||||
}
|
||||
|
||||
const FACET_FALLBACK_ORDER: readonly TemplateFacetGroupKey[] = [
|
||||
"coreValues",
|
||||
"communication",
|
||||
"membership",
|
||||
"decisionApproaches",
|
||||
"conflictManagement",
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* `finalReview.json.categories` ships a demo ordering + localized names
|
||||
* (Values / Communication / Membership / Decision-making / Conflict
|
||||
* management). We reuse that ordering for the state-derived rows so the
|
||||
* Rule layout stays stable across customize / use-without-changes /
|
||||
* plain-custom flows, and fall back to the demo chips when state resolves
|
||||
* to nothing selected.
|
||||
*/
|
||||
function readFallbackCategoryRows(
|
||||
categories: readonly { name: string; chips: readonly string[] }[],
|
||||
): {
|
||||
names: {
|
||||
values: string;
|
||||
communication: string;
|
||||
membership: string;
|
||||
decisions: string;
|
||||
conflict: string;
|
||||
};
|
||||
rows: FinalReviewCategoryRowDetailed[];
|
||||
} {
|
||||
function categoryNamesFromMessages(
|
||||
categories: readonly { name: string }[],
|
||||
): FinalReviewCategoryNames {
|
||||
const get = (i: number): string =>
|
||||
typeof categories[i]?.name === "string" ? categories[i].name : "";
|
||||
return {
|
||||
names: {
|
||||
values: get(0),
|
||||
communication: get(1),
|
||||
membership: get(2),
|
||||
decisions: get(3),
|
||||
conflict: get(4),
|
||||
},
|
||||
rows: categories.map((c) => ({
|
||||
name: c.name,
|
||||
groupKey: null,
|
||||
entries: [...c.chips].map((label) => ({
|
||||
label,
|
||||
groupKey: null,
|
||||
overrideKey: null,
|
||||
})),
|
||||
})),
|
||||
values: get(0),
|
||||
communication: get(1),
|
||||
membership: get(2),
|
||||
decisions: get(3),
|
||||
conflict: get(4),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -145,28 +108,32 @@ export function FinalReviewScreen({
|
||||
);
|
||||
|
||||
const { categories: finalReviewCategories } = useMemo(() => {
|
||||
const { names, rows: fallbackRows } = readFallbackCategoryRows(
|
||||
m.create.reviewAndComplete.finalReview.categories,
|
||||
);
|
||||
const derived = buildFinalReviewCategoryRowsDetailed(state, names);
|
||||
const copy = m.create.reviewAndComplete.finalReview;
|
||||
const names = categoryNamesFromMessages(copy.categories);
|
||||
const rowsToRender: readonly FinalReviewCategoryRowDetailed[] =
|
||||
derived.length > 0 ? derived : fallbackRows;
|
||||
const usingFallbackRows = derived.length === 0;
|
||||
buildFinalReviewCategoryRowsDetailed(state, names);
|
||||
|
||||
const lookup = new Map<
|
||||
string,
|
||||
{ target: FinalReviewChipEditTarget | null; readOnly: TemplateChipDetail }
|
||||
>();
|
||||
|
||||
const cats: Category[] = rowsToRender.map((row, rowIndex) => {
|
||||
const effectiveGroupKey: TemplateFacetGroupKey | null =
|
||||
row.groupKey ??
|
||||
(usingFallbackRows && rowIndex < FACET_FALLBACK_ORDER.length
|
||||
? FACET_FALLBACK_ORDER[rowIndex]
|
||||
: null);
|
||||
|
||||
const cats: Category[] = rowsToRender.map((row) => {
|
||||
const effectiveGroupKey: TemplateFacetGroupKey | null = row.groupKey;
|
||||
const reviewReturn =
|
||||
variant === "editPublished" ? ("edit-rule" as const) : ("final-review" as const);
|
||||
const isEmpty = row.entries.length === 0;
|
||||
const flag =
|
||||
isEmpty && effectiveGroupKey === "coreValues"
|
||||
? copy.flags.noValues
|
||||
: isEmpty && effectiveGroupKey != null
|
||||
? copy.flags.noCard
|
||||
: undefined;
|
||||
const addButtonText = isEmpty
|
||||
? effectiveGroupKey === "coreValues"
|
||||
? copy.addValue
|
||||
: copy.addCard
|
||||
: "";
|
||||
|
||||
const chipOptions = row.entries.map((entry, idx) => {
|
||||
const chipId = `${row.name}-${idx}`;
|
||||
@@ -190,12 +157,19 @@ export function FinalReviewScreen({
|
||||
id: chipId,
|
||||
label: entry.label,
|
||||
state: "unselected" as const,
|
||||
hasWarning: finalReviewChipHasWarning(
|
||||
state,
|
||||
entry.groupKey,
|
||||
entry.overrideKey,
|
||||
),
|
||||
};
|
||||
});
|
||||
return {
|
||||
name: row.name,
|
||||
chipOptions,
|
||||
addButton: effectiveGroupKey != null,
|
||||
addButtonText,
|
||||
flag,
|
||||
onChipClick: (_categoryName: string, chipId: string) => {
|
||||
const hit = lookup.get(chipId);
|
||||
if (!hit) return;
|
||||
@@ -219,7 +193,7 @@ export function FinalReviewScreen({
|
||||
});
|
||||
return { categories: cats };
|
||||
}, [
|
||||
m.create.reviewAndComplete.finalReview.categories,
|
||||
m.create.reviewAndComplete.finalReview,
|
||||
state,
|
||||
markCreateFlowInteraction,
|
||||
goToStep,
|
||||
@@ -249,8 +223,7 @@ export function FinalReviewScreen({
|
||||
const rawTitleForModal =
|
||||
typeof state.title === "string" ? state.title : "";
|
||||
|
||||
const descriptionEmptyHint =
|
||||
variant === "editPublished" ? t("communityContextEditModal.emptyHint") : undefined;
|
||||
const descriptionEmptyHint = t("communityContextEditModal.emptyHint");
|
||||
|
||||
const avatarUrl = useCommunityAvatarDisplayUrl(state.communityAvatarUrl);
|
||||
const storedFallback =
|
||||
@@ -305,34 +278,21 @@ export function FinalReviewScreen({
|
||||
<Rule
|
||||
title={ruleCardTitle}
|
||||
description={ruleCardDescription}
|
||||
onTitleClick={
|
||||
variant === "editPublished"
|
||||
? () => setTitleModalOpen(true)
|
||||
: undefined
|
||||
}
|
||||
titleEditAriaLabel={
|
||||
variant === "editPublished"
|
||||
? t("titleEditModal.ariaEditTitle")
|
||||
: undefined
|
||||
}
|
||||
onDescriptionClick={
|
||||
variant === "editPublished"
|
||||
? () => setCommunityContextModalOpen(true)
|
||||
: undefined
|
||||
}
|
||||
onTitleClick={() => setTitleModalOpen(true)}
|
||||
titleEditAriaLabel={t("titleEditModal.ariaEditTitle")}
|
||||
onDescriptionClick={() => setCommunityContextModalOpen(true)}
|
||||
descriptionEmptyHint={descriptionEmptyHint}
|
||||
descriptionEditAriaLabel={
|
||||
variant === "editPublished"
|
||||
? t("communityContextEditModal.ariaEditDescription")
|
||||
: undefined
|
||||
}
|
||||
descriptionEditAriaLabel={t("communityContextEditModal.ariaEditDescription")}
|
||||
size={mdUp ? "L" : "M"}
|
||||
expanded={true}
|
||||
descriptionAtBottom
|
||||
backgroundColor="bg-transparent"
|
||||
surfaceColor={surfaceColor}
|
||||
logoUrl={avatarUrl ?? getAssetPath(vectorMarkPath("mutual-aid"))}
|
||||
logoUrl={avatarUrl}
|
||||
logoAlt={ruleCardTitle}
|
||||
communityInitials={
|
||||
avatarUrl ? undefined : communityInitialsFromTitle(ruleCardTitle)
|
||||
}
|
||||
categories={finalReviewCategories}
|
||||
className={`${CREATE_FLOW_REVIEW_RULE_LAYOUT_CLASS}${surfaceColor ? "" : " invisible"}`}
|
||||
onClick={() => {}}
|
||||
@@ -352,28 +312,24 @@ export function FinalReviewScreen({
|
||||
onClose={() => setActiveReadOnlyDetail(null)}
|
||||
detail={activeReadOnlyDetail}
|
||||
/>
|
||||
{variant === "editPublished" ? (
|
||||
<>
|
||||
<FinalReviewTitleEditModal
|
||||
isOpen={titleModalOpen}
|
||||
onClose={() => setTitleModalOpen(false)}
|
||||
initialValue={rawTitleForModal}
|
||||
onSave={(value) => {
|
||||
markCreateFlowInteraction();
|
||||
updateState({ title: value });
|
||||
}}
|
||||
/>
|
||||
<FinalReviewCommunityContextEditModal
|
||||
isOpen={communityContextModalOpen}
|
||||
onClose={() => setCommunityContextModalOpen(false)}
|
||||
initialValue={rawCommunityContextForModal}
|
||||
onSave={(value) => {
|
||||
markCreateFlowInteraction();
|
||||
updateState({ communityContext: value, summary: value });
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
<FinalReviewTitleEditModal
|
||||
isOpen={titleModalOpen}
|
||||
onClose={() => setTitleModalOpen(false)}
|
||||
initialValue={rawTitleForModal}
|
||||
onSave={(value) => {
|
||||
markCreateFlowInteraction();
|
||||
updateState({ title: value });
|
||||
}}
|
||||
/>
|
||||
<FinalReviewCommunityContextEditModal
|
||||
isOpen={communityContextModalOpen}
|
||||
onClose={() => setCommunityContextModalOpen(false)}
|
||||
initialValue={rawCommunityContextForModal}
|
||||
onSave={(value) => {
|
||||
markCreateFlowInteraction();
|
||||
updateState({ communityContext: value, summary: value });
|
||||
}}
|
||||
/>
|
||||
</CreateFlowLockupCardStepShell>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user