@open-mercato/ui 0.6.1-develop.3291.1.6fad645fd0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +2 -0
  3. package/dist/ai/AiAssistantLauncher.js +8 -8
  4. package/dist/ai/AiAssistantLauncher.js.map +2 -2
  5. package/dist/ai/AiChat.js +92 -93
  6. package/dist/ai/AiChat.js.map +2 -2
  7. package/dist/ai/AiIcon.js +17 -0
  8. package/dist/ai/AiIcon.js.map +7 -0
  9. package/dist/ai/LoopDisabledBanner.js +28 -0
  10. package/dist/ai/LoopDisabledBanner.js.map +7 -0
  11. package/dist/ai/LoopTracePanel.js +123 -0
  12. package/dist/ai/LoopTracePanel.js.map +7 -0
  13. package/dist/ai/ModelPicker.js.map +1 -1
  14. package/dist/ai/index.js +8 -0
  15. package/dist/ai/index.js.map +2 -2
  16. package/dist/ai/parts/ConfirmationCard.js +1 -5
  17. package/dist/ai/parts/ConfirmationCard.js.map +2 -2
  18. package/dist/ai/parts/FieldDiffCard.js +8 -15
  19. package/dist/ai/parts/FieldDiffCard.js.map +2 -2
  20. package/dist/ai/parts/MutationResultCard.js +1 -4
  21. package/dist/ai/parts/MutationResultCard.js.map +2 -2
  22. package/dist/ai/ui-parts/pending-phase3-placeholder.js +0 -2
  23. package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +2 -2
  24. package/dist/ai/useAiChat.js +18 -4
  25. package/dist/ai/useAiChat.js.map +2 -2
  26. package/dist/backend/CrudForm.js +24 -2
  27. package/dist/backend/CrudForm.js.map +2 -2
  28. package/dist/backend/DataTable.js +2 -2
  29. package/dist/backend/DataTable.js.map +2 -2
  30. package/dist/backend/confirm-dialog/ConfirmDialog.js +102 -96
  31. package/dist/backend/confirm-dialog/ConfirmDialog.js.map +2 -2
  32. package/dist/backend/detail/CustomDataSection.js +22 -3
  33. package/dist/backend/detail/CustomDataSection.js.map +2 -2
  34. package/dist/backend/filters/AdvancedFilterBuilder.js +2 -2
  35. package/dist/backend/filters/AdvancedFilterBuilder.js.map +2 -2
  36. package/dist/backend/injection/ComponentOverrideProvider.js +1 -1
  37. package/dist/backend/injection/ComponentOverrideProvider.js.map +2 -2
  38. package/dist/backend/inputs/ComboboxInput.js +25 -7
  39. package/dist/backend/inputs/ComboboxInput.js.map +2 -2
  40. package/dist/backend/progress/useProgressPoll.js +58 -3
  41. package/dist/backend/progress/useProgressPoll.js.map +2 -2
  42. package/dist/backend/progress/useProgressSse.js +15 -2
  43. package/dist/backend/progress/useProgressSse.js.map +2 -2
  44. package/dist/backend/utils/bulkDelete.js +76 -0
  45. package/dist/backend/utils/bulkDelete.js.map +2 -2
  46. package/package.json +11 -6
  47. package/src/ai/AiAssistantLauncher.tsx +8 -8
  48. package/src/ai/AiChat.tsx +44 -38
  49. package/src/ai/AiIcon.tsx +12 -0
  50. package/src/ai/LoopDisabledBanner.tsx +38 -0
  51. package/src/ai/LoopTracePanel.tsx +184 -0
  52. package/src/ai/ModelPicker.tsx +1 -1
  53. package/src/ai/__tests__/AiChat.test.tsx +174 -5
  54. package/src/ai/index.ts +8 -0
  55. package/src/ai/parts/ConfirmationCard.tsx +1 -5
  56. package/src/ai/parts/FieldDiffCard.tsx +0 -3
  57. package/src/ai/parts/MutationResultCard.tsx +1 -4
  58. package/src/ai/parts/__tests__/MutationResultCard.test.tsx +5 -0
  59. package/src/ai/ui-parts/pending-phase3-placeholder.tsx +0 -2
  60. package/src/ai/useAiChat.ts +35 -5
  61. package/src/backend/CrudForm.tsx +30 -3
  62. package/src/backend/DataTable.tsx +2 -2
  63. package/src/backend/__tests__/CrudForm.custom-fields.test.tsx +72 -0
  64. package/src/backend/__tests__/CustomDataSection.test.tsx +76 -0
  65. package/src/backend/__tests__/DataTable.render.test.tsx +36 -0
  66. package/src/backend/__tests__/component-replacement.test.tsx +44 -0
  67. package/src/backend/confirm-dialog/ConfirmDialog.tsx +101 -91
  68. package/src/backend/confirm-dialog/__tests__/ConfirmDialog.test.tsx +19 -0
  69. package/src/backend/detail/CustomDataSection.tsx +26 -3
  70. package/src/backend/filters/AdvancedFilterBuilder.tsx +6 -6
  71. package/src/backend/injection/ComponentOverrideProvider.tsx +2 -1
  72. package/src/backend/inputs/ComboboxInput.tsx +29 -7
  73. package/src/backend/inputs/__tests__/ComboboxInput.test.tsx +33 -2
  74. package/src/backend/progress/useProgressPoll.ts +68 -3
  75. package/src/backend/progress/useProgressSse.ts +16 -2
  76. package/src/backend/utils/__tests__/bulkDelete.test.ts +42 -0
  77. package/src/backend/utils/bulkDelete.ts +93 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/filters/AdvancedFilterBuilder.tsx"],
