"use client" /** * Condition editor: one row per condition, shaped as a sentence. * * Subject, operator, value, in that order, because a rule is only readable when * it follows the grammar of a sentence. The operators come from the design * system's `FilterOperator` rather than a private enum, so "does not contain" * means here what it means in a hub filter and the wording is already decided. * * The value control follows the subject: free text for the identifier, and a * picker of that step's options when the subject is an earlier answer. Typing an * option id by hand is not a thing anyone should have to do. * * Rows combine with AND, shown as a leading "and" rather than a toggle. There is * no OR: it doubles the model and the reading cost to serve a case nobody has * asked for, and two conditions is already an unusual flow. */ import * as React from "react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { IDENTIFIER_SUBJECT_LABEL, createCondition, type Condition, type ConditionList, type LoginFlowDefinition, } from "@/lib/login-flow" import { OPERATOR_LABELS, type FilterOperator, } from "@exxatdesignux/ui/lib/table-properties-types" const OPERATORS = Object.keys(OPERATOR_LABELS) as FilterOperator[] const IDENTIFIER_VALUE = "identifier" /** * Choice steps before this one. Only earlier answers are offered, because a * condition on a later step's answer can never hold: at the moment it is read, * that step has not been asked yet. Offering it would author a step that silently * never runs. */ function earlierChoiceSteps(flow: LoginFlowDefinition, beforeIndex: number) { return flow.steps .slice(0, Math.max(beforeIndex, 0)) .filter(step => step.kind === "choice") } function ConditionRow({ condition, index, flow, beforeIndex, onChange, onRemove, }: { condition: Condition index: number flow: LoginFlowDefinition beforeIndex: number onChange: (next: Condition) => void onRemove: () => void }) { const base = `cond-${condition.id}` const choices = earlierChoiceSteps(flow, beforeIndex) const { subject } = condition const subjectValue = subject.kind === "identifier" ? IDENTIFIER_VALUE : subject.stepId const answerStep = subject.kind === "answer" ? choices.find(step => step.id === subject.stepId) : undefined return (
  • {index === 0 ? "When" : "and"}