'use client'; /* Origin: ticket-management (96S2) `tickets/ConditionGroupsBuilder.tsx`. */ import { Plus, X } from 'lucide-react'; import { MAX_CONDITION_GROUPS, MAX_CONDITIONS, type ConditionGroup, type FilterField, type MatchMode, type Operator, } from '@/lib/conditions'; import { PANEL_EMPTY_CLASS, panelAddButtonClass } from '@/lib/toolbar'; import { ConditionRow, blankCondition, nextConditionForField } from './FilterPanel'; /** The first row of a GROUP carries no joiner word — the group's own card and * the "Or" divider above it already say where it sits. */ function groupJoiner(index: number, matchMode: MatchMode): string { return index === 0 ? '' : matchMode === 'any' ? 'or' : 'and'; } /** * OR-of-AND condition GROUPS — Lark Base's automation shape. Each group is a * light-grey card of one or more `[field] [operator] [value]` rows, built from * `FilterPanel`'s own exported `ConditionRow` (never a second implementation * of it); rows within a group are joined by the SHARED `matchMode`; a plain * "Or" LABEL sits between groups — never a control, because groups are * always OR'd. * * Stateless, like `ConditionRow`: the owner keeps the draft and decides what a * change means. An empty group beside a filled one is allowed here as a DRAFT * state (the editor is mid-edit); `groupsMatch` reads an empty group as * "matches everything", so an owner that saves should refuse that shape. */ export default function ConditionGroupsBuilder({ fields, groups, matchMode, onChange, emptyText = 'No conditions — matches every row.', }: { fields: FilterField[]; groups: ConditionGroup[]; matchMode: MatchMode; onChange: (groups: ConditionGroup[], matchMode: MatchMode) => void; /** What the builder says when there are no groups at all. */ emptyText?: string; }) { const setGroupConditions = (gi: number, conditions: ConditionGroup['conditions']) => onChange(groups.map((g, i) => (i === gi ? { conditions } : g)), matchMode); const addGroup = () => { if (groups.length >= MAX_CONDITION_GROUPS) return; onChange([...groups, { conditions: [] }], matchMode); }; const removeGroup = (gi: number) => onChange(groups.filter((_, i) => i !== gi), matchMode); const addRow = (gi: number) => { const blank = blankCondition(fields); if (blank) setGroupConditions(gi, [...groups[gi].conditions, blank]); }; const changeRowField = (gi: number, ci: number, fieldId: string) => { const next = nextConditionForField(fields, groups[gi].conditions[ci], fieldId); if (next) setGroupConditions(gi, groups[gi].conditions.map((c, i) => (i === ci ? next : c))); }; const changeRowOp = (gi: number, ci: number, op: Operator) => setGroupConditions(gi, groups[gi].conditions.map((c, i) => (i === ci ? { ...c, op, value: '' } : c))); const changeRowValue = (gi: number, ci: number, value: string) => setGroupConditions(gi, groups[gi].conditions.map((c, i) => (i === ci ? { ...c, value } : c))); const removeRow = (gi: number, ci: number) => setGroupConditions(gi, groups[gi].conditions.filter((_, i) => i !== ci)); return (
{emptyText}
) : (No conditions in this group.
) : ( group.conditions.map((c, ci) => (