4
- "sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { Plus, Trash2, X, GripVertical, ChevronDown } from 'lucide-react'\nimport {\n DndContext,\n DragOverlay,\n PointerSensor,\n KeyboardSensor,\n useSensor,\n useSensors,\n closestCenter,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core'\nimport {\n SortableContext,\n sortableKeyboardCoordinates,\n useSortable,\n verticalListSortingStrategy,\n} from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\nimport { Button } from '../../primitives/button'\nimport { IconButton } from '../../primitives/icon-button'\nimport { Input } from '../../primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../../primitives/select'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover'\nimport { Checkbox } from '../../primitives/checkbox'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n FilterFieldDef,\n FilterFieldType,\n FilterOperator,\n} from '@open-mercato/shared/lib/query/advanced-filter'\nimport {\n OPERATORS_BY_FIELD_TYPE,\n getDefaultOperator,\n isValuelessOperator,\n} from '@open-mercato/shared/lib/query/advanced-filter'\nimport {\n type AdvancedFilterTree,\n type FilterRule,\n type FilterGroup,\n type FilterCombinator,\n TREE_LIMITS,\n} from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport { treeReducer, canAddRule, canAddGroup, type TreeAction } from './treeReducer'\nimport { FilterFieldPicker } from './FilterFieldPicker'\n\nexport type AdvancedFilterBuilderProps = {\n fields: FilterFieldDef[]\n value: AdvancedFilterTree\n onChange: (state: AdvancedFilterTree) => void\n onApply: () => void\n onClear: () => void\n pendingErrors?: Array<{ ruleId: string; messageKey: string; message: string }>\n}\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n is: 'is', is_not: 'is not', contains: 'contains', does_not_contain: 'does not contain',\n starts_with: 'starts with', ends_with: 'ends with', is_empty: 'is empty', is_not_empty: 'is not empty',\n equals: 'equals', not_equals: 'not equals', greater_than: 'greater than', less_than: 'less than',\n greater_or_equal: 'greater or equal', less_or_equal: 'less or equal', between: 'between',\n is_before: 'is before', is_after: 'is after', is_any_of: 'is any of', is_none_of: 'is none of',\n is_true: 'is true', is_false: 'is false', has_any_of: 'has any of', has_all_of: 'has all of', has_none_of: 'has none of',\n}\n\nfunction getFieldType(fields: FilterFieldDef[], fieldKey: string): FilterFieldType {\n return fields.find((f) => f.key === fieldKey)?.type ?? 'text'\n}\n\n// Operator value shapes. Used to decide whether changing operator preserves the\n// current value: same shape preserves (`is` \u2192 `is not` keeps \"Active\"); different\n// shape resets (`is` \u2192 `between` clears, because value goes from scalar to pair).\ntype OperatorShape = 'valueless' | 'multi' | 'range' | 'single'\n\nconst MULTI_VALUE_OPS = new Set<FilterOperator>([\n 'is_any_of', 'is_none_of', 'has_any_of', 'has_all_of', 'has_none_of',\n])\n\nfunction getOperatorShape(op: FilterOperator): OperatorShape {\n if (isValuelessOperator(op)) return 'valueless'\n if (MULTI_VALUE_OPS.has(op)) return 'multi'\n if (op === 'between') return 'range'\n return 'single'\n}\n\ntype DragHandleProps = React.HTMLAttributes<HTMLButtonElement> & {\n ref?: React.Ref<HTMLButtonElement>\n}\n\ntype Translator = ReturnType<typeof useT>\n\nexport function AdvancedFilterBuilder({\n fields, value, onChange, onApply, onClear, pendingErrors,\n}: AdvancedFilterBuilderProps) {\n const t = useT()\n\n const dispatch = React.useCallback((action: TreeAction) => {\n onChange(treeReducer(value, action))\n }, [onChange, value])\n\n React.useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n onApply()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [onApply])\n\n const errorByRuleId = React.useMemo(() => {\n const m = new Map<string, string>()\n for (const e of pendingErrors ?? []) m.set(e.ruleId, t(e.messageKey, e.message))\n return m\n }, [pendingErrors, t])\n\n const empty = value.root.children.length === 0\n const defaultField = fields.length > 0 ? fields[0].key : undefined\n\n // Per-group SortableContexts scope drag-and-drop to within-group ordering.\n // The DragOverlay below renders a stable preview that\n // matches the original row's footprint \u2014 without it, dnd-kit applies\n // `transform` to the source row in place, which the wrapping flex layout\n // distorted into a \"tall\" / morphed ghost.\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),\n )\n const [activeDrag, setActiveDrag] = React.useState<{ node: FilterRule | FilterGroup; parent: FilterGroup } | null>(null)\n const findNode = React.useCallback((id: string) => locateInTree(value.root, id), [value])\n\n const handleDragStart = React.useCallback((e: DragStartEvent) => {\n const found = findNode(String(e.active.id))\n if (found) setActiveDrag(found)\n }, [findNode])\n\n const handleDragEnd = React.useCallback((e: DragEndEvent) => {\n setActiveDrag(null)\n const { active, over } = e\n if (!over || active.id === over.id) return\n const from = findNode(String(active.id))\n const to = findNode(String(over.id))\n if (!from || !to) return\n if (from.parent.id !== to.parent.id) return\n const fromIdx = from.parent.children.findIndex((c) => c.id === active.id)\n const toIdx = to.parent.children.findIndex((c) => c.id === over.id)\n if (fromIdx < 0 || toIdx < 0) return\n dispatch({ type: 'reorderChildren', groupId: from.parent.id, fromIdx, toIdx })\n }, [dispatch, findNode])\n\n const handleDragCancel = React.useCallback(() => setActiveDrag(null), [])\n\n // Wrapper has no `min-w-[640px]`: it forced horizontal scroll on a 375px viewport.\n // Rules wrap onto multiple lines on mobile; on desktop (`sm:max-w-[720px]`) they fit\n // on one line.\n return (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n <div className=\"flex flex-col gap-3 p-3\">\n {!empty ? (\n <GroupView\n group={value.root}\n level={1}\n fields={fields}\n tree={value}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n ) : null}\n {!empty ? (\n <div className=\"flex items-center gap-3 pt-2 border-t border-border\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto px-0 py-0 text-base text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={onClear}\n >\n <X className=\"size-4\" />\n {t('ui.advancedFilter.clearAll', 'Clear all')}\n </Button>\n </div>\n ) : null}\n </div>\n {/* Portal DragOverlay to document.body. Radix DialogContent uses CSS `transform`\n for centering, which becomes the containing block for `position: fixed`\n descendants (dnd-kit's overlay) and shifts the ghost off-screen. Portaling\n restores viewport-relative coordinates. */}\n {typeof document !== 'undefined'\n ? createPortal(\n // dnd-kit's DragOverlay sets its own inline z-index, so a Tailwind\n // utility class would be overridden \u2014 read the DS token via CSS var.\n <DragOverlay dropAnimation={null} style={{ zIndex: 'var(--z-index-modal-elevated, 55)' }}>\n {activeDrag ? (\n <DragGhost node={activeDrag.node} parent={activeDrag.parent} fields={fields} t={t} />\n ) : null}\n </DragOverlay>,\n document.body,\n )\n : null}\n </DndContext>\n )\n}\n\n/** Find a node in the tree along with its parent group. Used by drag handlers. */\nfunction locateInTree(root: FilterGroup, id: string): { node: FilterRule | FilterGroup; parent: FilterGroup } | null {\n for (const child of root.children) {\n if (child.id === id) return { node: child, parent: root }\n if (child.type === 'group') {\n const found = locateInTree(child, id)\n if (found) return found\n }\n }\n return null\n}\n\n// Compact drag preview rendered inside DragOverlay. Cannot reuse RuleRow/GroupView \u2014\n// their popovers and form inputs conflict with the overlay.\nfunction DragGhost({\n node, parent, fields, t,\n}: {\n node: FilterRule | FilterGroup\n parent: FilterGroup\n fields: FilterFieldDef[]\n t: Translator\n}) {\n const isGroup = node.type === 'group'\n const labelParts: string[] = []\n if (isGroup) {\n const ruleCount = countRulesIn(node)\n labelParts.push(t('ui.advancedFilter.dragGhost.group', 'Group'))\n labelParts.push(`(${ruleCount})`)\n } else {\n const def = fields.find((f) => f.key === node.field)\n labelParts.push(def?.label ?? (node.field || t('ui.advancedFilter.dragGhost.unsetField', 'Unset field')))\n labelParts.push(OPERATOR_LABELS[node.operator] ?? node.operator)\n if (typeof node.value === 'string' && node.value) labelParts.push(`\"${node.value}\"`)\n }\n return (\n <div className=\"inline-flex items-center gap-2 rounded-md border border-border bg-popover px-3 py-1.5 text-sm shadow-md cursor-grabbing\">\n <GripVertical className=\"size-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">{parent.combinator === 'and' ? t('ui.advancedFilter.connector.and', 'and') : t('ui.advancedFilter.connector.or', 'or')}</span>\n <span className=\"font-medium\">{labelParts.join(' ')}</span>\n </div>\n )\n}\n\nfunction countRulesIn(group: FilterGroup): number {\n let n = 0\n for (const c of group.children) {\n if (c.type === 'rule') n += 1\n else n += countRulesIn(c)\n }\n return n\n}\n\nfunction GroupView({\n group, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n group: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n const isRoot = level === 1\n // Sub-groups render as a card so the nesting boundary is visible; root group stays\n // flush with the popover so it doesn't double-up the dialog's own card chrome.\n const containerClass = isRoot\n ? 'space-y-3'\n : 'space-y-3 rounded-lg border border-border bg-muted/30 p-3'\n\n // Toolbar-first layout (Kendo React Filter pattern): one [And]/[Or] toggle per group\n // is the only combinator UI. Mixing AND with OR requires `+ Add subgroup`. Matches\n // `CompositeFilterDescriptor` semantics (one logic per group).\n return (\n <div className={containerClass}>\n <GroupToolbar\n group={group}\n level={level}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n fields={fields}\n isRoot={isRoot}\n t={t}\n />\n\n <SortableChildrenList\n group={group}\n level={level}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n </div>\n )\n}\n\n// Kendo-style segmented `[And] [Or]` toggle. Clicking the unselected side flips\n// the group's combinator. Selected side is brand-violet filled so the affordance\n// is unambiguous.\nfunction AndOrToggle({\n combinator, onChange, t,\n}: {\n combinator: FilterCombinator\n onChange: (c: FilterCombinator) => void\n t: Translator\n}) {\n const andLabel = t('ui.advancedFilter.combinator.and', 'And')\n const orLabel = t('ui.advancedFilter.combinator.or', 'Or')\n const baseBtn = 'h-8 px-3 text-sm font-medium outline-none focus-visible:shadow-focus disabled:cursor-not-allowed'\n const selBtn = 'bg-brand-violet text-white hover:bg-brand-violet/90'\n const unselBtn = 'bg-background text-foreground hover:bg-accent'\n return (\n <div\n role=\"group\"\n aria-label={t('ui.advancedFilter.combinator.label', 'Group combinator')}\n className=\"inline-flex rounded-md border border-input overflow-hidden shrink-0\"\n data-testid=\"advanced-filter-combinator-toggle\"\n data-combinator={combinator}\n >\n <button\n type=\"button\"\n aria-pressed={combinator === 'and'}\n onClick={() => combinator !== 'and' && onChange('and')}\n className={`${baseBtn} ${combinator === 'and' ? selBtn : unselBtn}`}\n >\n {andLabel}\n </button>\n <button\n type=\"button\"\n aria-pressed={combinator === 'or'}\n onClick={() => combinator !== 'or' && onChange('or')}\n className={`${baseBtn} border-l border-input ${combinator === 'or' ? selBtn : unselBtn}`}\n >\n {orLabel}\n </button>\n </div>\n )\n}\n\nfunction SortableChildrenList({\n group, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n group: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n // DndContext + handlers live one level up so a drag can move children across\n // groups. Each group owns its own SortableContext to scope sort ordering.\n return (\n <SortableContext items={group.children.map((c) => c.id)} strategy={verticalListSortingStrategy}>\n {group.children.map((child, idx) => (\n <SortableRow\n key={child.id}\n child={child}\n idx={idx}\n parent={group}\n level={level}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n ))}\n </SortableContext>\n )\n}\n\nfunction SortableRow({\n child, idx, parent, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n child: FilterRule | FilterGroup\n idx: number\n parent: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id: child.id,\n })\n // Hide the source while dragging (DragOverlay renders the snapshot); keep it in\n // layout via `visibility: hidden` so siblings don't jump. Without this, the flex\n // wrapper applies both `transform` and its own resizing to the source row.\n const style: React.CSSProperties = isDragging\n ? { visibility: 'hidden' as const }\n : { transform: CSS.Transform.toString(transform), transition }\n const dragHandleProps: DragHandleProps = { ...attributes, ...listeners }\n\n return (\n <div ref={setNodeRef} style={style} data-testid=\"filter-rule-row\" data-filter-node-id={child.id}>\n {child.type === 'rule' ? (\n <RuleRow\n rule={child}\n fields={fields}\n dispatch={dispatch}\n errorByRuleId={errorByRuleId}\n dragHandleProps={dragHandleProps}\n t={t}\n />\n ) : (\n <div className=\"flex items-start gap-2\">\n {/* Raw <button> required: dnd-kit spreads `attributes`+`listeners` onto the\n activator; <Button>'s own props/ref handling breaks the keyboard sensor. */}\n <button\n type=\"button\"\n {...dragHandleProps}\n data-testid=\"filter-drag-handle\"\n className=\"mt-1 text-muted-foreground/60 hover:text-muted-foreground cursor-grab outline-none focus-visible:shadow-focus rounded-sm\"\n aria-label={t('ui.advancedFilter.dragHandle', 'Drag to reorder')}\n >\n <GripVertical className=\"size-4\" />\n </button>\n <div className=\"flex-1\">\n <GroupView\n group={child}\n level={level + 1}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n </div>\n </div>\n )}\n </div>\n )\n}\n\nfunction RuleRow({\n rule, fields, dispatch, errorByRuleId, dragHandleProps, t,\n}: {\n rule: FilterRule\n fields: FilterFieldDef[]\n dispatch: (a: TreeAction) => void\n errorByRuleId: Map<string, string>\n dragHandleProps: DragHandleProps\n t: Translator\n}) {\n const fieldType = getFieldType(fields, rule.field)\n const operators = OPERATORS_BY_FIELD_TYPE[fieldType] ?? OPERATORS_BY_FIELD_TYPE.text\n const valueless = isValuelessOperator(rule.operator)\n const errorMsg = errorByRuleId.get(rule.id)\n const fieldDef = fields.find((f) => f.key === rule.field)\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const fieldBtnRef = React.useRef<HTMLButtonElement>(null)\n\n return (\n <div className=\"flex flex-col gap-1\" data-testid=\"filter-rule\" data-filter-rule-id={rule.id}>\n <div className=\"flex flex-wrap items-start gap-2\">\n {/* Raw <button> required for dnd-kit activator: see SortableRow drag-handle comment. */}\n <button\n type=\"button\"\n {...dragHandleProps}\n data-testid=\"filter-drag-handle\"\n className=\"mt-1 text-muted-foreground/60 hover:text-muted-foreground cursor-grab outline-none focus-visible:shadow-focus rounded-sm\"\n aria-label={t('ui.advancedFilter.dragHandle', 'Drag to reorder')}\n >\n <GripVertical className=\"size-4\" />\n </button>\n <Button\n ref={fieldBtnRef}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPickerOpen(true)}\n className=\"min-w-[140px] justify-start\"\n >\n {fieldDef?.label ?? t('ui.advancedFilter.selectField', 'Select field')}\n </Button>\n <FilterFieldPicker\n fields={fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(f) => {\n const nextType = getFieldType(fields, f.key)\n dispatch({\n type: 'updateRule',\n ruleId: rule.id,\n updates: { field: f.key, operator: getDefaultOperator(nextType), value: '' },\n })\n }}\n triggerRef={fieldBtnRef as React.RefObject<HTMLElement | null>}\n />\n <Select\n value={rule.operator}\n onValueChange={(next) => {\n // Preserve the value when the operator shape doesn't change; reset\n // only when shape changes (e.g. `is` \u2192 `between` clears scalar to pair).\n const nextOp = next as FilterOperator\n const sameShape = getOperatorShape(rule.operator) === getOperatorShape(nextOp)\n const updates: Partial<Pick<FilterRule, 'operator' | 'value'>> = sameShape\n ? { operator: nextOp }\n : { operator: nextOp, value: getOperatorShape(nextOp) === 'multi' || getOperatorShape(nextOp) === 'range' ? [] : '' }\n dispatch({ type: 'updateRule', ruleId: rule.id, updates })\n }}\n >\n {/* `w-auto` overrides SelectTrigger's default `w-full` so it sits\n inline with the field/value inputs instead of breaking onto a new row. */}\n <SelectTrigger\n size=\"sm\"\n className=\"w-auto min-w-[140px]\"\n aria-label={t('ui.advancedFilter.selectOperator', 'Select operator')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent data-advanced-filter-portal=\"\">\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {t(`ui.advancedFilter.operator.${op}`, OPERATOR_LABELS[op])}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {!valueless ? (\n <ValueInput\n rule={rule}\n fields={fields}\n fieldType={fieldType}\n dispatch={dispatch}\n hasError={!!errorMsg}\n t={t}\n />\n ) : null}\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => dispatch({ type: 'removeNode', nodeId: rule.id })}\n aria-label={t('ui.advancedFilter.removeCondition', 'Remove condition')}\n title={t('ui.advancedFilter.removeCondition', 'Remove condition')}\n >\n <Trash2 className=\"size-4 text-muted-foreground\" />\n </IconButton>\n </div>\n {errorMsg ? (\n <div className=\"ml-6 pl-12 text-xs text-status-error-text\">{errorMsg}</div>\n ) : null}\n </div>\n )\n}\n\nfunction ValueInput({\n rule, fields, fieldType, dispatch, hasError, t,\n}: {\n rule: FilterRule\n fields: FilterFieldDef[]\n fieldType: FilterFieldType\n dispatch: (a: TreeAction) => void\n hasError: boolean\n t: Translator\n}) {\n const fieldDef = fields.find((f) => f.key === rule.field)\n const value = rule.value\n const errorClass = hasError\n ? 'border-status-error-border ring-1 ring-status-error-border focus-visible:border-status-error-border focus-within:border-status-error-border'\n : ''\n\n if (fieldType === 'select' && fieldDef?.options) {\n // Multi-value operators (`is_any_of`, `is_none_of`, `has_any_of`, etc.)\n // need an array value with multiple selectable options. Radix `Select` is\n // single-select only, so the previous code silently overwrote each\n // selection (and validation kept failing because the value never became an\n // array). Render a checkbox-list popover when the operator is multi-shape.\n if (getOperatorShape(rule.operator) === 'multi') {\n return (\n <MultiValuePicker\n rule={rule}\n options={fieldDef.options}\n dispatch={dispatch}\n errorClass={errorClass}\n t={t}\n />\n )\n }\n return (\n <Select\n value={typeof value === 'string' && value.length ? value : undefined}\n onValueChange={(next) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: next ?? '' } })\n }\n >\n <SelectTrigger\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n aria-label={t('ui.advancedFilter.selectValue', 'Select value')}\n >\n <SelectValue placeholder={t('ui.advancedFilter.selectValuePlaceholder', 'Select...')} />\n </SelectTrigger>\n <SelectContent data-advanced-filter-portal=\"\">\n {fieldDef.options.map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.tone ? (\n <span className=\"inline-flex items-center gap-2\">\n <span className={toneDotClass(opt.tone)} aria-hidden=\"true\" />\n {opt.label}\n </span>\n ) : (\n opt.label\n )}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n }\n\n // `Input` ships `w-full` by default \u2014 use `w-auto` so inputs don't stretch\n // inside the wrapping flex row. `between` needs a `[start, end]` pair, so it\n // gets its own component below.\n if (rule.operator === 'between' && (fieldType === 'number' || fieldType === 'date')) {\n return (\n <BetweenInput\n rule={rule}\n fieldType={fieldType}\n dispatch={dispatch}\n errorClass={errorClass}\n t={t}\n />\n )\n }\n\n if (fieldType === 'date') {\n return (\n <Input\n type=\"date\"\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n aria-label={t('ui.advancedFilter.dateValue', 'Date value')}\n />\n )\n }\n\n if (fieldType === 'number') {\n return (\n <Input\n type=\"number\"\n size=\"sm\"\n className={`w-[120px] ${errorClass}`}\n value={typeof value === 'number' ? value : typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n placeholder={t('ui.advancedFilter.numberPlaceholder', 'Value')}\n aria-label={t('ui.advancedFilter.numberValue', 'Number value')}\n />\n )\n }\n\n return (\n <Input\n type=\"text\"\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n placeholder={t('ui.advancedFilter.textPlaceholder', 'Value...')}\n aria-label={t('ui.advancedFilter.textValue', 'Text value')}\n />\n )\n}\n\n/**\n * Two inputs (start / end) for the `between` operator on date and number\n * fields. Stores the value as `[start, end]`; either side can be empty while\n * the user types, but validation requires both to be set before apply.\n */\nfunction BetweenInput({\n rule, fieldType, dispatch, errorClass, t,\n}: {\n rule: FilterRule\n fieldType: FilterFieldType\n dispatch: (a: TreeAction) => void\n errorClass: string\n t: Translator\n}) {\n const pair = Array.isArray(rule.value) ? rule.value : []\n const start = typeof pair[0] === 'string' || typeof pair[0] === 'number' ? String(pair[0] ?? '') : ''\n const end = typeof pair[1] === 'string' || typeof pair[1] === 'number' ? String(pair[1] ?? '') : ''\n const update = (which: 'start' | 'end', next: string) => {\n const value = which === 'start' ? [next, end] : [start, next]\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value } })\n }\n const inputType = fieldType === 'date' ? 'date' : 'number'\n const inputWidth = fieldType === 'date' ? 'w-auto min-w-[140px]' : 'w-[110px]'\n return (\n <div className=\"inline-flex items-center gap-1\">\n <Input\n type={inputType}\n size=\"sm\"\n className={`${inputWidth} ${errorClass}`}\n value={start}\n onChange={(e) => update('start', e.target.value)}\n placeholder={fieldType === 'number' ? t('ui.advancedFilter.numberPlaceholder', 'Value') : undefined}\n aria-label={t('ui.advancedFilter.betweenStart', 'Start')}\n />\n <span className=\"text-xs text-muted-foreground\">{t('ui.advancedFilter.and', 'and')}</span>\n <Input\n type={inputType}\n size=\"sm\"\n className={`${inputWidth} ${errorClass}`}\n value={end}\n onChange={(e) => update('end', e.target.value)}\n placeholder={fieldType === 'number' ? t('ui.advancedFilter.numberPlaceholder', 'Value') : undefined}\n aria-label={t('ui.advancedFilter.betweenEnd', 'End')}\n />\n </div>\n )\n}\n\n/**\n * Trigger + popover with a checkbox list. Used when a select-typed rule has a\n * multi-shape operator (`is_any_of`, `has_all_of`, etc.). The trigger displays\n * a \"{first} +N\" summary; the popover lets the user toggle each option.\n */\nfunction MultiValuePicker({\n rule, options, dispatch, errorClass, t,\n}: {\n rule: FilterRule\n options: NonNullable<FilterFieldDef['options']>\n dispatch: (a: TreeAction) => void\n errorClass: string\n t: Translator\n}) {\n const selected: string[] = Array.isArray(rule.value)\n ? rule.value.map((v) => String(v)).filter((v) => v.length > 0)\n : []\n const selectedLabel = (() => {\n if (selected.length === 0) return t('ui.advancedFilter.selectValuePlaceholder', 'Select...')\n const firstOpt = options.find((o) => o.value === selected[0])\n const head = firstOpt?.label ?? selected[0]\n const more = selected.length - 1\n return more > 0 ? `${head} +${more}` : head\n })()\n const toggle = (val: string) => {\n const next = selected.includes(val) ? selected.filter((v) => v !== val) : [...selected, val]\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: next } })\n }\n return (\n <Popover>\n <PopoverTrigger asChild>\n {/* Visually mirrors `SelectTrigger size=\"sm\"` (h-8, rounded-md, border)\n so the operator + value pair stays visually consistent. */}\n <button\n type=\"button\"\n className={`inline-flex h-8 items-center justify-between gap-2 rounded-md border border-input bg-background px-2.5 text-xs shadow-xs transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground w-auto min-w-[160px] ${errorClass}`}\n aria-label={t('ui.advancedFilter.selectValue', 'Select value')}\n data-testid=\"multi-value-trigger\"\n >\n <span className={selected.length === 0 ? 'text-muted-foreground' : ''}>{selectedLabel}</span>\n <ChevronDown className=\"size-3 text-muted-foreground\" />\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-1\" align=\"start\" data-advanced-filter-portal=\"\">\n <div className=\"max-h-64 overflow-y-auto\" role=\"listbox\" aria-multiselectable=\"true\">\n {options.map((opt) => {\n const isOn = selected.includes(opt.value)\n return (\n // Raw <button>: needs `role=\"option\"` + `aria-selected`; <Button> would override role.\n <button\n key={opt.value}\n type=\"button\"\n role=\"option\"\n aria-selected={isOn}\n onClick={() => toggle(opt.value)}\n className=\"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-accent focus-visible:bg-accent focus-visible:outline-none\"\n >\n <Checkbox checked={isOn} aria-hidden=\"true\" tabIndex={-1} className=\"pointer-events-none\" />\n {opt.tone ? (\n <span className=\"inline-flex items-center gap-2\">\n <span className={toneDotClass(opt.tone)} aria-hidden=\"true\" />\n {opt.label}\n </span>\n ) : (\n <span>{opt.label}</span>\n )}\n </button>\n )\n })}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\nfunction toneDotClass(tone: NonNullable<FilterFieldDef['options']>[number]['tone']): string {\n // Solid filled dot using the saturated `*-icon` token (matches Tag's `dotColorMap`).\n const base = 'inline-block size-2 rounded-full shrink-0'\n switch (tone) {\n case 'success': return `${base} bg-status-success-icon`\n case 'error': return `${base} bg-status-error-icon`\n case 'warning': return `${base} bg-status-warning-icon`\n case 'info': return `${base} bg-status-info-icon`\n case 'neutral': return `${base} bg-status-neutral-icon`\n case 'pink': return `${base} bg-status-pink-icon`\n case 'brand': return `${base} bg-brand-violet`\n default: return `${base} bg-muted-foreground/40`\n }\n}\n\n// Single Kendo-style toolbar per group: combinator toggle, `+ Add condition`,\n// `+ Add subgroup` / `+ Add group`, plus a trailing delete for non-root groups.\nfunction GroupToolbar({\n group, level, tree, dispatch, defaultField, fields, isRoot, t,\n}: {\n group: FilterGroup\n level: number\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n fields: FilterFieldDef[]\n isRoot: boolean\n t: Translator\n}) {\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const addBtnRef = React.useRef<HTMLButtonElement>(null)\n const addRuleEnabled = canAddRule(tree, group.id)\n const addGroupEnabled = canAddGroup(tree, group.id)\n\n const widthCapHit = group.children.length >= TREE_LIMITS.maxChildrenPerGroup\n const depthCapHit = level >= TREE_LIMITS.maxGroupLevel\n\n const addRuleTitle = !addRuleEnabled\n ? widthCapHit\n ? t('ui.advancedFilter.limitWidthReached', 'Maximum {max} conditions per group', {\n max: TREE_LIMITS.maxChildrenPerGroup,\n })\n : t('ui.advancedFilter.limitTotalReached', 'Maximum {max} conditions reached', {\n max: TREE_LIMITS.maxTotalRules,\n })\n : undefined\n\n const addGroupTitle = !addGroupEnabled\n ? depthCapHit\n ? t('ui.advancedFilter.limitDepthReached', 'Maximum nesting depth reached')\n : widthCapHit\n ? t('ui.advancedFilter.limitWidthReached', 'Maximum {max} conditions per group', {\n max: TREE_LIMITS.maxChildrenPerGroup,\n })\n : t('ui.advancedFilter.limitTotalReached', 'Maximum {max} conditions reached', {\n max: TREE_LIMITS.maxTotalRules,\n })\n : undefined\n\n const addGroupLabel = level === 1\n ? t('ui.advancedFilter.addGroup', '+ Add group')\n : t('ui.advancedFilter.addSubgroup', '+ Add subgroup')\n\n // `text-brand-violet` overrides Button's default disabled token, so we have to\n // re-state muted disabled colors here \u2014 otherwise disabled CTAs still look clickable.\n const ctaClass = 'text-brand-violet hover:text-brand-violet/80 hover:bg-transparent px-0 disabled:text-muted-foreground/50 disabled:hover:text-muted-foreground/50'\n\n return (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <AndOrToggle\n combinator={group.combinator}\n onChange={(c) => dispatch({ type: 'updateGroupCombinator', groupId: group.id, combinator: c })}\n t={t}\n />\n <Button\n ref={addBtnRef}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={!addRuleEnabled}\n title={addRuleTitle}\n onClick={() => setPickerOpen(true)}\n className={ctaClass}\n >\n <Plus className=\"size-4\" />\n {t('ui.advancedFilter.addCondition', '+ Add condition')}\n </Button>\n <FilterFieldPicker\n fields={fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(f) =>\n dispatch({\n type: 'addRule',\n groupId: group.id,\n defaultField: f.key,\n defaultOperator: getDefaultOperator(f.type),\n })\n }\n triggerRef={addBtnRef as React.RefObject<HTMLElement | null>}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={!addGroupEnabled}\n title={addGroupTitle}\n onClick={() =>\n dispatch({ type: 'addGroup', groupId: group.id, defaultField })\n }\n className={ctaClass}\n >\n <Plus className=\"size-4\" />\n {addGroupLabel}\n </Button>\n {!isRoot ? (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => dispatch({ type: 'removeNode', nodeId: group.id })}\n aria-label={t('ui.advancedFilter.deleteGroup', 'Delete group')}\n title={t('ui.advancedFilter.deleteGroup', 'Delete group')}\n className=\"ml-auto\"\n >\n <X className=\"size-4 text-muted-foreground\" />\n </IconButton>\n ) : null}\n </div>\n {!addGroupEnabled && depthCapHit ? (\n <p className=\"text-xs text-muted-foreground\" data-testid=\"filter-depth-limit-hint\">\n {t('ui.advancedFilter.limitDepthHint', 'You\\'ve reached the maximum nesting depth ({max} levels). Switch this group\\'s combinator with the toggle above instead of nesting deeper.', {\n max: TREE_LIMITS.maxGroupLevel,\n })}\n </p>\n ) : null}\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { Plus, Trash2, X, GripVertical, ChevronDown } from 'lucide-react'\nimport {\n DndContext,\n DragOverlay,\n PointerSensor,\n KeyboardSensor,\n useSensor,\n useSensors,\n closestCenter,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core'\nimport {\n SortableContext,\n sortableKeyboardCoordinates,\n useSortable,\n verticalListSortingStrategy,\n} from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\nimport { Button } from '../../primitives/button'\nimport { IconButton } from '../../primitives/icon-button'\nimport { Input } from '../../primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../../primitives/select'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover'\nimport { Checkbox } from '../../primitives/checkbox'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n FilterFieldDef,\n FilterFieldType,\n FilterOperator,\n} from '@open-mercato/shared/lib/query/advanced-filter'\nimport {\n OPERATORS_BY_FIELD_TYPE,\n getDefaultOperator,\n isValuelessOperator,\n} from '@open-mercato/shared/lib/query/advanced-filter'\nimport {\n type AdvancedFilterTree,\n type FilterRule,\n type FilterGroup,\n type FilterCombinator,\n TREE_LIMITS,\n} from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport { treeReducer, canAddRule, canAddGroup, type TreeAction } from './treeReducer'\nimport { FilterFieldPicker } from './FilterFieldPicker'\n\nexport type AdvancedFilterBuilderProps = {\n fields: FilterFieldDef[]\n value: AdvancedFilterTree\n onChange: (state: AdvancedFilterTree) => void\n onApply: () => void\n onClear: () => void\n pendingErrors?: Array<{ ruleId: string; messageKey: string; message: string }>\n}\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n is: 'is', is_not: 'is not', contains: 'contains', does_not_contain: 'does not contain',\n starts_with: 'starts with', ends_with: 'ends with', is_empty: 'is empty', is_not_empty: 'is not empty',\n equals: 'equals', not_equals: 'not equals', greater_than: 'greater than', less_than: 'less than',\n greater_or_equal: 'greater or equal', less_or_equal: 'less or equal', between: 'between',\n is_before: 'is before', is_after: 'is after', is_any_of: 'is any of', is_none_of: 'is none of',\n is_true: 'is true', is_false: 'is false', has_any_of: 'has any of', has_all_of: 'has all of', has_none_of: 'has none of',\n}\n\nfunction getFieldType(fields: FilterFieldDef[], fieldKey: string): FilterFieldType {\n return fields.find((f) => f.key === fieldKey)?.type ?? 'text'\n}\n\n// Operator value shapes. Used to decide whether changing operator preserves the\n// current value: same shape preserves (`is` \u2192 `is not` keeps \"Active\"); different\n// shape resets (`is` \u2192 `between` clears, because value goes from scalar to pair).\ntype OperatorShape = 'valueless' | 'multi' | 'range' | 'single'\n\nconst MULTI_VALUE_OPS = new Set<FilterOperator>([\n 'is_any_of', 'is_none_of', 'has_any_of', 'has_all_of', 'has_none_of',\n])\n\nfunction getOperatorShape(op: FilterOperator): OperatorShape {\n if (isValuelessOperator(op)) return 'valueless'\n if (MULTI_VALUE_OPS.has(op)) return 'multi'\n if (op === 'between') return 'range'\n return 'single'\n}\n\ntype DragHandleProps = React.HTMLAttributes<HTMLButtonElement> & {\n ref?: React.Ref<HTMLButtonElement>\n}\n\ntype Translator = ReturnType<typeof useT>\n\nexport function AdvancedFilterBuilder({\n fields, value, onChange, onApply, onClear, pendingErrors,\n}: AdvancedFilterBuilderProps) {\n const t = useT()\n\n const dispatch = React.useCallback((action: TreeAction) => {\n onChange(treeReducer(value, action))\n }, [onChange, value])\n\n React.useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n onApply()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [onApply])\n\n const errorByRuleId = React.useMemo(() => {\n const m = new Map<string, string>()\n for (const e of pendingErrors ?? []) m.set(e.ruleId, t(e.messageKey, e.message))\n return m\n }, [pendingErrors, t])\n\n const empty = value.root.children.length === 0\n const defaultField = fields.length > 0 ? fields[0].key : undefined\n\n // Per-group SortableContexts scope drag-and-drop to within-group ordering.\n // The DragOverlay below renders a stable preview that\n // matches the original row's footprint \u2014 without it, dnd-kit applies\n // `transform` to the source row in place, which the wrapping flex layout\n // distorted into a \"tall\" / morphed ghost.\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),\n )\n const [activeDrag, setActiveDrag] = React.useState<{ node: FilterRule | FilterGroup; parent: FilterGroup } | null>(null)\n const findNode = React.useCallback((id: string) => locateInTree(value.root, id), [value])\n\n const handleDragStart = React.useCallback((e: DragStartEvent) => {\n const found = findNode(String(e.active.id))\n if (found) setActiveDrag(found)\n }, [findNode])\n\n const handleDragEnd = React.useCallback((e: DragEndEvent) => {\n setActiveDrag(null)\n const { active, over } = e\n if (!over || active.id === over.id) return\n const from = findNode(String(active.id))\n const to = findNode(String(over.id))\n if (!from || !to) return\n if (from.parent.id !== to.parent.id) return\n const fromIdx = from.parent.children.findIndex((c) => c.id === active.id)\n const toIdx = to.parent.children.findIndex((c) => c.id === over.id)\n if (fromIdx < 0 || toIdx < 0) return\n dispatch({ type: 'reorderChildren', groupId: from.parent.id, fromIdx, toIdx })\n }, [dispatch, findNode])\n\n const handleDragCancel = React.useCallback(() => setActiveDrag(null), [])\n\n // Wrapper has no `min-w-[640px]`: it forced horizontal scroll on a 375px viewport.\n // Rules wrap onto multiple lines on mobile; on desktop (`sm:max-w-[720px]`) they fit\n // on one line.\n return (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n <div className=\"flex flex-col gap-3 p-3\">\n {!empty ? (\n <GroupView\n group={value.root}\n level={1}\n fields={fields}\n tree={value}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n ) : null}\n {!empty ? (\n <div className=\"flex items-center gap-3 pt-2 border-t border-border\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto px-0 py-0 text-base text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={onClear}\n >\n <X className=\"size-4\" />\n {t('ui.advancedFilter.clearAll', 'Clear all')}\n </Button>\n </div>\n ) : null}\n </div>\n {/* Portal DragOverlay to document.body. Radix DialogContent uses CSS `transform`\n for centering, which becomes the containing block for `position: fixed`\n descendants (dnd-kit's overlay) and shifts the ghost off-screen. Portaling\n restores viewport-relative coordinates. */}\n {typeof document !== 'undefined'\n ? createPortal(\n // dnd-kit's DragOverlay sets its own inline z-index, so a Tailwind\n // utility class would be overridden \u2014 read the DS token via CSS var.\n <DragOverlay dropAnimation={null} style={{ zIndex: 'var(--z-index-modal-elevated, 55)' }}>\n {activeDrag ? (\n <DragGhost node={activeDrag.node} parent={activeDrag.parent} fields={fields} t={t} />\n ) : null}\n </DragOverlay>,\n document.body,\n )\n : null}\n </DndContext>\n )\n}\n\n/** Find a node in the tree along with its parent group. Used by drag handlers. */\nfunction locateInTree(root: FilterGroup, id: string): { node: FilterRule | FilterGroup; parent: FilterGroup } | null {\n for (const child of root.children) {\n if (child.id === id) return { node: child, parent: root }\n if (child.type === 'group') {\n const found = locateInTree(child, id)\n if (found) return found\n }\n }\n return null\n}\n\n// Compact drag preview rendered inside DragOverlay. Cannot reuse RuleRow/GroupView \u2014\n// their popovers and form inputs conflict with the overlay.\nfunction DragGhost({\n node, parent, fields, t,\n}: {\n node: FilterRule | FilterGroup\n parent: FilterGroup\n fields: FilterFieldDef[]\n t: Translator\n}) {\n const isGroup = node.type === 'group'\n const labelParts: string[] = []\n if (isGroup) {\n const ruleCount = countRulesIn(node)\n labelParts.push(t('ui.advancedFilter.dragGhost.group', 'Group'))\n labelParts.push(`(${ruleCount})`)\n } else {\n const def = fields.find((f) => f.key === node.field)\n labelParts.push(def?.label ?? (node.field || t('ui.advancedFilter.dragGhost.unsetField', 'Unset field')))\n labelParts.push(OPERATOR_LABELS[node.operator] ?? node.operator)\n if (typeof node.value === 'string' && node.value) labelParts.push(`\"${node.value}\"`)\n }\n return (\n <div className=\"inline-flex items-center gap-2 rounded-md border border-border bg-popover px-3 py-1.5 text-sm shadow-md cursor-grabbing\">\n <GripVertical className=\"size-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">{parent.combinator === 'and' ? t('ui.advancedFilter.connector.and', 'and') : t('ui.advancedFilter.connector.or', 'or')}</span>\n <span className=\"font-medium\">{labelParts.join(' ')}</span>\n </div>\n )\n}\n\nfunction countRulesIn(group: FilterGroup): number {\n let n = 0\n for (const c of group.children) {\n if (c.type === 'rule') n += 1\n else n += countRulesIn(c)\n }\n return n\n}\n\nfunction GroupView({\n group, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n group: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n const isRoot = level === 1\n // Sub-groups render as a card so the nesting boundary is visible; root group stays\n // flush with the popover so it doesn't double-up the dialog's own card chrome.\n const containerClass = isRoot\n ? 'space-y-3'\n : 'space-y-3 rounded-lg border border-border bg-muted/30 p-3'\n\n // Toolbar-first layout (Kendo React Filter pattern): one [And]/[Or] toggle per group\n // is the only combinator UI. Mixing AND with OR requires `Add subgroup`. Matches\n // `CompositeFilterDescriptor` semantics (one logic per group).\n return (\n <div className={containerClass}>\n <GroupToolbar\n group={group}\n level={level}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n fields={fields}\n isRoot={isRoot}\n t={t}\n />\n\n <SortableChildrenList\n group={group}\n level={level}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n </div>\n )\n}\n\n// Kendo-style segmented `[And] [Or]` toggle. Clicking the unselected side flips\n// the group's combinator. Selected side is brand-violet filled so the affordance\n// is unambiguous.\nfunction AndOrToggle({\n combinator, onChange, t,\n}: {\n combinator: FilterCombinator\n onChange: (c: FilterCombinator) => void\n t: Translator\n}) {\n const andLabel = t('ui.advancedFilter.combinator.and', 'And')\n const orLabel = t('ui.advancedFilter.combinator.or', 'Or')\n const baseBtn = 'h-8 px-3 text-sm font-medium outline-none focus-visible:shadow-focus disabled:cursor-not-allowed'\n const selBtn = 'bg-brand-violet text-white hover:bg-brand-violet/90'\n const unselBtn = 'bg-background text-foreground hover:bg-accent'\n return (\n <div\n role=\"group\"\n aria-label={t('ui.advancedFilter.combinator.label', 'Group combinator')}\n className=\"inline-flex rounded-md border border-input overflow-hidden shrink-0\"\n data-testid=\"advanced-filter-combinator-toggle\"\n data-combinator={combinator}\n >\n <button\n type=\"button\"\n aria-pressed={combinator === 'and'}\n onClick={() => combinator !== 'and' && onChange('and')}\n className={`${baseBtn} ${combinator === 'and' ? selBtn : unselBtn}`}\n >\n {andLabel}\n </button>\n <button\n type=\"button\"\n aria-pressed={combinator === 'or'}\n onClick={() => combinator !== 'or' && onChange('or')}\n className={`${baseBtn} border-l border-input ${combinator === 'or' ? selBtn : unselBtn}`}\n >\n {orLabel}\n </button>\n </div>\n )\n}\n\nfunction SortableChildrenList({\n group, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n group: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n // DndContext + handlers live one level up so a drag can move children across\n // groups. Each group owns its own SortableContext to scope sort ordering.\n return (\n <SortableContext items={group.children.map((c) => c.id)} strategy={verticalListSortingStrategy}>\n {group.children.map((child, idx) => (\n <SortableRow\n key={child.id}\n child={child}\n idx={idx}\n parent={group}\n level={level}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n ))}\n </SortableContext>\n )\n}\n\nfunction SortableRow({\n child, idx, parent, level, fields, tree, dispatch, defaultField, errorByRuleId, t,\n}: {\n child: FilterRule | FilterGroup\n idx: number\n parent: FilterGroup\n level: number\n fields: FilterFieldDef[]\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n errorByRuleId: Map<string, string>\n t: Translator\n}) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id: child.id,\n })\n // Hide the source while dragging (DragOverlay renders the snapshot); keep it in\n // layout via `visibility: hidden` so siblings don't jump. Without this, the flex\n // wrapper applies both `transform` and its own resizing to the source row.\n const style: React.CSSProperties = isDragging\n ? { visibility: 'hidden' as const }\n : { transform: CSS.Transform.toString(transform), transition }\n const dragHandleProps: DragHandleProps = { ...attributes, ...listeners }\n\n return (\n <div ref={setNodeRef} style={style} data-testid=\"filter-rule-row\" data-filter-node-id={child.id}>\n {child.type === 'rule' ? (\n <RuleRow\n rule={child}\n fields={fields}\n dispatch={dispatch}\n errorByRuleId={errorByRuleId}\n dragHandleProps={dragHandleProps}\n t={t}\n />\n ) : (\n <div className=\"flex items-start gap-2\">\n {/* Raw <button> required: dnd-kit spreads `attributes`+`listeners` onto the\n activator; <Button>'s own props/ref handling breaks the keyboard sensor. */}\n <button\n type=\"button\"\n {...dragHandleProps}\n data-testid=\"filter-drag-handle\"\n className=\"mt-1 text-muted-foreground/60 hover:text-muted-foreground cursor-grab outline-none focus-visible:shadow-focus rounded-sm\"\n aria-label={t('ui.advancedFilter.dragHandle', 'Drag to reorder')}\n >\n <GripVertical className=\"size-4\" />\n </button>\n <div className=\"flex-1\">\n <GroupView\n group={child}\n level={level + 1}\n fields={fields}\n tree={tree}\n dispatch={dispatch}\n defaultField={defaultField}\n errorByRuleId={errorByRuleId}\n t={t}\n />\n </div>\n </div>\n )}\n </div>\n )\n}\n\nfunction RuleRow({\n rule, fields, dispatch, errorByRuleId, dragHandleProps, t,\n}: {\n rule: FilterRule\n fields: FilterFieldDef[]\n dispatch: (a: TreeAction) => void\n errorByRuleId: Map<string, string>\n dragHandleProps: DragHandleProps\n t: Translator\n}) {\n const fieldType = getFieldType(fields, rule.field)\n const operators = OPERATORS_BY_FIELD_TYPE[fieldType] ?? OPERATORS_BY_FIELD_TYPE.text\n const valueless = isValuelessOperator(rule.operator)\n const errorMsg = errorByRuleId.get(rule.id)\n const fieldDef = fields.find((f) => f.key === rule.field)\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const fieldBtnRef = React.useRef<HTMLButtonElement>(null)\n\n return (\n <div className=\"flex flex-col gap-1\" data-testid=\"filter-rule\" data-filter-rule-id={rule.id}>\n <div className=\"flex flex-wrap items-start gap-2\">\n {/* Raw <button> required for dnd-kit activator: see SortableRow drag-handle comment. */}\n <button\n type=\"button\"\n {...dragHandleProps}\n data-testid=\"filter-drag-handle\"\n className=\"mt-1 text-muted-foreground/60 hover:text-muted-foreground cursor-grab outline-none focus-visible:shadow-focus rounded-sm\"\n aria-label={t('ui.advancedFilter.dragHandle', 'Drag to reorder')}\n >\n <GripVertical className=\"size-4\" />\n </button>\n <Button\n ref={fieldBtnRef}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPickerOpen(true)}\n className=\"min-w-[140px] justify-start\"\n >\n {fieldDef?.label ?? t('ui.advancedFilter.selectField', 'Select field')}\n </Button>\n <FilterFieldPicker\n fields={fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(f) => {\n const nextType = getFieldType(fields, f.key)\n dispatch({\n type: 'updateRule',\n ruleId: rule.id,\n updates: { field: f.key, operator: getDefaultOperator(nextType), value: '' },\n })\n }}\n triggerRef={fieldBtnRef as React.RefObject<HTMLElement | null>}\n />\n <Select\n value={rule.operator}\n onValueChange={(next) => {\n // Preserve the value when the operator shape doesn't change; reset\n // only when shape changes (e.g. `is` \u2192 `between` clears scalar to pair).\n const nextOp = next as FilterOperator\n const sameShape = getOperatorShape(rule.operator) === getOperatorShape(nextOp)\n const updates: Partial<Pick<FilterRule, 'operator' | 'value'>> = sameShape\n ? { operator: nextOp }\n : { operator: nextOp, value: getOperatorShape(nextOp) === 'multi' || getOperatorShape(nextOp) === 'range' ? [] : '' }\n dispatch({ type: 'updateRule', ruleId: rule.id, updates })\n }}\n >\n {/* `w-auto` overrides SelectTrigger's default `w-full` so it sits\n inline with the field/value inputs instead of breaking onto a new row. */}\n <SelectTrigger\n size=\"sm\"\n className=\"w-auto min-w-[140px]\"\n aria-label={t('ui.advancedFilter.selectOperator', 'Select operator')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent data-advanced-filter-portal=\"\">\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {t(`ui.advancedFilter.operator.${op}`, OPERATOR_LABELS[op])}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {!valueless ? (\n <ValueInput\n rule={rule}\n fields={fields}\n fieldType={fieldType}\n dispatch={dispatch}\n hasError={!!errorMsg}\n t={t}\n />\n ) : null}\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => dispatch({ type: 'removeNode', nodeId: rule.id })}\n aria-label={t('ui.advancedFilter.removeCondition', 'Remove condition')}\n title={t('ui.advancedFilter.removeCondition', 'Remove condition')}\n >\n <Trash2 className=\"size-4 text-muted-foreground\" />\n </IconButton>\n </div>\n {errorMsg ? (\n <div className=\"ml-6 pl-12 text-xs text-status-error-text\">{errorMsg}</div>\n ) : null}\n </div>\n )\n}\n\nfunction ValueInput({\n rule, fields, fieldType, dispatch, hasError, t,\n}: {\n rule: FilterRule\n fields: FilterFieldDef[]\n fieldType: FilterFieldType\n dispatch: (a: TreeAction) => void\n hasError: boolean\n t: Translator\n}) {\n const fieldDef = fields.find((f) => f.key === rule.field)\n const value = rule.value\n const errorClass = hasError\n ? 'border-status-error-border ring-1 ring-status-error-border focus-visible:border-status-error-border focus-within:border-status-error-border'\n : ''\n\n if (fieldType === 'select' && fieldDef?.options) {\n // Multi-value operators (`is_any_of`, `is_none_of`, `has_any_of`, etc.)\n // need an array value with multiple selectable options. Radix `Select` is\n // single-select only, so the previous code silently overwrote each\n // selection (and validation kept failing because the value never became an\n // array). Render a checkbox-list popover when the operator is multi-shape.\n if (getOperatorShape(rule.operator) === 'multi') {\n return (\n <MultiValuePicker\n rule={rule}\n options={fieldDef.options}\n dispatch={dispatch}\n errorClass={errorClass}\n t={t}\n />\n )\n }\n return (\n <Select\n value={typeof value === 'string' && value.length ? value : undefined}\n onValueChange={(next) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: next ?? '' } })\n }\n >\n <SelectTrigger\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n aria-label={t('ui.advancedFilter.selectValue', 'Select value')}\n >\n <SelectValue placeholder={t('ui.advancedFilter.selectValuePlaceholder', 'Select...')} />\n </SelectTrigger>\n <SelectContent data-advanced-filter-portal=\"\">\n {fieldDef.options.map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.tone ? (\n <span className=\"inline-flex items-center gap-2\">\n <span className={toneDotClass(opt.tone)} aria-hidden=\"true\" />\n {opt.label}\n </span>\n ) : (\n opt.label\n )}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n }\n\n // `Input` ships `w-full` by default \u2014 use `w-auto` so inputs don't stretch\n // inside the wrapping flex row. `between` needs a `[start, end]` pair, so it\n // gets its own component below.\n if (rule.operator === 'between' && (fieldType === 'number' || fieldType === 'date')) {\n return (\n <BetweenInput\n rule={rule}\n fieldType={fieldType}\n dispatch={dispatch}\n errorClass={errorClass}\n t={t}\n />\n )\n }\n\n if (fieldType === 'date') {\n return (\n <Input\n type=\"date\"\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n aria-label={t('ui.advancedFilter.dateValue', 'Date value')}\n />\n )\n }\n\n if (fieldType === 'number') {\n return (\n <Input\n type=\"number\"\n size=\"sm\"\n className={`w-[120px] ${errorClass}`}\n value={typeof value === 'number' ? value : typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n placeholder={t('ui.advancedFilter.numberPlaceholder', 'Value')}\n aria-label={t('ui.advancedFilter.numberValue', 'Number value')}\n />\n )\n }\n\n return (\n <Input\n type=\"text\"\n size=\"sm\"\n className={`w-auto min-w-[160px] ${errorClass}`}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) =>\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: e.target.value } })\n }\n placeholder={t('ui.advancedFilter.textPlaceholder', 'Value...')}\n aria-label={t('ui.advancedFilter.textValue', 'Text value')}\n />\n )\n}\n\n/**\n * Two inputs (start / end) for the `between` operator on date and number\n * fields. Stores the value as `[start, end]`; either side can be empty while\n * the user types, but validation requires both to be set before apply.\n */\nfunction BetweenInput({\n rule, fieldType, dispatch, errorClass, t,\n}: {\n rule: FilterRule\n fieldType: FilterFieldType\n dispatch: (a: TreeAction) => void\n errorClass: string\n t: Translator\n}) {\n const pair = Array.isArray(rule.value) ? rule.value : []\n const start = typeof pair[0] === 'string' || typeof pair[0] === 'number' ? String(pair[0] ?? '') : ''\n const end = typeof pair[1] === 'string' || typeof pair[1] === 'number' ? String(pair[1] ?? '') : ''\n const update = (which: 'start' | 'end', next: string) => {\n const value = which === 'start' ? [next, end] : [start, next]\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value } })\n }\n const inputType = fieldType === 'date' ? 'date' : 'number'\n const inputWidth = fieldType === 'date' ? 'w-auto min-w-[140px]' : 'w-[110px]'\n return (\n <div className=\"inline-flex items-center gap-1\">\n <Input\n type={inputType}\n size=\"sm\"\n className={`${inputWidth} ${errorClass}`}\n value={start}\n onChange={(e) => update('start', e.target.value)}\n placeholder={fieldType === 'number' ? t('ui.advancedFilter.numberPlaceholder', 'Value') : undefined}\n aria-label={t('ui.advancedFilter.betweenStart', 'Start')}\n />\n <span className=\"text-xs text-muted-foreground\">{t('ui.advancedFilter.and', 'and')}</span>\n <Input\n type={inputType}\n size=\"sm\"\n className={`${inputWidth} ${errorClass}`}\n value={end}\n onChange={(e) => update('end', e.target.value)}\n placeholder={fieldType === 'number' ? t('ui.advancedFilter.numberPlaceholder', 'Value') : undefined}\n aria-label={t('ui.advancedFilter.betweenEnd', 'End')}\n />\n </div>\n )\n}\n\n/**\n * Trigger + popover with a checkbox list. Used when a select-typed rule has a\n * multi-shape operator (`is_any_of`, `has_all_of`, etc.). The trigger displays\n * a \"{first} +N\" summary; the popover lets the user toggle each option.\n */\nfunction MultiValuePicker({\n rule, options, dispatch, errorClass, t,\n}: {\n rule: FilterRule\n options: NonNullable<FilterFieldDef['options']>\n dispatch: (a: TreeAction) => void\n errorClass: string\n t: Translator\n}) {\n const selected: string[] = Array.isArray(rule.value)\n ? rule.value.map((v) => String(v)).filter((v) => v.length > 0)\n : []\n const selectedLabel = (() => {\n if (selected.length === 0) return t('ui.advancedFilter.selectValuePlaceholder', 'Select...')\n const firstOpt = options.find((o) => o.value === selected[0])\n const head = firstOpt?.label ?? selected[0]\n const more = selected.length - 1\n return more > 0 ? `${head} +${more}` : head\n })()\n const toggle = (val: string) => {\n const next = selected.includes(val) ? selected.filter((v) => v !== val) : [...selected, val]\n dispatch({ type: 'updateRule', ruleId: rule.id, updates: { value: next } })\n }\n return (\n <Popover>\n <PopoverTrigger asChild>\n {/* Visually mirrors `SelectTrigger size=\"sm\"` (h-8, rounded-md, border)\n so the operator + value pair stays visually consistent. */}\n <button\n type=\"button\"\n className={`inline-flex h-8 items-center justify-between gap-2 rounded-md border border-input bg-background px-2.5 text-xs shadow-xs transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground w-auto min-w-[160px] ${errorClass}`}\n aria-label={t('ui.advancedFilter.selectValue', 'Select value')}\n data-testid=\"multi-value-trigger\"\n >\n <span className={selected.length === 0 ? 'text-muted-foreground' : ''}>{selectedLabel}</span>\n <ChevronDown className=\"size-3 text-muted-foreground\" />\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-1\" align=\"start\" data-advanced-filter-portal=\"\">\n <div className=\"max-h-64 overflow-y-auto\" role=\"listbox\" aria-multiselectable=\"true\">\n {options.map((opt) => {\n const isOn = selected.includes(opt.value)\n return (\n // Raw <button>: needs `role=\"option\"` + `aria-selected`; <Button> would override role.\n <button\n key={opt.value}\n type=\"button\"\n role=\"option\"\n aria-selected={isOn}\n onClick={() => toggle(opt.value)}\n className=\"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-accent focus-visible:bg-accent focus-visible:outline-none\"\n >\n <Checkbox checked={isOn} aria-hidden=\"true\" tabIndex={-1} className=\"pointer-events-none\" />\n {opt.tone ? (\n <span className=\"inline-flex items-center gap-2\">\n <span className={toneDotClass(opt.tone)} aria-hidden=\"true\" />\n {opt.label}\n </span>\n ) : (\n <span>{opt.label}</span>\n )}\n </button>\n )\n })}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\nfunction toneDotClass(tone: NonNullable<FilterFieldDef['options']>[number]['tone']): string {\n // Solid filled dot using the saturated `*-icon` token (matches Tag's `dotColorMap`).\n const base = 'inline-block size-2 rounded-full shrink-0'\n switch (tone) {\n case 'success': return `${base} bg-status-success-icon`\n case 'error': return `${base} bg-status-error-icon`\n case 'warning': return `${base} bg-status-warning-icon`\n case 'info': return `${base} bg-status-info-icon`\n case 'neutral': return `${base} bg-status-neutral-icon`\n case 'pink': return `${base} bg-status-pink-icon`\n case 'brand': return `${base} bg-brand-violet`\n default: return `${base} bg-muted-foreground/40`\n }\n}\n\n// Single Kendo-style toolbar per group: combinator toggle, `Add condition`,\n// `Add subgroup` / `Add group`, plus a trailing delete for non-root groups.\nfunction GroupToolbar({\n group, level, tree, dispatch, defaultField, fields, isRoot, t,\n}: {\n group: FilterGroup\n level: number\n tree: AdvancedFilterTree\n dispatch: (a: TreeAction) => void\n defaultField?: string\n fields: FilterFieldDef[]\n isRoot: boolean\n t: Translator\n}) {\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const addBtnRef = React.useRef<HTMLButtonElement>(null)\n const addRuleEnabled = canAddRule(tree, group.id)\n const addGroupEnabled = canAddGroup(tree, group.id)\n\n const widthCapHit = group.children.length >= TREE_LIMITS.maxChildrenPerGroup\n const depthCapHit = level >= TREE_LIMITS.maxGroupLevel\n\n const addRuleTitle = !addRuleEnabled\n ? widthCapHit\n ? t('ui.advancedFilter.limitWidthReached', 'Maximum {max} conditions per group', {\n max: TREE_LIMITS.maxChildrenPerGroup,\n })\n : t('ui.advancedFilter.limitTotalReached', 'Maximum {max} conditions reached', {\n max: TREE_LIMITS.maxTotalRules,\n })\n : undefined\n\n const addGroupTitle = !addGroupEnabled\n ? depthCapHit\n ? t('ui.advancedFilter.limitDepthReached', 'Maximum nesting depth reached')\n : widthCapHit\n ? t('ui.advancedFilter.limitWidthReached', 'Maximum {max} conditions per group', {\n max: TREE_LIMITS.maxChildrenPerGroup,\n })\n : t('ui.advancedFilter.limitTotalReached', 'Maximum {max} conditions reached', {\n max: TREE_LIMITS.maxTotalRules,\n })\n : undefined\n\n const addGroupLabel = level === 1\n ? t('ui.advancedFilter.addGroup', 'Add group')\n : t('ui.advancedFilter.addSubgroup', 'Add subgroup')\n\n // `text-brand-violet` overrides Button's default disabled token, so we have to\n // re-state muted disabled colors here \u2014 otherwise disabled CTAs still look clickable.\n const ctaClass = 'text-brand-violet hover:text-brand-violet/80 hover:bg-transparent px-0 disabled:text-muted-foreground/50 disabled:hover:text-muted-foreground/50'\n\n return (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <AndOrToggle\n combinator={group.combinator}\n onChange={(c) => dispatch({ type: 'updateGroupCombinator', groupId: group.id, combinator: c })}\n t={t}\n />\n <Button\n ref={addBtnRef}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={!addRuleEnabled}\n title={addRuleTitle}\n onClick={() => setPickerOpen(true)}\n className={ctaClass}\n >\n <Plus className=\"size-4\" />\n {t('ui.advancedFilter.addCondition', 'Add condition')}\n </Button>\n <FilterFieldPicker\n fields={fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(f) =>\n dispatch({\n type: 'addRule',\n groupId: group.id,\n defaultField: f.key,\n defaultOperator: getDefaultOperator(f.type),\n })\n }\n triggerRef={addBtnRef as React.RefObject<HTMLElement | null>}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={!addGroupEnabled}\n title={addGroupTitle}\n onClick={() =>\n dispatch({ type: 'addGroup', groupId: group.id, defaultField })\n }\n className={ctaClass}\n >\n <Plus className=\"size-4\" />\n {addGroupLabel}\n </Button>\n {!isRoot ? (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => dispatch({ type: 'removeNode', nodeId: group.id })}\n aria-label={t('ui.advancedFilter.deleteGroup', 'Delete group')}\n title={t('ui.advancedFilter.deleteGroup', 'Delete group')}\n className=\"ml-auto\"\n >\n <X className=\"size-4 text-muted-foreground\" />\n </IconButton>\n ) : null}\n </div>\n {!addGroupEnabled && depthCapHit ? (\n <p className=\"text-xs text-muted-foreground\" data-testid=\"filter-depth-limit-hint\">\n {t('ui.advancedFilter.limitDepthHint', 'You\\'ve reached the maximum nesting depth ({max} levels). Switch this group\\'s combinator with the toggle above instead of nesting deeper.', {\n max: TREE_LIMITS.maxGroupLevel,\n })}\n </p>\n ) : null}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -13,8 +13,8 @@ function ComponentOverrideProvider({
13
13
  children
