Open each core value as a card of meaning and signals steps, so groups write their own words from an empty field with Generate draft, skip values without blocking the flow, and Customize without keeping helper text as the description.

This commit is contained in:
adilallo committed 2026-11-05 21:28:00 -07:00
1 parent 2a7b9e6da7
commit 64a542f456
25 files changed
+877 -354

No files matched your search

+181
View File
@@ -0,0 +1,181 @@
"use client";
/**
* Figma: Overlay / Modal / Card (24973:1852) with Has Text off and Has Steps
* on. A value opens a card of meaning + signals rows; each row opens that
* field's Step Modal (23746:49780) with Generate draft on an empty field.
*/
import { memo, useCallback, useEffect, useState, type ReactNode } from "react";
import Create from "../../../components/modals/Create";
import { useMessages } from "../../../contexts/MessagesContext";
import type { ModalHeaderMenuItem } from "../../../components/modals/ModalHeader/ModalHeader.types";
import type { CoreValueDetailEntry } from "../types";
import ModalTextAreaField from "./ModalTextAreaField";
import { ValueDialogStepCard } from "./ValueDialogStepCard";
export type ValueDialogSession = "pending" | "editing";
export type ValueDialogStep = "meaning" | "signals";
export type ValueDialogProps = {
isOpen: boolean;
valueLabel: string;
session: ValueDialogSession | null;
draft: CoreValueDetailEntry;
onDraftChange: (_next: CoreValueDetailEntry) => void;
generateDraft?: CoreValueDetailEntry;
onClose: () => void;
onConfirm: () => void;
onDeselect?: () => void;
nextButtonDisabled?: boolean;
kebabTriggerAriaLabel?: string;
kebabMenuAriaLabel?: string;
kebabMenuItems?: ModalHeaderMenuItem[];
ariaLabel?: string;
/** Customized field-blocks body. Replaces the meaning/signals step cards. */
children?: ReactNode;
};
function ValueDialogComponent({
isOpen,
valueLabel,
session,
draft,
onDraftChange,
generateDraft,
onClose,
onConfirm,
onDeselect,
nextButtonDisabled = false,
kebabTriggerAriaLabel,
kebabMenuAriaLabel,
kebabMenuItems,
ariaLabel,
children,
}: ValueDialogProps) {
const m = useMessages();
const t = m.create.customRule.coreValues.detailModal;
const draftField = m.create.draftField;
const [activeStep, setActiveStep] = useState<ValueDialogStep | null>(null);
useEffect(() => {
if (!isOpen) {
setActiveStep(null);
}
}, [isOpen]);
const handleRequestClose = useCallback(() => {
if (activeStep != null) {
setActiveStep(null);
return;
}
onClose();
}, [activeStep, onClose]);
const meaningDone = draft.meaning.trim().length > 0;
const signalsDone = draft.signals.trim().length > 0;
const useCustomBody = Boolean(children);
const showSteps = !useCustomBody && activeStep == null;
const editing = session === "editing";
const primaryLabel = editing ? t.save : t.addToRule;
const stepTitle =
activeStep === "meaning"
? t.meaningStepTitle.replace("{value}", valueLabel)
: activeStep === "signals"
? t.signalsLabel
: valueLabel;
const stepDescription =
activeStep === "meaning"
? t.meaningDescription
: activeStep === "signals"
? t.signalsStepDescription.replace("{value}", valueLabel)
: undefined;
return (
<Create
isOpen={isOpen}
onClose={handleRequestClose}
title={stepTitle}
description={activeStep != null ? stepDescription : undefined}
showBackButton={false}
showCancelButton
showNextButton
showRemoveButton={editing && activeStep == null}
onCancel={handleRequestClose}
onNext={onConfirm}
onRemove={onDeselect}
cancelButtonText={t.cancel}
nextButtonText={primaryLabel}
removeButtonText={t.deselect}
nextButtonDisabled={nextButtonDisabled}
stepper={false}
backdropVariant="blurredYellow"
kebabTriggerAriaLabel={kebabTriggerAriaLabel}
kebabMenuAriaLabel={kebabMenuAriaLabel}
kebabMenuItems={
activeStep == null && kebabMenuItems && kebabMenuItems.length > 0
? kebabMenuItems
: undefined
}
ariaLabel={ariaLabel || valueLabel}
>
{useCustomBody ? (
children
) : showSteps ? (
<div className="flex w-full flex-col gap-[8px]">
<ValueDialogStepCard
stepNumber={1}
stepBadgeLabel={t.stepBadge}
title={t.meaningLabel}
description={
meaningDone ? draft.meaning : t.meaningDescription
}
done={meaningDone}
onClick={() => setActiveStep("meaning")}
/>
<ValueDialogStepCard
stepNumber={2}
stepBadgeLabel={t.stepBadge}
title={t.signalsLabel}
description={
signalsDone ? draft.signals : t.signalsDescription
}
done={signalsDone}
onClick={() => setActiveStep("signals")}
/>
</div>
) : activeStep === "meaning" ? (
<ModalTextAreaField
hideLabel
label={t.meaningLabel}
value={draft.meaning}
onChange={(meaning) => onDraftChange({ ...draft, meaning })}
placeholder={t.placeholder}
generateDraftText={generateDraft?.meaning}
generateDraftLabel={draftField.generateDraft}
generateDraftHint={t.generateDraftHint}
filledHint={t.filledHint}
autoFocus
/>
) : (
<ModalTextAreaField
hideLabel
label={t.signalsLabel}
value={draft.signals}
onChange={(signals) => onDraftChange({ ...draft, signals })}
placeholder={t.placeholder}
generateDraftText={generateDraft?.signals}
generateDraftLabel={draftField.generateDraft}
generateDraftHint={t.generateDraftHint}
filledHint={t.filledHint}
autoFocus
/>
)}
</Create>
);
}
ValueDialogComponent.displayName = "ValueDialog";
export const ValueDialog = memo(ValueDialogComponent);