14
14
  }) {
15
15
  const [userFeatures, setUserFeatures] = React.useState([]);
16
+ registerComponentOverrides(overrides);
16
17
  React.useEffect(() => {
17
- registerComponentOverrides(overrides);
18
18
  return () => {
19
19
  registerComponentOverrides([]);
20
20
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/injection/ComponentOverrideProvider.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { createContext, useContext } from 'react'\nimport type { ComponentOverride } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { registerComponentOverrides } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { apiCall } from '../utils/apiCall'\n\ntype FeatureCheckResponse = { granted: string[] }\n\nconst OverrideUserFeaturesContext = createContext<readonly string[]>([])\n\nexport function useOverrideUserFeatures(): readonly string[] {\n return useContext(OverrideUserFeaturesContext)\n}\n\nexport function ComponentOverrideProvider({\n overrides,\n children,\n}: {\n overrides: ComponentOverride[]\n children: React.ReactNode\n}) {\n const [userFeatures, setUserFeatures] = React.useState<readonly string[]>([])\n\n React.useEffect(() => {\n registerComponentOverrides(overrides)\n return () => {\n registerComponentOverrides([])\n }\n }, [overrides])\n\n React.useEffect(() => {\n const requiredFeatures = new Set<string>()\n for (const override of overrides) {\n for (const feature of override.features ?? []) {\n if (feature && feature.trim().length > 0) requiredFeatures.add(feature)\n }\n }\n if (requiredFeatures.size === 0) return\n let cancelled = false\n const fetchFeatures = async () => {\n const call = await apiCall<FeatureCheckResponse>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: Array.from(requiredFeatures) }),\n })\n if (cancelled) return\n if (call.ok && call.result?.granted) {\n setUserFeatures(call.result.granted)\n }\n }\n void fetchFeatures()\n return () => {\n cancelled = true\n }\n }, [overrides])\n\n return (\n <OverrideUserFeaturesContext.Provider value={userFeatures}>\n {children}\n </OverrideUserFeaturesContext.Provider>\n )\n}\n\nexport default ComponentOverrideProvider\n"],
5
- "mappings": ";AA2DI;AAzDJ,YAAY,WAAW;AACvB,SAAS,eAAe,kBAAkB;AAE1C,SAAS,kCAAkC;AAC3C,SAAS,eAAe;AAIxB,MAAM,8BAA8B,cAAiC,CAAC,CAAC;AAEhE,SAAS,0BAA6C;AAC3D,SAAO,WAAW,2BAA2B;AAC/C;AAEO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAGG;AACD,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAA4B,CAAC,CAAC;AAE5E,QAAM,UAAU,MAAM;AACpB,+BAA2B,SAAS;AACpC,WAAO,MAAM;AACX,iCAA2B,CAAC,CAAC;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,UAAU,MAAM;AACpB,UAAM,mBAAmB,oBAAI,IAAY;AACzC,eAAW,YAAY,WAAW;AAChC,iBAAW,WAAW,SAAS,YAAY,CAAC,GAAG;AAC7C,YAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,EAAG,kBAAiB,IAAI,OAAO;AAAA,MACxE;AAAA,IACF;AACA,QAAI,iBAAiB,SAAS,EAAG;AACjC,QAAI,YAAY;AAChB,UAAM,gBAAgB,YAAY;AAChC,YAAM,OAAO,MAAM,QAA8B,2BAA2B;AAAA,QAC1E,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,MAAM,KAAK,gBAAgB,EAAE,CAAC;AAAA,MACjE,CAAC;AACD,UAAI,UAAW;AACf,UAAI,KAAK,MAAM,KAAK,QAAQ,SAAS;AACnC,wBAAgB,KAAK,OAAO,OAAO;AAAA,MACrC;AAAA,IACF;AACA,SAAK,cAAc;AACnB,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,SACE,oBAAC,4BAA4B,UAA5B,EAAqC,OAAO,cAC1C,UACH;AAEJ;AAEA,IAAO,oCAAQ;",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { createContext, useContext } from 'react'\nimport type { ComponentOverride } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { registerComponentOverrides } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { apiCall } from '../utils/apiCall'\n\ntype FeatureCheckResponse = { granted: string[] }\n\nconst OverrideUserFeaturesContext = createContext<readonly string[]>([])\n\nexport function useOverrideUserFeatures(): readonly string[] {\n return useContext(OverrideUserFeaturesContext)\n}\n\nexport function ComponentOverrideProvider({\n overrides,\n children,\n}: {\n overrides: ComponentOverride[]\n children: React.ReactNode\n}) {\n const [userFeatures, setUserFeatures] = React.useState<readonly string[]>([])\n\n registerComponentOverrides(overrides)\n\n React.useEffect(() => {\n return () => {\n registerComponentOverrides([])\n }\n }, [overrides])\n\n React.useEffect(() => {\n const requiredFeatures = new Set<string>()\n for (const override of overrides) {\n for (const feature of override.features ?? []) {\n if (feature && feature.trim().length > 0) requiredFeatures.add(feature)\n }\n }\n if (requiredFeatures.size === 0) return\n let cancelled = false\n const fetchFeatures = async () => {\n const call = await apiCall<FeatureCheckResponse>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: Array.from(requiredFeatures) }),\n })\n if (cancelled) return\n if (call.ok && call.result?.granted) {\n setUserFeatures(call.result.granted)\n }\n }\n void fetchFeatures()\n return () => {\n cancelled = true\n }\n }, [overrides])\n\n return (\n <OverrideUserFeaturesContext.Provider value={userFeatures}>\n {children}\n </OverrideUserFeaturesContext.Provider>\n )\n}\n\nexport default ComponentOverrideProvider\n"],
5
+ "mappings": ";AA4DI;AA1DJ,YAAY,WAAW;AACvB,SAAS,eAAe,kBAAkB;AAE1C,SAAS,kCAAkC;AAC3C,SAAS,eAAe;AAIxB,MAAM,8BAA8B,cAAiC,CAAC,CAAC;AAEhE,SAAS,0BAA6C;AAC3D,SAAO,WAAW,2BAA2B;AAC/C;AAEO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAGG;AACD,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAA4B,CAAC,CAAC;AAE5E,6BAA2B,SAAS;AAEpC,QAAM,UAAU,MAAM;AACpB,WAAO,MAAM;AACX,iCAA2B,CAAC,CAAC;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,UAAU,MAAM;AACpB,UAAM,mBAAmB,oBAAI,IAAY;AACzC,eAAW,YAAY,WAAW;AAChC,iBAAW,WAAW,SAAS,YAAY,CAAC,GAAG;AAC7C,YAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,EAAG,kBAAiB,IAAI,OAAO;AAAA,MACxE;AAAA,IACF;AACA,QAAI,iBAAiB,SAAS,EAAG;AACjC,QAAI,YAAY;AAChB,UAAM,gBAAgB,YAAY;AAChC,YAAM,OAAO,MAAM,QAA8B,2BAA2B;AAAA,QAC1E,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,MAAM,KAAK,gBAAgB,EAAE,CAAC;AAAA,MACjE,CAAC;AACD,UAAI,UAAW;AACf,UAAI,KAAK,MAAM,KAAK,QAAQ,SAAS;AACnC,wBAAgB,KAAK,OAAO,OAAO;AAAA,MACrC;AAAA,IACF;AACA,SAAK,cAAc;AACnB,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,SACE,oBAAC,4BAA4B,UAA5B,EAAqC,OAAO,cAC1C,UACH;AAEJ;AAEA,IAAO,oCAAQ;",
6
6
  "names": []
7
7
  }
@@ -20,6 +20,13 @@ function normalizeOptions(input) {
20
20
  };
21
21
  }).filter((option) => !!option);
22
22
  }
23
+ function areOptionsEqual(a, b) {
24
+ if (a.length !== b.length) return false;
25
+ return a.every((option, index) => {
26
+ const next = b[index];
27
+ return option.value === next.value && option.label === next.label && (option.description ?? null) === (next.description ?? null);
28
+ });
29
+ }
23
30
  function ComboboxInput({
24
31
  value,
25
32
  onChange,
@@ -45,6 +52,7 @@ function ComboboxInput({
45
52
  const [selectedIndex, setSelectedIndex] = React.useState(-1);
46
53
  const inputRef = React.useRef(null);
47
54
  const suppressOpenOnFocusRef = React.useRef(Boolean(autoFocus && !disabled));
55
+ const eagerFallbackLoadedValueRef = React.useRef(null);
48
56
  const staticOptions = React.useMemo(
49
57
  () => normalizeOptions([...seedOptions ?? [], ...suggestions ?? []]),
50
58
  [seedOptions, suggestions]
@@ -56,6 +64,13 @@ function ComboboxInput({
56
64
  }
57
65
  return set;
58
66
  }, [staticOptions, asyncOptions, resolvedOptions]);
67
+ const coveredOptionValues = React.useMemo(() => {
68
+ const set = /* @__PURE__ */ new Set();
69
+ for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {
70
+ set.add(option.value);
71
+ }
72
+ return set;
73
+ }, [staticOptions, asyncOptions, resolvedOptions]);
59
74
  const optionMap = React.useMemo(() => {
60
75
  const map = /* @__PURE__ */ new Map();
61
76
  const register = (option) => {
@@ -70,16 +85,15 @@ function ComboboxInput({
70
85
  if (value) {
71
86
  const existing = map.get(value);
72
87
  if (!existing) {
73
- const sync = typeof resolveLabel === "function" ? resolveLabel(value) : void 0;
74
88
  map.set(value, {
75
89
  value,
76
- label: typeof sync === "string" && sync ? sync : value,
90
+ label: value,
77
91
  description: resolveDescription?.(value) ?? null
78
92
  });
79
93
  }
80
94
  }
81
95
  return map;
82
- }, [asyncOptions, resolvedOptions, resolveDescription, resolveLabel, staticOptions, value]);
96
+ }, [asyncOptions, resolvedOptions, resolveDescription, staticOptions, value]);
83
97
  const availableOptions = React.useMemo(() => {
84
98
  return Array.from(optionMap.values());
85
99
  }, [optionMap]);
@@ -101,7 +115,8 @@ function ComboboxInput({
101
115
  try {
102
116
  const items = await loadSuggestions(query);
103
117
  if (!cancelled) {
104
- setAsyncOptions(normalizeOptions(items));
118
+ const normalized = normalizeOptions(items);
119
+ setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized);
105
120
  }
106
121
  } finally {
107
122
  if (!cancelled) setLoading(false);
@@ -115,7 +130,6 @@ function ComboboxInput({
115
130
  const eagerResolveLabel = typeof resolveLabel === "function" ? resolveLabel : void 0;
116
131
  React.useEffect(() => {
117
132
  if (!value || disabled) return;
118
- if (knownLabelValues.has(value)) return;
119
133
  let cancelled = false;
120
134
  const apply = (label, description) => {
121
135
  const clean = typeof label === "string" ? label.trim() : "";
@@ -126,18 +140,22 @@ function ComboboxInput({
126
140
  });
127
141
  };
128
142
  if (eagerResolveLabel) {
143
+ if (knownLabelValues.has(value)) return;
129
144
  Promise.resolve().then(() => eagerResolveLabel(value)).then((label) => apply(label, resolveDescription?.(value))).catch(() => {
130
145
  });
131
146
  return () => {
132
147
  cancelled = true;
133
148
  };
134
149
  }
150
+ if (coveredOptionValues.has(value)) return;
151
+ if (eagerFallbackLoadedValueRef.current === value) return;
152
+ eagerFallbackLoadedValueRef.current = value;
135
153
  if (loadSuggestions) {
136
154
  setLoading(true);
137
155
  Promise.resolve().then(() => loadSuggestions()).then((items) => {
138
156
  if (cancelled) return;
139
157
  const normalized = normalizeOptions(items);
140
- setAsyncOptions(normalized);
158
+ setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized);
141
159
  }).catch(() => {
142
160
  }).finally(() => {
143
161
  if (!cancelled) setLoading(false);
@@ -146,7 +164,7 @@ function ComboboxInput({
146
164
  return () => {
147
165
  cancelled = true;
148
166
  };
149
- }, [value, disabled, knownLabelValues, eagerResolveLabel, loadSuggestions]);
167
+ }, [value, disabled, knownLabelValues, coveredOptionValues, eagerResolveLabel, loadSuggestions]);
150
168
  React.useEffect(() => {
151
169
  if (document.activeElement !== inputRef.current) {
152
170
  const option = optionMap.get(value);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/inputs/ComboboxInput.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\n\nexport type ComboboxOption = {\n value: string\n label: string\n description?: string | null\n}\n\nexport type ComboboxInputProps = {\n value: string\n onChange: (next: string) => void\n placeholder?: string\n suggestions?: Array<string | ComboboxOption>\n // Options to hydrate the option map up front (typically the linked entity's\n // display fields, already present in a record-detail payload). Merged with\n // `suggestions` so a pre-selected value renders its label without interaction.\n seedOptions?: ComboboxOption[]\n loadSuggestions?: (query?: string) => Promise<Array<string | ComboboxOption>>\n // Eagerly resolve a pre-selected `value` to a human label when it is not\n // covered by `suggestions`/`seedOptions`/`loadSuggestions` results. Runs once\n // per value, before any user interaction. May be sync or async.\n resolveLabel?: (value: string) => string | Promise<string>\n resolveDescription?: (value: string) => string | null | undefined\n autoFocus?: boolean\n disabled?: boolean\n allowCustomValues?: boolean\n}\n\nfunction normalizeOptions(input?: Array<string | ComboboxOption>): ComboboxOption[] {\n if (!Array.isArray(input)) return []\n return input\n .map((option) => {\n if (typeof option === 'string') {\n const trimmed = option.trim()\n if (!trimmed) return null\n return { value: trimmed, label: trimmed }\n }\n const value = typeof option.value === 'string' ? option.value.trim() : ''\n if (!value) return null\n return {\n value,\n label: option.label?.trim() || value,\n description: option.description ?? null,\n }\n })\n .filter((option): option is ComboboxOption => !!option)\n}\n\nexport function ComboboxInput({\n value,\n onChange,\n placeholder,\n suggestions,\n seedOptions,\n loadSuggestions,\n resolveLabel,\n resolveDescription,\n autoFocus,\n disabled = false,\n allowCustomValues = true,\n}: ComboboxInputProps) {\n const t = useT()\n const resolvedPlaceholder = placeholder ?? t('ui.inputs.comboboxInput.placeholder', 'Type to search...')\n const loadingLabel = t('ui.inputs.comboboxInput.loading', 'Loading suggestions\u2026')\n const [input, setInput] = React.useState('')\n const [asyncOptions, setAsyncOptions] = React.useState<ComboboxOption[]>([])\n const [resolvedOptions, setResolvedOptions] = React.useState<ComboboxOption[]>([])\n const [loading, setLoading] = React.useState(false)\n const [touched, setTouched] = React.useState(false)\n const [showSuggestions, setShowSuggestions] = React.useState(false)\n const [selectedIndex, setSelectedIndex] = React.useState(-1)\n const inputRef = React.useRef<HTMLInputElement>(null)\n const suppressOpenOnFocusRef = React.useRef(Boolean(autoFocus && !disabled))\n\n const staticOptions = React.useMemo(\n () => normalizeOptions([...(seedOptions ?? []), ...(suggestions ?? [])]),\n [seedOptions, suggestions]\n )\n\n // Options whose label is genuinely known (not a self-mapping `value === label`\n // placeholder). Used to decide whether eager resolution still needs to run.\n const knownLabelValues = React.useMemo(() => {\n const set = new Set<string>()\n for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {\n if (option.label && option.label !== option.value) set.add(option.value)\n }\n return set\n }, [staticOptions, asyncOptions, resolvedOptions])\n\n const optionMap = React.useMemo(() => {\n const map = new Map<string, ComboboxOption>()\n const register = (option: ComboboxOption) => {\n const existing = map.get(option.value)\n // Prefer an entry that carries a real label over a self-mapping placeholder.\n if (!existing || (existing.label === existing.value && option.label !== option.value)) {\n map.set(option.value, option)\n }\n }\n staticOptions.forEach(register)\n asyncOptions.forEach(register)\n resolvedOptions.forEach(register)\n if (value) {\n const existing = map.get(value)\n if (!existing) {\n const sync = typeof resolveLabel === 'function' ? resolveLabel(value) : undefined\n map.set(value, {\n value,\n label: typeof sync === 'string' && sync ? sync : value,\n description: resolveDescription?.(value) ?? null,\n })\n }\n }\n return map\n }, [asyncOptions, resolvedOptions, resolveDescription, resolveLabel, staticOptions, value])\n\n const availableOptions = React.useMemo(() => {\n return Array.from(optionMap.values())\n }, [optionMap])\n\n const filteredSuggestions = React.useMemo(() => {\n const query = input.toLowerCase().trim()\n if (!query) return availableOptions\n return availableOptions.filter((option) => {\n const labelMatch = option.label.toLowerCase().includes(query)\n const descMatch = option.description?.toLowerCase().includes(query)\n return labelMatch || Boolean(descMatch)\n })\n }, [availableOptions, input])\n\n React.useEffect(() => {\n if (!loadSuggestions || !touched || disabled) return\n const query = input.trim()\n let cancelled = false\n const handle = window.setTimeout(async () => {\n setLoading(true)\n try {\n const items = await loadSuggestions(query)\n if (!cancelled) {\n setAsyncOptions(normalizeOptions(items))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n window.clearTimeout(handle)\n }\n }, [disabled, input, loadSuggestions, touched])\n\n // Eagerly resolve a pre-selected value to its label without requiring the user\n // to focus the field. Runs once per value when it is not already covered.\n const eagerResolveLabel = typeof resolveLabel === 'function' ? resolveLabel : undefined\n React.useEffect(() => {\n if (!value || disabled) return\n if (knownLabelValues.has(value)) return\n let cancelled = false\n const apply = (label?: string | null, description?: string | null) => {\n const clean = typeof label === 'string' ? label.trim() : ''\n if (cancelled || !clean || clean === value) return\n setResolvedOptions((prev) => {\n if (prev.some((option) => option.value === value && option.label === clean)) return prev\n return [...prev.filter((option) => option.value !== value), { value, label: clean, description: description ?? null }]\n })\n }\n if (eagerResolveLabel) {\n Promise.resolve()\n .then(() => eagerResolveLabel(value))\n .then((label) => apply(label, resolveDescription?.(value)))\n .catch(() => {})\n return () => { cancelled = true }\n }\n // Fallback: pull the first page of async suggestions so a remount that lost\n // its option cache can still recover the label without user interaction.\n if (loadSuggestions) {\n setLoading(true)\n Promise.resolve()\n .then(() => loadSuggestions())\n .then((items) => {\n if (cancelled) return\n const normalized = normalizeOptions(items)\n setAsyncOptions(normalized)\n })\n .catch(() => {})\n .finally(() => { if (!cancelled) setLoading(false) })\n }\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value, disabled, knownLabelValues, eagerResolveLabel, loadSuggestions])\n\n // Sync input with value when value changes externally and input is not focused.\n React.useEffect(() => {\n if (document.activeElement !== inputRef.current) {\n const option = optionMap.get(value)\n setInput(option?.label ?? value ?? '')\n }\n }, [value, optionMap])\n\n const selectValue = React.useCallback(\n (nextValue: string) => {\n if (disabled) return\n const trimmed = nextValue.trim()\n onChange(trimmed)\n const option = optionMap.get(trimmed)\n setInput(option?.label ?? trimmed)\n setShowSuggestions(false)\n setSelectedIndex(-1)\n },\n [disabled, onChange, optionMap]\n )\n\n const findOptionForInput = React.useCallback(\n (raw: string): ComboboxOption | null => {\n const query = raw.trim().toLowerCase()\n if (!query) return null\n for (const option of optionMap.values()) {\n if (option.value === raw.trim()) return option\n if (option.label.toLowerCase() === query) return option\n }\n return null\n },\n [optionMap]\n )\n\n const confirmSelection = React.useCallback(\n (raw: string) => {\n if (disabled) return\n const option = findOptionForInput(raw)\n if (option) {\n selectValue(option.value)\n return\n }\n if (!allowCustomValues) {\n // Revert to the current value's label \u2014 but only if we actually know it.\n // Baking the raw value back in while eager resolution is still pending\n // would freeze a placeholder (e.g. a UUID) into the visible input.\n setShowSuggestions(false)\n const currentOption = optionMap.get(value)\n if (currentOption && currentOption.label !== currentOption.value) {\n setInput(currentOption.label)\n } else if (!value) {\n setInput('')\n }\n return\n }\n selectValue(raw)\n },\n [allowCustomValues, disabled, findOptionForInput, optionMap, selectValue, value]\n )\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n if (!showSuggestions) {\n setShowSuggestions(true)\n setSelectedIndex(0)\n } else {\n setSelectedIndex((prev) => Math.min(prev + 1, filteredSuggestions.length - 1))\n }\n } else if (event.key === 'ArrowUp') {\n event.preventDefault()\n setSelectedIndex((prev) => Math.max(prev - 1, -1))\n } else if (event.key === 'Enter') {\n event.preventDefault()\n if (selectedIndex >= 0 && filteredSuggestions[selectedIndex]) {\n selectValue(filteredSuggestions[selectedIndex].value)\n } else {\n confirmSelection(input)\n }\n } else if (event.key === 'Escape') {\n event.preventDefault()\n setShowSuggestions(false)\n setSelectedIndex(-1)\n }\n },\n [confirmSelection, disabled, filteredSuggestions, input, selectValue, selectedIndex, showSuggestions]\n )\n\n return (\n <div className=\"relative w-full\">\n {/* Use raw <input> here instead of the DS Input primitive: ComboboxInput's\n focus / suggestions-popup interplay relies on the trigger being a plain\n input element. The DS wrapper introduces a <div> that desyncs autocomplete\n on this specific surface. Keeps the rest of the form on Input primitive. */}\n <input\n ref={inputRef}\n type=\"text\"\n className=\"w-full h-9 rounded-md border border-input bg-background px-3 text-sm shadow-xs transition-colors outline-none placeholder:text-muted-foreground focus-visible:shadow-focus focus-visible:border-foreground disabled:bg-bg-disabled disabled:border-border-disabled disabled:text-muted-foreground disabled:cursor-not-allowed\"\n value={input}\n placeholder={resolvedPlaceholder}\n autoFocus={autoFocus}\n data-crud-focus-target=\"\"\n disabled={disabled}\n onFocus={() => {\n setTouched(true)\n if (suppressOpenOnFocusRef.current) {\n suppressOpenOnFocusRef.current = false\n return\n }\n setShowSuggestions(true)\n }}\n onChange={(event) => {\n setTouched(true)\n setInput(event.target.value)\n setShowSuggestions(true)\n setSelectedIndex(-1)\n }}\n onKeyDown={handleKeyDown}\n onBlur={() => {\n // Delay to allow click on suggestions\n setTimeout(() => {\n if (disabled) return\n confirmSelection(input)\n }, 200)\n }}\n />\n\n {showSuggestions && !disabled && (loading || filteredSuggestions.length > 0) && (\n <div className=\"absolute z-popover w-full mt-1 rounded-md border border-input bg-popover p-2 shadow-md max-h-48 sm:max-h-60 overflow-auto\">\n {loading && touched ? (\n <div className=\"px-2 py-1.5 text-xs text-muted-foreground\">{loadingLabel}</div>\n ) : (\n <div className=\"flex flex-col gap-1\">\n {filteredSuggestions.map((option, index) => (\n <Button\n key={option.value}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={[\n 'w-full h-auto justify-start font-normal text-left flex flex-col items-start rounded-lg p-2',\n index === selectedIndex ? 'bg-muted' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => selectValue(option.value)}\n onMouseEnter={() => setSelectedIndex(index)}\n >\n <span className=\"font-medium text-foreground\">{option.label}</span>\n {option.description ? (\n <span className=\"text-xs text-muted-foreground\">{option.description}</span>\n ) : null}\n </Button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\n\nexport type ComboboxOption = {\n value: string\n label: string\n description?: string | null\n}\n\nexport type ComboboxInputProps = {\n value: string\n onChange: (next: string) => void\n placeholder?: string\n suggestions?: Array<string | ComboboxOption>\n // Options to hydrate the option map up front (typically the linked entity's\n // display fields, already present in a record-detail payload). Merged with\n // `suggestions` so a pre-selected value renders its label without interaction.\n seedOptions?: ComboboxOption[]\n loadSuggestions?: (query?: string) => Promise<Array<string | ComboboxOption>>\n // Eagerly resolve a pre-selected `value` to a human label when it is not\n // covered by `suggestions`/`seedOptions`/`loadSuggestions` results. Runs once\n // per value, before any user interaction. May be sync or async.\n resolveLabel?: (value: string) => string | Promise<string>\n resolveDescription?: (value: string) => string | null | undefined\n autoFocus?: boolean\n disabled?: boolean\n allowCustomValues?: boolean\n}\n\nfunction normalizeOptions(input?: Array<string | ComboboxOption>): ComboboxOption[] {\n if (!Array.isArray(input)) return []\n return input\n .map((option) => {\n if (typeof option === 'string') {\n const trimmed = option.trim()\n if (!trimmed) return null\n return { value: trimmed, label: trimmed }\n }\n const value = typeof option.value === 'string' ? option.value.trim() : ''\n if (!value) return null\n return {\n value,\n label: option.label?.trim() || value,\n description: option.description ?? null,\n }\n })\n .filter((option): option is ComboboxOption => !!option)\n}\n\nfunction areOptionsEqual(a: ComboboxOption[], b: ComboboxOption[]): boolean {\n if (a.length !== b.length) return false\n return a.every((option, index) => {\n const next = b[index]\n return option.value === next.value\n && option.label === next.label\n && (option.description ?? null) === (next.description ?? null)\n })\n}\n\nexport function ComboboxInput({\n value,\n onChange,\n placeholder,\n suggestions,\n seedOptions,\n loadSuggestions,\n resolveLabel,\n resolveDescription,\n autoFocus,\n disabled = false,\n allowCustomValues = true,\n}: ComboboxInputProps) {\n const t = useT()\n const resolvedPlaceholder = placeholder ?? t('ui.inputs.comboboxInput.placeholder', 'Type to search...')\n const loadingLabel = t('ui.inputs.comboboxInput.loading', 'Loading suggestions\u2026')\n const [input, setInput] = React.useState('')\n const [asyncOptions, setAsyncOptions] = React.useState<ComboboxOption[]>([])\n const [resolvedOptions, setResolvedOptions] = React.useState<ComboboxOption[]>([])\n const [loading, setLoading] = React.useState(false)\n const [touched, setTouched] = React.useState(false)\n const [showSuggestions, setShowSuggestions] = React.useState(false)\n const [selectedIndex, setSelectedIndex] = React.useState(-1)\n const inputRef = React.useRef<HTMLInputElement>(null)\n const suppressOpenOnFocusRef = React.useRef(Boolean(autoFocus && !disabled))\n const eagerFallbackLoadedValueRef = React.useRef<string | null>(null)\n\n const staticOptions = React.useMemo(\n () => normalizeOptions([...(seedOptions ?? []), ...(suggestions ?? [])]),\n [seedOptions, suggestions]\n )\n\n // Options whose label is genuinely known (not a self-mapping `value === label`\n // placeholder). Used to decide whether eager resolution still needs to run.\n const knownLabelValues = React.useMemo(() => {\n const set = new Set<string>()\n for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {\n if (option.label && option.label !== option.value) set.add(option.value)\n }\n return set\n }, [staticOptions, asyncOptions, resolvedOptions])\n\n const coveredOptionValues = React.useMemo(() => {\n const set = new Set<string>()\n for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {\n set.add(option.value)\n }\n return set\n }, [staticOptions, asyncOptions, resolvedOptions])\n\n const optionMap = React.useMemo(() => {\n const map = new Map<string, ComboboxOption>()\n const register = (option: ComboboxOption) => {\n const existing = map.get(option.value)\n // Prefer an entry that carries a real label over a self-mapping placeholder.\n if (!existing || (existing.label === existing.value && option.label !== option.value)) {\n map.set(option.value, option)\n }\n }\n staticOptions.forEach(register)\n asyncOptions.forEach(register)\n resolvedOptions.forEach(register)\n if (value) {\n const existing = map.get(value)\n if (!existing) {\n map.set(value, {\n value,\n label: value,\n description: resolveDescription?.(value) ?? null,\n })\n }\n }\n return map\n }, [asyncOptions, resolvedOptions, resolveDescription, staticOptions, value])\n\n const availableOptions = React.useMemo(() => {\n return Array.from(optionMap.values())\n }, [optionMap])\n\n const filteredSuggestions = React.useMemo(() => {\n const query = input.toLowerCase().trim()\n if (!query) return availableOptions\n return availableOptions.filter((option) => {\n const labelMatch = option.label.toLowerCase().includes(query)\n const descMatch = option.description?.toLowerCase().includes(query)\n return labelMatch || Boolean(descMatch)\n })\n }, [availableOptions, input])\n\n React.useEffect(() => {\n if (!loadSuggestions || !touched || disabled) return\n const query = input.trim()\n let cancelled = false\n const handle = window.setTimeout(async () => {\n setLoading(true)\n try {\n const items = await loadSuggestions(query)\n if (!cancelled) {\n const normalized = normalizeOptions(items)\n setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized)\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n window.clearTimeout(handle)\n }\n }, [disabled, input, loadSuggestions, touched])\n\n // Eagerly resolve a pre-selected value to its label without requiring the user\n // to focus the field. Runs once per value when it is not already covered.\n const eagerResolveLabel = typeof resolveLabel === 'function' ? resolveLabel : undefined\n React.useEffect(() => {\n if (!value || disabled) return\n let cancelled = false\n const apply = (label?: string | null, description?: string | null) => {\n const clean = typeof label === 'string' ? label.trim() : ''\n if (cancelled || !clean || clean === value) return\n setResolvedOptions((prev) => {\n if (prev.some((option) => option.value === value && option.label === clean)) return prev\n return [...prev.filter((option) => option.value !== value), { value, label: clean, description: description ?? null }]\n })\n }\n if (eagerResolveLabel) {\n if (knownLabelValues.has(value)) return\n Promise.resolve()\n .then(() => eagerResolveLabel(value))\n .then((label) => apply(label, resolveDescription?.(value)))\n .catch(() => {})\n return () => { cancelled = true }\n }\n if (coveredOptionValues.has(value)) return\n if (eagerFallbackLoadedValueRef.current === value) return\n eagerFallbackLoadedValueRef.current = value\n // Fallback: pull the first page of async suggestions so a remount that lost\n // its option cache can still recover the label without user interaction.\n if (loadSuggestions) {\n setLoading(true)\n Promise.resolve()\n .then(() => loadSuggestions())\n .then((items) => {\n if (cancelled) return\n const normalized = normalizeOptions(items)\n setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized)\n })\n .catch(() => {})\n .finally(() => { if (!cancelled) setLoading(false) })\n }\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value, disabled, knownLabelValues, coveredOptionValues, eagerResolveLabel, loadSuggestions])\n\n // Sync input with value when value changes externally and input is not focused.\n React.useEffect(() => {\n if (document.activeElement !== inputRef.current) {\n const option = optionMap.get(value)\n setInput(option?.label ?? value ?? '')\n }\n }, [value, optionMap])\n\n const selectValue = React.useCallback(\n (nextValue: string) => {\n if (disabled) return\n const trimmed = nextValue.trim()\n onChange(trimmed)\n const option = optionMap.get(trimmed)\n setInput(option?.label ?? trimmed)\n setShowSuggestions(false)\n setSelectedIndex(-1)\n },\n [disabled, onChange, optionMap]\n )\n\n const findOptionForInput = React.useCallback(\n (raw: string): ComboboxOption | null => {\n const query = raw.trim().toLowerCase()\n if (!query) return null\n for (const option of optionMap.values()) {\n if (option.value === raw.trim()) return option\n if (option.label.toLowerCase() === query) return option\n }\n return null\n },\n [optionMap]\n )\n\n const confirmSelection = React.useCallback(\n (raw: string) => {\n if (disabled) return\n const option = findOptionForInput(raw)\n if (option) {\n selectValue(option.value)\n return\n }\n if (!allowCustomValues) {\n // Revert to the current value's label \u2014 but only if we actually know it.\n // Baking the raw value back in while eager resolution is still pending\n // would freeze a placeholder (e.g. a UUID) into the visible input.\n setShowSuggestions(false)\n const currentOption = optionMap.get(value)\n if (currentOption && currentOption.label !== currentOption.value) {\n setInput(currentOption.label)\n } else if (!value) {\n setInput('')\n }\n return\n }\n selectValue(raw)\n },\n [allowCustomValues, disabled, findOptionForInput, optionMap, selectValue, value]\n )\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n if (!showSuggestions) {\n setShowSuggestions(true)\n setSelectedIndex(0)\n } else {\n setSelectedIndex((prev) => Math.min(prev + 1, filteredSuggestions.length - 1))\n }\n } else if (event.key === 'ArrowUp') {\n event.preventDefault()\n setSelectedIndex((prev) => Math.max(prev - 1, -1))\n } else if (event.key === 'Enter') {\n event.preventDefault()\n if (selectedIndex >= 0 && filteredSuggestions[selectedIndex]) {\n selectValue(filteredSuggestions[selectedIndex].value)\n } else {\n confirmSelection(input)\n }\n } else if (event.key === 'Escape') {\n event.preventDefault()\n setShowSuggestions(false)\n setSelectedIndex(-1)\n }\n },\n [confirmSelection, disabled, filteredSuggestions, input, selectValue, selectedIndex, showSuggestions]\n )\n\n return (\n <div className=\"relative w-full\">\n {/* Use raw <input> here instead of the DS Input primitive: ComboboxInput's\n focus / suggestions-popup interplay relies on the trigger being a plain\n input element. The DS wrapper introduces a <div> that desyncs autocomplete\n on this specific surface. Keeps the rest of the form on Input primitive. */}\n <input\n ref={inputRef}\n type=\"text\"\n className=\"w-full h-9 rounded-md border border-input bg-background px-3 text-sm shadow-xs transition-colors outline-none placeholder:text-muted-foreground focus-visible:shadow-focus focus-visible:border-foreground disabled:bg-bg-disabled disabled:border-border-disabled disabled:text-muted-foreground disabled:cursor-not-allowed\"\n value={input}\n placeholder={resolvedPlaceholder}\n autoFocus={autoFocus}\n data-crud-focus-target=\"\"\n disabled={disabled}\n onFocus={() => {\n setTouched(true)\n if (suppressOpenOnFocusRef.current) {\n suppressOpenOnFocusRef.current = false\n return\n }\n setShowSuggestions(true)\n }}\n onChange={(event) => {\n setTouched(true)\n setInput(event.target.value)\n setShowSuggestions(true)\n setSelectedIndex(-1)\n }}\n onKeyDown={handleKeyDown}\n onBlur={() => {\n // Delay to allow click on suggestions\n setTimeout(() => {\n if (disabled) return\n confirmSelection(input)\n }, 200)\n }}\n />\n\n {showSuggestions && !disabled && (loading || filteredSuggestions.length > 0) && (\n <div className=\"absolute z-popover w-full mt-1 rounded-md border border-input bg-popover p-2 shadow-md max-h-48 sm:max-h-60 overflow-auto\">\n {loading && touched ? (\n <div className=\"px-2 py-1.5 text-xs text-muted-foreground\">{loadingLabel}</div>\n ) : (\n <div className=\"flex flex-col gap-1\">\n {filteredSuggestions.map((option, index) => (\n <Button\n key={option.value}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={[\n 'w-full h-auto justify-start font-normal text-left flex flex-col items-start rounded-lg p-2',\n index === selectedIndex ? 'bg-muted' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => selectValue(option.value)}\n onMouseEnter={() => setSelectedIndex(index)}\n >\n <span className=\"font-medium text-foreground\">{option.label}</span>\n {option.description ? (\n <span className=\"text-xs text-muted-foreground\">{option.description}</span>\n ) : null}\n </Button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }