@open-mercato/ui 0.8.1-develop.7296.1.2111d779db → 0.9.0

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 (85) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/backend/CrudForm.js +19 -6
  3. package/dist/backend/CrudForm.js.map +2 -2
  4. package/dist/backend/DataTable.js +62 -10
  5. package/dist/backend/DataTable.js.map +2 -2
  6. package/dist/backend/RowActions.js +54 -5
  7. package/dist/backend/RowActions.js.map +2 -2
  8. package/dist/backend/charts/KpiCard.js +2 -1
  9. package/dist/backend/charts/KpiCard.js.map +2 -2
  10. package/dist/backend/detail/AttachmentsSection.js +17 -5
  11. package/dist/backend/detail/AttachmentsSection.js.map +2 -2
  12. package/dist/backend/detail/NotesSection.js +38 -20
  13. package/dist/backend/detail/NotesSection.js.map +2 -2
  14. package/dist/backend/dev/DevRuntimeDiagnosticsBanner.js +11 -1
  15. package/dist/backend/dev/DevRuntimeDiagnosticsBanner.js.map +2 -2
  16. package/dist/backend/icons/lucideRegistry.generated.js +26 -0
  17. package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
  18. package/dist/backend/injection/eventBridge.js +33 -0
  19. package/dist/backend/injection/eventBridge.js.map +2 -2
  20. package/dist/backend/inputs/LookupSelect.js +20 -3
  21. package/dist/backend/inputs/LookupSelect.js.map +2 -2
  22. package/dist/backend/inputs/PhoneNumberField.js +1 -1
  23. package/dist/backend/inputs/PhoneNumberField.js.map +2 -2
  24. package/dist/backend/inputs/TagsInput.js +20 -2
  25. package/dist/backend/inputs/TagsInput.js.map +2 -2
  26. package/dist/backend/messages/EmailThreadsPanel.js +2 -1
  27. package/dist/backend/messages/EmailThreadsPanel.js.map +2 -2
  28. package/dist/backend/messages/MessageComposer.js.map +2 -2
  29. package/dist/backend/messages/index.js.map +2 -2
  30. package/dist/backend/messages/message-compose-form-groups.js +24 -0
  31. package/dist/backend/messages/message-compose-form-groups.js.map +2 -2
  32. package/dist/backend/messages/useMessageCompose.js +27 -0
  33. package/dist/backend/messages/useMessageCompose.js.map +2 -2
  34. package/dist/backend/messages/useMessageComposeOperations.js +1 -0
  35. package/dist/backend/messages/useMessageComposeOperations.js.map +2 -2
  36. package/dist/backend/notifications/NotificationItem.js +8 -1
  37. package/dist/backend/notifications/NotificationItem.js.map +2 -2
  38. package/dist/portal/PortalShell.js +2 -2
  39. package/dist/portal/PortalShell.js.map +2 -2
  40. package/dist/primitives/calendar.js +69 -14
  41. package/dist/primitives/calendar.js.map +2 -2
  42. package/package.json +4 -5
  43. package/src/backend/CrudForm.tsx +30 -8
  44. package/src/backend/DataTable.tsx +95 -20
  45. package/src/backend/RowActions.tsx +114 -6
  46. package/src/backend/__tests__/AttachmentsSection.upload.test.tsx +183 -0
  47. package/src/backend/__tests__/CrudForm.fieldInjection.test.tsx +139 -2
  48. package/src/backend/__tests__/CrudForm.hiddenGroups.test.tsx +1 -1
  49. package/src/backend/__tests__/DataTable.extensions.test.tsx +30 -0
  50. package/src/backend/__tests__/DataTable.perspectiveReconcile.test.tsx +320 -2
  51. package/src/backend/__tests__/DataTable.stickyResponsive.test.tsx +19 -0
  52. package/src/backend/__tests__/DevRuntimeDiagnosticsBanner.test.tsx +117 -0
  53. package/src/backend/__tests__/NotesSection.test.tsx +138 -0
  54. package/src/backend/__tests__/RowActions.test.tsx +202 -0
  55. package/src/backend/charts/KpiCard.tsx +1 -0
  56. package/src/backend/charts/__tests__/KpiCard.test.tsx +20 -0
  57. package/src/backend/detail/AttachmentsSection.tsx +17 -5
  58. package/src/backend/detail/NotesSection.tsx +122 -66
  59. package/src/backend/dev/DevRuntimeDiagnosticsBanner.tsx +24 -1
  60. package/src/backend/icons/lucideRegistry.generated.tsx +26 -0
  61. package/src/backend/injection/__tests__/eventBridge.test.tsx +47 -1
  62. package/src/backend/injection/eventBridge.ts +35 -0
  63. package/src/backend/inputs/LookupSelect.tsx +32 -3
  64. package/src/backend/inputs/PhoneNumberField.tsx +1 -1
  65. package/src/backend/inputs/TagsInput.tsx +24 -3
  66. package/src/backend/inputs/__tests__/LookupSelect.test.tsx +166 -1
  67. package/src/backend/inputs/__tests__/PhoneNumberField.typeahead.test.tsx +43 -0
  68. package/src/backend/inputs/__tests__/TagsInput.test.tsx +81 -0
  69. package/src/backend/messages/EmailThreadsPanel.tsx +21 -1
  70. package/src/backend/messages/MessageComposer.tsx +1 -0
  71. package/src/backend/messages/__tests__/EmailThreadsPanel.senderLabel.test.tsx +99 -0
  72. package/src/backend/messages/__tests__/MessageComposer.test.tsx +44 -0
  73. package/src/backend/messages/__tests__/useMessageCompose.test.tsx +69 -0
  74. package/src/backend/messages/__tests__/useMessageComposeSender.test.tsx +208 -0
  75. package/src/backend/messages/index.ts +1 -0
  76. package/src/backend/messages/message-compose-form-groups.tsx +41 -0
  77. package/src/backend/messages/message-composer.types.ts +26 -0
  78. package/src/backend/messages/useMessageCompose.ts +46 -0
  79. package/src/backend/messages/useMessageComposeOperations.ts +8 -0
  80. package/src/backend/notifications/NotificationItem.tsx +7 -0
  81. package/src/backend/notifications/__tests__/NotificationItem.customRenderer.test.tsx +139 -0
  82. package/src/portal/PortalShell.tsx +7 -4
  83. package/src/portal/__tests__/PortalShell.test.tsx +29 -1
  84. package/src/primitives/__tests__/calendar.test.tsx +174 -1
  85. package/src/primitives/calendar.tsx +168 -96
@@ -4,6 +4,7 @@ import {
4
4
  AlertCircle,
5
5
  AlertOctagon,
6
6
  AlertTriangle,
7
+ AlignLeft,
7
8
  Apple,
8
9
  Archive,
9
10
  AtSign,
@@ -68,9 +69,12 @@ import {
68
69
  GitPullRequestArrow,
69
70
  Globe,
70
71
  GraduationCap,
72
+ Grid3x3,
71
73
  Hammer,
72
74
  Hand,
73
75
  Handshake,
76
+ Hash,
77
+ Heading,
74
78
  Headphones,
75
79
  Heart,
76
80
  HelpCircle,
@@ -85,6 +89,7 @@ import {
85
89
  Link,
86
90
  List,
87
91
  ListChecks,
92
+ ListOrdered,
88
93
  ListX,
89
94
  Loader,
90
95
  Loader2,
@@ -105,9 +110,11 @@ import {
105
110
  PackagePlus,
106
111
  PackageSearch,
107
112
  PackageX,
113
+ Paperclip,
108
114
  PauseCircle,
109
115
  PencilLine,
110
116
  PenLine,
117
+ PenTool,
111
118
  Percent,
112
119
  Phone,
113
120
  PhoneCall,
@@ -121,7 +128,9 @@ import {
121
128
  Repeat2,
122
129
  Reply,
123
130
  RotateCcw,
131
+ Rows,
124
132
  Ruler,
133
+ Search,
125
134
  Send,
126
135
  Settings,
127
136
  Shapes,
@@ -133,6 +142,7 @@ import {
133
142
  ShoppingCart,
134
143
  Shuffle,
135
144
  Sliders,
145
+ SlidersHorizontal,
136
146
  Smartphone,
137
147
  Snowflake,
138
148
  Sparkles,
@@ -144,11 +154,14 @@ import {
144
154
  ThumbsUp,
145
155
  Ticket,
146
156
  Timer,
157
+ ToggleLeft,
158
+ ToggleRight,
147
159
  Trash2,
148
160
  TrendingUp,
149
161
  TriangleAlert,
150
162
  Trophy,
151
163
  Truck,
164
+ Type,
152
165
  Undo,
153
166
  Undo2,
154
167
  Unlock,
@@ -175,6 +188,7 @@ const LUCIDE_ICON_REGISTRY = {
175
188
  "alert-circle": AlertCircle,
176
189
  "alert-octagon": AlertOctagon,
177
190
  "alert-triangle": AlertTriangle,
191
+ "align-left": AlignLeft,
178
192
  "apple": Apple,
179
193
  "archive": Archive,
180
194
  "at-sign": AtSign,
@@ -239,9 +253,12 @@ const LUCIDE_ICON_REGISTRY = {
239
253
  "git-pull-request-arrow": GitPullRequestArrow,
240
254
  "globe": Globe,
241
255
  "graduation-cap": GraduationCap,
256
+ "grid-3x3": Grid3x3,
242
257
  "hammer": Hammer,
243
258
  "hand": Hand,
244
259
  "handshake": Handshake,
260
+ "hash": Hash,
261
+ "heading": Heading,
245
262
  "headphones": Headphones,
246
263
  "heart": Heart,
247
264
  "help-circle": HelpCircle,
@@ -256,6 +273,7 @@ const LUCIDE_ICON_REGISTRY = {
256
273
  "link": Link,
257
274
  "list": List,
258
275
  "list-checks": ListChecks,
276
+ "list-ordered": ListOrdered,
259
277
  "list-x": ListX,
260
278
  "loader": Loader,
261
279
  "loader-2": Loader2,
@@ -276,8 +294,10 @@ const LUCIDE_ICON_REGISTRY = {
276
294
  "package-plus": PackagePlus,
277
295
  "package-search": PackageSearch,
278
296
  "package-x": PackageX,
297
+ "paperclip": Paperclip,
279
298
  "pause-circle": PauseCircle,
280
299
  "pen-line": PenLine,
300
+ "pen-tool": PenTool,
281
301
  "pencil-line": PencilLine,
282
302
  "percent": Percent,
283
303
  "phone": Phone,
@@ -292,7 +312,9 @@ const LUCIDE_ICON_REGISTRY = {
292
312
  "repeat-2": Repeat2,
293
313
  "reply": Reply,
294
314
  "rotate-ccw": RotateCcw,
315
+ "rows": Rows,
295
316
  "ruler": Ruler,
317
+ "search": Search,
296
318
  "send": Send,
297
319
  "settings": Settings,
298
320
  "shapes": Shapes,
@@ -304,6 +326,7 @@ const LUCIDE_ICON_REGISTRY = {
304
326
  "shopping-cart": ShoppingCart,
305
327
  "shuffle": Shuffle,
306
328
  "sliders": Sliders,
329
+ "sliders-horizontal": SlidersHorizontal,
307
330
  "smartphone": Smartphone,
308
331
  "snowflake": Snowflake,
309
332
  "sparkles": Sparkles,
@@ -315,11 +338,14 @@ const LUCIDE_ICON_REGISTRY = {
315
338
  "thumbs-up": ThumbsUp,
316
339
  "ticket": Ticket,
317
340
  "timer": Timer,
341
+ "toggle-left": ToggleLeft,
342
+ "toggle-right": ToggleRight,
318
343
  "trash-2": Trash2,
319
344
  "trending-up": TrendingUp,
320
345
  "triangle-alert": TriangleAlert,
321
346
  "trophy": Trophy,
322
347
  "truck": Truck,
348
+ "type": Type,
323
349
  "undo": Undo,
324
350
  "undo-2": Undo2,
325
351
  "unlock": Unlock,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/icons/lucideRegistry.generated.tsx"],
4
- "sourcesContent": ["/* eslint-disable */\n// AUTO-GENERATED by @open-mercato/ui build. Do not edit by hand.\n// Generated from discovered icon string usages across the repo.\n\nimport type { LucideIcon } from 'lucide-react';\nimport {\n Activity,\n AlarmClock,\n AlertCircle,\n AlertOctagon,\n AlertTriangle,\n Apple,\n Archive,\n AtSign,\n Award,\n BadgeCheck,\n Ban,\n Banknote,\n BarChart2,\n BarChart3,\n Bell,\n Bolt,\n Book,\n Bookmark,\n Bot,\n Box,\n Boxes,\n Briefcase,\n BriefcaseBusiness,\n Building,\n Building2,\n Calendar,\n CalendarCheck,\n CalendarClock,\n CalendarCog,\n CalendarMinus,\n CalendarOff,\n CalendarX,\n Check,\n CheckCircle,\n CheckCircle2,\n CheckSquare,\n Circle,\n ClipboardCheck,\n ClipboardList,\n Clock,\n Clock3,\n Coins,\n Copy,\n CopyX,\n CreditCard,\n Crown,\n Database,\n DollarSign,\n Download,\n ExternalLink,\n Eye,\n FileCheck,\n FileClock,\n FileMinus,\n FilePenLine,\n Files,\n FileSpreadsheet,\n FileText,\n FilterX,\n Flag,\n Flame,\n FolderTree,\n FunctionSquare,\n Gauge,\n GitBranch,\n GitCompareArrows,\n GitPullRequestArrow,\n Globe,\n GraduationCap,\n Hammer,\n Hand,\n Handshake,\n Headphones,\n Heart,\n HelpCircle,\n Hourglass,\n Inbox,\n Key,\n KeyRound,\n Layers,\n Leaf,\n Lightbulb,\n LineChart,\n Link,\n List,\n ListChecks,\n ListX,\n Loader,\n Loader2,\n Lock,\n Mail,\n MailOpen,\n Map,\n MapPin,\n MapPinned,\n Megaphone,\n MessageCircle,\n MessageSquareReply,\n MinusCircle,\n Monitor,\n Notebook,\n Package,\n PackageCheck,\n PackagePlus,\n PackageSearch,\n PackageX,\n PauseCircle,\n PencilLine,\n PenLine,\n Percent,\n Phone,\n PhoneCall,\n PieChart,\n PlusSquare,\n Radio,\n Receipt,\n ReceiptText,\n RefreshCcw,\n RefreshCw,\n Repeat2,\n Reply,\n RotateCcw,\n Ruler,\n Send,\n Settings,\n Shapes,\n Share2,\n Shield,\n ShieldAlert,\n ShieldCheck,\n ShoppingBag,\n ShoppingCart,\n Shuffle,\n Sliders,\n Smartphone,\n Snowflake,\n Sparkles,\n Star,\n StickyNote,\n Store,\n Tag,\n Target,\n ThumbsUp,\n Ticket,\n Timer,\n Trash2,\n TrendingUp,\n TriangleAlert,\n Trophy,\n Truck,\n Undo,\n Undo2,\n Unlock,\n User,\n UserCheck,\n UserMinus,\n UserPlus,\n UserRound,\n Users,\n UserX,\n Variable,\n Wallet,\n Warehouse,\n Webhook,\n Workflow,\n Wrench,\n X,\n XCircle,\n ZapOff,\n} from 'lucide-react';\n\nexport const LUCIDE_ICON_REGISTRY: Record<string, LucideIcon> = {\n 'activity': Activity,\n 'alarm-clock': AlarmClock,\n 'alert-circle': AlertCircle,\n 'alert-octagon': AlertOctagon,\n 'alert-triangle': AlertTriangle,\n 'apple': Apple,\n 'archive': Archive,\n 'at-sign': AtSign,\n 'award': Award,\n 'badge-check': BadgeCheck,\n 'ban': Ban,\n 'banknote': Banknote,\n 'bar-chart-2': BarChart2,\n 'bar-chart-3': BarChart3,\n 'bell': Bell,\n 'bolt': Bolt,\n 'book': Book,\n 'bookmark': Bookmark,\n 'bot': Bot,\n 'box': Box,\n 'boxes': Boxes,\n 'briefcase': Briefcase,\n 'briefcase-business': BriefcaseBusiness,\n 'building': Building,\n 'building2': Building2,\n 'calendar': Calendar,\n 'calendar-check': CalendarCheck,\n 'calendar-clock': CalendarClock,\n 'calendar-cog': CalendarCog,\n 'calendar-minus': CalendarMinus,\n 'calendar-off': CalendarOff,\n 'calendar-x': CalendarX,\n 'check': Check,\n 'check-circle': CheckCircle,\n 'check-circle-2': CheckCircle2,\n 'check-square': CheckSquare,\n 'circle': Circle,\n 'clipboard-check': ClipboardCheck,\n 'clipboard-list': ClipboardList,\n 'clock': Clock,\n 'clock-3': Clock3,\n 'coins': Coins,\n 'copy': Copy,\n 'copy-x': CopyX,\n 'credit-card': CreditCard,\n 'crown': Crown,\n 'database': Database,\n 'dollar-sign': DollarSign,\n 'download': Download,\n 'external-link': ExternalLink,\n 'eye': Eye,\n 'file-check': FileCheck,\n 'file-clock': FileClock,\n 'file-minus': FileMinus,\n 'file-pen-line': FilePenLine,\n 'file-spreadsheet': FileSpreadsheet,\n 'file-text': FileText,\n 'files': Files,\n 'filter-x': FilterX,\n 'flag': Flag,\n 'flame': Flame,\n 'folder-tree': FolderTree,\n 'function-square': FunctionSquare,\n 'gauge': Gauge,\n 'git-branch': GitBranch,\n 'git-compare-arrows': GitCompareArrows,\n 'git-pull-request-arrow': GitPullRequestArrow,\n 'globe': Globe,\n 'graduation-cap': GraduationCap,\n 'hammer': Hammer,\n 'hand': Hand,\n 'handshake': Handshake,\n 'headphones': Headphones,\n 'heart': Heart,\n 'help-circle': HelpCircle,\n 'hourglass': Hourglass,\n 'inbox': Inbox,\n 'key': Key,\n 'key-round': KeyRound,\n 'layers': Layers,\n 'leaf': Leaf,\n 'lightbulb': Lightbulb,\n 'line-chart': LineChart,\n 'link': Link,\n 'list': List,\n 'list-checks': ListChecks,\n 'list-x': ListX,\n 'loader': Loader,\n 'loader-2': Loader2,\n 'lock': Lock,\n 'mail': Mail,\n 'mail-open': MailOpen,\n 'map': Map,\n 'map-pin': MapPin,\n 'map-pinned': MapPinned,\n 'megaphone': Megaphone,\n 'message-circle': MessageCircle,\n 'message-square-reply': MessageSquareReply,\n 'minus-circle': MinusCircle,\n 'monitor': Monitor,\n 'notebook': Notebook,\n 'package': Package,\n 'package-check': PackageCheck,\n 'package-plus': PackagePlus,\n 'package-search': PackageSearch,\n 'package-x': PackageX,\n 'pause-circle': PauseCircle,\n 'pen-line': PenLine,\n 'pencil-line': PencilLine,\n 'percent': Percent,\n 'phone': Phone,\n 'phone-call': PhoneCall,\n 'pie-chart': PieChart,\n 'plus-square': PlusSquare,\n 'radio': Radio,\n 'receipt': Receipt,\n 'receipt-text': ReceiptText,\n 'refresh-ccw': RefreshCcw,\n 'refresh-cw': RefreshCw,\n 'repeat-2': Repeat2,\n 'reply': Reply,\n 'rotate-ccw': RotateCcw,\n 'ruler': Ruler,\n 'send': Send,\n 'settings': Settings,\n 'shapes': Shapes,\n 'share-2': Share2,\n 'shield': Shield,\n 'shield-alert': ShieldAlert,\n 'shield-check': ShieldCheck,\n 'shopping-bag': ShoppingBag,\n 'shopping-cart': ShoppingCart,\n 'shuffle': Shuffle,\n 'sliders': Sliders,\n 'smartphone': Smartphone,\n 'snowflake': Snowflake,\n 'sparkles': Sparkles,\n 'star': Star,\n 'sticky-note': StickyNote,\n 'store': Store,\n 'tag': Tag,\n 'target': Target,\n 'thumbs-up': ThumbsUp,\n 'ticket': Ticket,\n 'timer': Timer,\n 'trash-2': Trash2,\n 'trending-up': TrendingUp,\n 'triangle-alert': TriangleAlert,\n 'trophy': Trophy,\n 'truck': Truck,\n 'undo': Undo,\n 'undo-2': Undo2,\n 'unlock': Unlock,\n 'user': User,\n 'user-check': UserCheck,\n 'user-minus': UserMinus,\n 'user-plus': UserPlus,\n 'user-round': UserRound,\n 'user-x': UserX,\n 'users': Users,\n 'variable': Variable,\n 'wallet': Wallet,\n 'warehouse': Warehouse,\n 'webhook': Webhook,\n 'workflow': Workflow,\n 'wrench': Wrench,\n 'x': X,\n 'x-circle': XCircle,\n 'zap-off': ZapOff,\n};\n"],
5
- "mappings": "AAKA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,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",
4
+ "sourcesContent": ["/* eslint-disable */\n// AUTO-GENERATED by @open-mercato/ui build. Do not edit by hand.\n// Generated from discovered icon string usages across the repo.\n\nimport type { LucideIcon } from 'lucide-react';\nimport {\n Activity,\n AlarmClock,\n AlertCircle,\n AlertOctagon,\n AlertTriangle,\n AlignLeft,\n Apple,\n Archive,\n AtSign,\n Award,\n BadgeCheck,\n Ban,\n Banknote,\n BarChart2,\n BarChart3,\n Bell,\n Bolt,\n Book,\n Bookmark,\n Bot,\n Box,\n Boxes,\n Briefcase,\n BriefcaseBusiness,\n Building,\n Building2,\n Calendar,\n CalendarCheck,\n CalendarClock,\n CalendarCog,\n CalendarMinus,\n CalendarOff,\n CalendarX,\n Check,\n CheckCircle,\n CheckCircle2,\n CheckSquare,\n Circle,\n ClipboardCheck,\n ClipboardList,\n Clock,\n Clock3,\n Coins,\n Copy,\n CopyX,\n CreditCard,\n Crown,\n Database,\n DollarSign,\n Download,\n ExternalLink,\n Eye,\n FileCheck,\n FileClock,\n FileMinus,\n FilePenLine,\n Files,\n FileSpreadsheet,\n FileText,\n FilterX,\n Flag,\n Flame,\n FolderTree,\n FunctionSquare,\n Gauge,\n GitBranch,\n GitCompareArrows,\n GitPullRequestArrow,\n Globe,\n GraduationCap,\n Grid3x3,\n Hammer,\n Hand,\n Handshake,\n Hash,\n Heading,\n Headphones,\n Heart,\n HelpCircle,\n Hourglass,\n Inbox,\n Key,\n KeyRound,\n Layers,\n Leaf,\n Lightbulb,\n LineChart,\n Link,\n List,\n ListChecks,\n ListOrdered,\n ListX,\n Loader,\n Loader2,\n Lock,\n Mail,\n MailOpen,\n Map,\n MapPin,\n MapPinned,\n Megaphone,\n MessageCircle,\n MessageSquareReply,\n MinusCircle,\n Monitor,\n Notebook,\n Package,\n PackageCheck,\n PackagePlus,\n PackageSearch,\n PackageX,\n Paperclip,\n PauseCircle,\n PencilLine,\n PenLine,\n PenTool,\n Percent,\n Phone,\n PhoneCall,\n PieChart,\n PlusSquare,\n Radio,\n Receipt,\n ReceiptText,\n RefreshCcw,\n RefreshCw,\n Repeat2,\n Reply,\n RotateCcw,\n Rows,\n Ruler,\n Search,\n Send,\n Settings,\n Shapes,\n Share2,\n Shield,\n ShieldAlert,\n ShieldCheck,\n ShoppingBag,\n ShoppingCart,\n Shuffle,\n Sliders,\n SlidersHorizontal,\n Smartphone,\n Snowflake,\n Sparkles,\n Star,\n StickyNote,\n Store,\n Tag,\n Target,\n ThumbsUp,\n Ticket,\n Timer,\n ToggleLeft,\n ToggleRight,\n Trash2,\n TrendingUp,\n TriangleAlert,\n Trophy,\n Truck,\n Type,\n Undo,\n Undo2,\n Unlock,\n User,\n UserCheck,\n UserMinus,\n UserPlus,\n UserRound,\n Users,\n UserX,\n Variable,\n Wallet,\n Warehouse,\n Webhook,\n Workflow,\n Wrench,\n X,\n XCircle,\n ZapOff,\n} from 'lucide-react';\n\nexport const LUCIDE_ICON_REGISTRY: Record<string, LucideIcon> = {\n 'activity': Activity,\n 'alarm-clock': AlarmClock,\n 'alert-circle': AlertCircle,\n 'alert-octagon': AlertOctagon,\n 'alert-triangle': AlertTriangle,\n 'align-left': AlignLeft,\n 'apple': Apple,\n 'archive': Archive,\n 'at-sign': AtSign,\n 'award': Award,\n 'badge-check': BadgeCheck,\n 'ban': Ban,\n 'banknote': Banknote,\n 'bar-chart-2': BarChart2,\n 'bar-chart-3': BarChart3,\n 'bell': Bell,\n 'bolt': Bolt,\n 'book': Book,\n 'bookmark': Bookmark,\n 'bot': Bot,\n 'box': Box,\n 'boxes': Boxes,\n 'briefcase': Briefcase,\n 'briefcase-business': BriefcaseBusiness,\n 'building': Building,\n 'building2': Building2,\n 'calendar': Calendar,\n 'calendar-check': CalendarCheck,\n 'calendar-clock': CalendarClock,\n 'calendar-cog': CalendarCog,\n 'calendar-minus': CalendarMinus,\n 'calendar-off': CalendarOff,\n 'calendar-x': CalendarX,\n 'check': Check,\n 'check-circle': CheckCircle,\n 'check-circle-2': CheckCircle2,\n 'check-square': CheckSquare,\n 'circle': Circle,\n 'clipboard-check': ClipboardCheck,\n 'clipboard-list': ClipboardList,\n 'clock': Clock,\n 'clock-3': Clock3,\n 'coins': Coins,\n 'copy': Copy,\n 'copy-x': CopyX,\n 'credit-card': CreditCard,\n 'crown': Crown,\n 'database': Database,\n 'dollar-sign': DollarSign,\n 'download': Download,\n 'external-link': ExternalLink,\n 'eye': Eye,\n 'file-check': FileCheck,\n 'file-clock': FileClock,\n 'file-minus': FileMinus,\n 'file-pen-line': FilePenLine,\n 'file-spreadsheet': FileSpreadsheet,\n 'file-text': FileText,\n 'files': Files,\n 'filter-x': FilterX,\n 'flag': Flag,\n 'flame': Flame,\n 'folder-tree': FolderTree,\n 'function-square': FunctionSquare,\n 'gauge': Gauge,\n 'git-branch': GitBranch,\n 'git-compare-arrows': GitCompareArrows,\n 'git-pull-request-arrow': GitPullRequestArrow,\n 'globe': Globe,\n 'graduation-cap': GraduationCap,\n 'grid-3x3': Grid3x3,\n 'hammer': Hammer,\n 'hand': Hand,\n 'handshake': Handshake,\n 'hash': Hash,\n 'heading': Heading,\n 'headphones': Headphones,\n 'heart': Heart,\n 'help-circle': HelpCircle,\n 'hourglass': Hourglass,\n 'inbox': Inbox,\n 'key': Key,\n 'key-round': KeyRound,\n 'layers': Layers,\n 'leaf': Leaf,\n 'lightbulb': Lightbulb,\n 'line-chart': LineChart,\n 'link': Link,\n 'list': List,\n 'list-checks': ListChecks,\n 'list-ordered': ListOrdered,\n 'list-x': ListX,\n 'loader': Loader,\n 'loader-2': Loader2,\n 'lock': Lock,\n 'mail': Mail,\n 'mail-open': MailOpen,\n 'map': Map,\n 'map-pin': MapPin,\n 'map-pinned': MapPinned,\n 'megaphone': Megaphone,\n 'message-circle': MessageCircle,\n 'message-square-reply': MessageSquareReply,\n 'minus-circle': MinusCircle,\n 'monitor': Monitor,\n 'notebook': Notebook,\n 'package': Package,\n 'package-check': PackageCheck,\n 'package-plus': PackagePlus,\n 'package-search': PackageSearch,\n 'package-x': PackageX,\n 'paperclip': Paperclip,\n 'pause-circle': PauseCircle,\n 'pen-line': PenLine,\n 'pen-tool': PenTool,\n 'pencil-line': PencilLine,\n 'percent': Percent,\n 'phone': Phone,\n 'phone-call': PhoneCall,\n 'pie-chart': PieChart,\n 'plus-square': PlusSquare,\n 'radio': Radio,\n 'receipt': Receipt,\n 'receipt-text': ReceiptText,\n 'refresh-ccw': RefreshCcw,\n 'refresh-cw': RefreshCw,\n 'repeat-2': Repeat2,\n 'reply': Reply,\n 'rotate-ccw': RotateCcw,\n 'rows': Rows,\n 'ruler': Ruler,\n 'search': Search,\n 'send': Send,\n 'settings': Settings,\n 'shapes': Shapes,\n 'share-2': Share2,\n 'shield': Shield,\n 'shield-alert': ShieldAlert,\n 'shield-check': ShieldCheck,\n 'shopping-bag': ShoppingBag,\n 'shopping-cart': ShoppingCart,\n 'shuffle': Shuffle,\n 'sliders': Sliders,\n 'sliders-horizontal': SlidersHorizontal,\n 'smartphone': Smartphone,\n 'snowflake': Snowflake,\n 'sparkles': Sparkles,\n 'star': Star,\n 'sticky-note': StickyNote,\n 'store': Store,\n 'tag': Tag,\n 'target': Target,\n 'thumbs-up': ThumbsUp,\n 'ticket': Ticket,\n 'timer': Timer,\n 'toggle-left': ToggleLeft,\n 'toggle-right': ToggleRight,\n 'trash-2': Trash2,\n 'trending-up': TrendingUp,\n 'triangle-alert': TriangleAlert,\n 'trophy': Trophy,\n 'truck': Truck,\n 'type': Type,\n 'undo': Undo,\n 'undo-2': Undo2,\n 'unlock': Unlock,\n 'user': User,\n 'user-check': UserCheck,\n 'user-minus': UserMinus,\n 'user-plus': UserPlus,\n 'user-round': UserRound,\n 'user-x': UserX,\n 'users': Users,\n 'variable': Variable,\n 'wallet': Wallet,\n 'warehouse': Warehouse,\n 'webhook': Webhook,\n 'workflow': Workflow,\n 'wrench': Wrench,\n 'x': X,\n 'x-circle': XCircle,\n 'zap-off': ZapOff,\n};\n"],
5
+ "mappings": "AAKA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { useEffect, useRef } from "react";
3
+ import { subscribeOrganizationScopeChanged } from "@open-mercato/shared/lib/frontend/organizationEvents";
3
4
  import { APP_EVENT_DOM_NAME } from "./useAppEvent.js";
4
5
  import { createLogger } from "@open-mercato/shared/lib/logger";
5
6
  const logger = createLogger("ui").child({ component: "EventBridge" });
@@ -9,6 +10,20 @@ const RECONNECT_BASE_MS = 1e3;
9
10
  const RECONNECT_MAX_MS = 3e4;
10
11
  const DEDUP_WINDOW_MS = 500;
11
12
  const BRIDGE_RECONNECTED_EVENT_ID = "om:bridge:reconnected";
13
+ const SCOPE_COOKIE_NAMES = ["om_selected_org", "om_selected_tenant"];
14
+ function readScopeCookieSignature() {
15
+ if (typeof document === "undefined") return "";
16
+ const values = /* @__PURE__ */ new Map();
17
+ for (const part of document.cookie.split(";")) {
18
+ const separatorIndex = part.indexOf("=");
19
+ if (separatorIndex < 0) continue;
20
+ const name = part.slice(0, separatorIndex).trim();
21
+ if (SCOPE_COOKIE_NAMES.includes(name)) {
22
+ values.set(name, part.slice(separatorIndex + 1).trim());
23
+ }
24
+ }
25
+ return SCOPE_COOKIE_NAMES.map((name) => `${name}=${values.get(name) ?? ""}`).join(";");
26
+ }
12
27
  function setEventBridgeReady(ready) {
13
28
  if (typeof window === "undefined") return;
14
29
  window.__omEventBridgeReady = ready;
@@ -21,6 +36,7 @@ function useEventBridge() {
21
36
  const recentEvents = useRef(/* @__PURE__ */ new Map());
22
37
  const hasEverConnected = useRef(false);
23
38
  const reconnectPending = useRef(false);
39
+ const connectedScopeSignature = useRef(null);
24
40
  useEffect(() => {
25
41
  let mounted = true;
26
42
  function isPageVisible() {
@@ -53,7 +69,9 @@ function useEventBridge() {
53
69
  try {
54
70
  const source = new EventSource(SSE_ENDPOINT, { withCredentials: true });
55
71
  sourceRef.current = source;
72
+ connectedScopeSignature.current = readScopeCookieSignature();
56
73
  source.onopen = () => {
74
+ if (sourceRef.current !== source) return;
57
75
  const shouldEmitReconnect = hasEverConnected.current && reconnectPending.current;
58
76
  hasEverConnected.current = true;
59
77
  reconnectPending.current = false;
@@ -74,6 +92,7 @@ function useEventBridge() {
74
92
  }
75
93
  };
76
94
  source.onmessage = (event) => {
95
+ if (sourceRef.current !== source) return;
77
96
  resetHeartbeatTimer();
78
97
  if (!event.data || event.data === ":heartbeat") return;
79
98
  try {
@@ -87,6 +106,7 @@ function useEventBridge() {
87
106
  }
88
107
  };
89
108
  source.onerror = () => {
109
+ if (sourceRef.current !== source) return;
90
110
  if (hasEverConnected.current) {
91
111
  reconnectPending.current = true;
92
112
  }
@@ -135,11 +155,24 @@ function useEventBridge() {
135
155
  }
136
156
  connect();
137
157
  }
158
+ function handleOrganizationScopeChange() {
159
+ if (sourceRef.current && connectedScopeSignature.current === readScopeCookieSignature()) return;
160
+ if (hasEverConnected.current) reconnectPending.current = true;
161
+ disconnect();
162
+ if (reconnectTimer.current) {
163
+ clearTimeout(reconnectTimer.current);
164
+ reconnectTimer.current = null;
165
+ }
166
+ reconnectAttempts.current = 0;
167
+ connect();
168
+ }
138
169
  document.addEventListener("visibilitychange", handleVisibilityChange);
170
+ const unsubscribeOrganizationScope = subscribeOrganizationScopeChanged(handleOrganizationScopeChange);
139
171
  connect();
140
172
  return () => {
141
173
  mounted = false;
142
174
  document.removeEventListener("visibilitychange", handleVisibilityChange);
175
+ unsubscribeOrganizationScope();
143
176
  disconnect();
144
177
  if (reconnectTimer.current) {
145
178
  clearTimeout(reconnectTimer.current);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/injection/eventBridge.ts"],
4
- "sourcesContent": ["\"use client\"\nimport { useEffect, useRef } from 'react'\nimport type { AppEventPayload } from '@open-mercato/shared/modules/widgets/injection'\nimport { APP_EVENT_DOM_NAME } from './useAppEvent'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui').child({ component: 'EventBridge' })\n\nconst SSE_ENDPOINT = '/api/events/stream'\nconst HEARTBEAT_TIMEOUT = 45_000 // Expect heartbeat every 30s, allow 45s grace\nconst RECONNECT_BASE_MS = 1_000\nconst RECONNECT_MAX_MS = 30_000\nconst DEDUP_WINDOW_MS = 500\nconst BRIDGE_RECONNECTED_EVENT_ID = 'om:bridge:reconnected'\n\ntype EventBridgeReadyWindow = { __omEventBridgeReady?: boolean }\n\nfunction setEventBridgeReady(ready: boolean): void {\n if (typeof window === 'undefined') return\n ;(window as unknown as EventBridgeReadyWindow).__omEventBridgeReady = ready\n}\n\n/**\n * React hook that establishes a singleton SSE connection to the event bridge.\n *\n * Mount once in the app shell/layout. Receives server-side events with\n * `clientBroadcast: true` and dispatches them as `om:event` CustomEvents\n * on the window object for consumption by `useAppEvent`.\n *\n * Features:\n * - Auto-reconnect with exponential backoff\n * - Deduplication within 500ms window\n * - Heartbeat-based liveness detection\n * - Singleton connection per browser tab\n */\nexport function useEventBridge(): void {\n const sourceRef = useRef<EventSource | null>(null)\n const reconnectAttempts = useRef(0)\n const reconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const heartbeatTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const recentEvents = useRef<Map<string, number>>(new Map())\n const hasEverConnected = useRef(false)\n const reconnectPending = useRef(false)\n\n useEffect(() => {\n let mounted = true\n\n function isPageVisible(): boolean {\n return document.visibilityState !== 'hidden'\n }\n\n function isDuplicate(eventPayload: AppEventPayload): boolean {\n const key = `${eventPayload.id}:${JSON.stringify(eventPayload.payload ?? {})}`\n const lastSeen = recentEvents.current.get(key)\n if (lastSeen && Date.now() - lastSeen < DEDUP_WINDOW_MS) return true\n recentEvents.current.set(key, Date.now())\n // Prune old entries\n if (recentEvents.current.size > 100) {\n const now = Date.now()\n for (const [k, v] of recentEvents.current) {\n if (now - v > DEDUP_WINDOW_MS * 2) recentEvents.current.delete(k)\n }\n }\n return false\n }\n\n function resetHeartbeatTimer() {\n if (heartbeatTimer.current) clearTimeout(heartbeatTimer.current)\n heartbeatTimer.current = setTimeout(() => {\n logger.warn('Heartbeat timeout \u2014 reconnecting')\n disconnect()\n scheduleReconnect()\n }, HEARTBEAT_TIMEOUT)\n }\n\n function connect() {\n if (!mounted || !isPageVisible()) return\n if (sourceRef.current) return\n\n try {\n const source = new EventSource(SSE_ENDPOINT, { withCredentials: true })\n sourceRef.current = source\n\n source.onopen = () => {\n const shouldEmitReconnect = hasEverConnected.current && reconnectPending.current\n hasEverConnected.current = true\n reconnectPending.current = false\n reconnectAttempts.current = 0\n setEventBridgeReady(true)\n resetHeartbeatTimer()\n if (shouldEmitReconnect) {\n window.dispatchEvent(\n new CustomEvent(APP_EVENT_DOM_NAME, {\n detail: {\n id: BRIDGE_RECONNECTED_EVENT_ID,\n payload: {},\n timestamp: Date.now(),\n organizationId: '',\n } satisfies AppEventPayload,\n }),\n )\n }\n }\n\n source.onmessage = (event) => {\n resetHeartbeatTimer()\n if (!event.data || event.data === ':heartbeat') return\n\n try {\n const parsed = JSON.parse(event.data) as AppEventPayload\n if (!parsed.id || typeof parsed.id !== 'string') return\n\n if (isDuplicate(parsed)) return\n\n window.dispatchEvent(\n new CustomEvent(APP_EVENT_DOM_NAME, { detail: parsed }),\n )\n } catch {\n // Ignore malformed events\n }\n }\n\n source.onerror = () => {\n if (hasEverConnected.current) {\n reconnectPending.current = true\n }\n disconnect()\n if (mounted && isPageVisible()) scheduleReconnect()\n }\n } catch {\n if (hasEverConnected.current) {\n reconnectPending.current = true\n }\n if (mounted && isPageVisible()) scheduleReconnect()\n }\n }\n\n function disconnect() {\n setEventBridgeReady(false)\n if (sourceRef.current) {\n sourceRef.current.close()\n sourceRef.current = null\n }\n if (heartbeatTimer.current) {\n clearTimeout(heartbeatTimer.current)\n heartbeatTimer.current = null\n }\n }\n\n function scheduleReconnect() {\n if (reconnectTimer.current) return\n const delay = Math.min(\n RECONNECT_BASE_MS * Math.pow(2, reconnectAttempts.current),\n RECONNECT_MAX_MS,\n )\n reconnectAttempts.current++\n reconnectTimer.current = setTimeout(() => {\n reconnectTimer.current = null\n connect()\n }, delay)\n }\n\n function handleVisibilityChange() {\n if (!isPageVisible()) {\n if (hasEverConnected.current) reconnectPending.current = true\n disconnect()\n if (reconnectTimer.current) {\n clearTimeout(reconnectTimer.current)\n reconnectTimer.current = null\n }\n return\n }\n connect()\n }\n\n document.addEventListener('visibilitychange', handleVisibilityChange)\n connect()\n\n return () => {\n mounted = false\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n disconnect()\n if (reconnectTimer.current) {\n clearTimeout(reconnectTimer.current)\n reconnectTimer.current = null\n }\n }\n }, [])\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport { useEffect, useRef } from 'react'\nimport type { AppEventPayload } from '@open-mercato/shared/modules/widgets/injection'\nimport { subscribeOrganizationScopeChanged } from '@open-mercato/shared/lib/frontend/organizationEvents'\nimport { APP_EVENT_DOM_NAME } from './useAppEvent'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui').child({ component: 'EventBridge' })\n\nconst SSE_ENDPOINT = '/api/events/stream'\nconst HEARTBEAT_TIMEOUT = 45_000 // Expect heartbeat every 30s, allow 45s grace\nconst RECONNECT_BASE_MS = 1_000\nconst RECONNECT_MAX_MS = 30_000\nconst DEDUP_WINDOW_MS = 500\nconst BRIDGE_RECONNECTED_EVENT_ID = 'om:bridge:reconnected'\nconst SCOPE_COOKIE_NAMES = ['om_selected_org', 'om_selected_tenant'] as const\n\nfunction readScopeCookieSignature(): string {\n if (typeof document === 'undefined') return ''\n const values = new Map<string, string>()\n for (const part of document.cookie.split(';')) {\n const separatorIndex = part.indexOf('=')\n if (separatorIndex < 0) continue\n const name = part.slice(0, separatorIndex).trim()\n if ((SCOPE_COOKIE_NAMES as readonly string[]).includes(name)) {\n values.set(name, part.slice(separatorIndex + 1).trim())\n }\n }\n return SCOPE_COOKIE_NAMES.map((name) => `${name}=${values.get(name) ?? ''}`).join(';')\n}\n\ntype EventBridgeReadyWindow = { __omEventBridgeReady?: boolean }\n\nfunction setEventBridgeReady(ready: boolean): void {\n if (typeof window === 'undefined') return\n ;(window as unknown as EventBridgeReadyWindow).__omEventBridgeReady = ready\n}\n\n/**\n * React hook that establishes a singleton SSE connection to the event bridge.\n *\n * Mount once in the app shell/layout. Receives server-side events with\n * `clientBroadcast: true` and dispatches them as `om:event` CustomEvents\n * on the window object for consumption by `useAppEvent`.\n *\n * Features:\n * - Auto-reconnect with exponential backoff\n * - Deduplication within 500ms window\n * - Heartbeat-based liveness detection\n * - Singleton connection per browser tab\n */\nexport function useEventBridge(): void {\n const sourceRef = useRef<EventSource | null>(null)\n const reconnectAttempts = useRef(0)\n const reconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const heartbeatTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const recentEvents = useRef<Map<string, number>>(new Map())\n const hasEverConnected = useRef(false)\n const reconnectPending = useRef(false)\n const connectedScopeSignature = useRef<string | null>(null)\n\n useEffect(() => {\n let mounted = true\n\n function isPageVisible(): boolean {\n return document.visibilityState !== 'hidden'\n }\n\n function isDuplicate(eventPayload: AppEventPayload): boolean {\n const key = `${eventPayload.id}:${JSON.stringify(eventPayload.payload ?? {})}`\n const lastSeen = recentEvents.current.get(key)\n if (lastSeen && Date.now() - lastSeen < DEDUP_WINDOW_MS) return true\n recentEvents.current.set(key, Date.now())\n // Prune old entries\n if (recentEvents.current.size > 100) {\n const now = Date.now()\n for (const [k, v] of recentEvents.current) {\n if (now - v > DEDUP_WINDOW_MS * 2) recentEvents.current.delete(k)\n }\n }\n return false\n }\n\n function resetHeartbeatTimer() {\n if (heartbeatTimer.current) clearTimeout(heartbeatTimer.current)\n heartbeatTimer.current = setTimeout(() => {\n logger.warn('Heartbeat timeout \u2014 reconnecting')\n disconnect()\n scheduleReconnect()\n }, HEARTBEAT_TIMEOUT)\n }\n\n function connect() {\n if (!mounted || !isPageVisible()) return\n if (sourceRef.current) return\n\n try {\n const source = new EventSource(SSE_ENDPOINT, { withCredentials: true })\n sourceRef.current = source\n connectedScopeSignature.current = readScopeCookieSignature()\n\n source.onopen = () => {\n if (sourceRef.current !== source) return\n const shouldEmitReconnect = hasEverConnected.current && reconnectPending.current\n hasEverConnected.current = true\n reconnectPending.current = false\n reconnectAttempts.current = 0\n setEventBridgeReady(true)\n resetHeartbeatTimer()\n if (shouldEmitReconnect) {\n window.dispatchEvent(\n new CustomEvent(APP_EVENT_DOM_NAME, {\n detail: {\n id: BRIDGE_RECONNECTED_EVENT_ID,\n payload: {},\n timestamp: Date.now(),\n organizationId: '',\n } satisfies AppEventPayload,\n }),\n )\n }\n }\n\n source.onmessage = (event) => {\n if (sourceRef.current !== source) return\n resetHeartbeatTimer()\n if (!event.data || event.data === ':heartbeat') return\n\n try {\n const parsed = JSON.parse(event.data) as AppEventPayload\n if (!parsed.id || typeof parsed.id !== 'string') return\n\n if (isDuplicate(parsed)) return\n\n window.dispatchEvent(\n new CustomEvent(APP_EVENT_DOM_NAME, { detail: parsed }),\n )\n } catch {\n // Ignore malformed events\n }\n }\n\n source.onerror = () => {\n if (sourceRef.current !== source) return\n if (hasEverConnected.current) {\n reconnectPending.current = true\n }\n disconnect()\n if (mounted && isPageVisible()) scheduleReconnect()\n }\n } catch {\n if (hasEverConnected.current) {\n reconnectPending.current = true\n }\n if (mounted && isPageVisible()) scheduleReconnect()\n }\n }\n\n function disconnect() {\n setEventBridgeReady(false)\n if (sourceRef.current) {\n sourceRef.current.close()\n sourceRef.current = null\n }\n if (heartbeatTimer.current) {\n clearTimeout(heartbeatTimer.current)\n heartbeatTimer.current = null\n }\n }\n\n function scheduleReconnect() {\n if (reconnectTimer.current) return\n const delay = Math.min(\n RECONNECT_BASE_MS * Math.pow(2, reconnectAttempts.current),\n RECONNECT_MAX_MS,\n )\n reconnectAttempts.current++\n reconnectTimer.current = setTimeout(() => {\n reconnectTimer.current = null\n connect()\n }, delay)\n }\n\n function handleVisibilityChange() {\n if (!isPageVisible()) {\n if (hasEverConnected.current) reconnectPending.current = true\n disconnect()\n if (reconnectTimer.current) {\n clearTimeout(reconnectTimer.current)\n reconnectTimer.current = null\n }\n return\n }\n connect()\n }\n\n function handleOrganizationScopeChange() {\n if (sourceRef.current && connectedScopeSignature.current === readScopeCookieSignature()) return\n if (hasEverConnected.current) reconnectPending.current = true\n disconnect()\n if (reconnectTimer.current) {\n clearTimeout(reconnectTimer.current)\n reconnectTimer.current = null\n }\n reconnectAttempts.current = 0\n connect()\n }\n\n document.addEventListener('visibilitychange', handleVisibilityChange)\n const unsubscribeOrganizationScope = subscribeOrganizationScopeChanged(handleOrganizationScopeChange)\n connect()\n\n return () => {\n mounted = false\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n unsubscribeOrganizationScope()\n disconnect()\n if (reconnectTimer.current) {\n clearTimeout(reconnectTimer.current)\n reconnectTimer.current = null\n }\n }\n }, [])\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -7,6 +7,11 @@ import { Button } from "../../primitives/button.js";
7
7
  import { cn } from "@open-mercato/shared/lib/utils";
8
8
  import { createLogger } from "@open-mercato/shared/lib/logger";
9
9
  const logger = createLogger("ui").child({ component: "LookupSelect" });
10
+ function keepSelectedItem(result, selected, value) {
11
+ if (!value || !selected || selected.id !== value) return result;
12
+ if (result.some((item) => item.id === value)) return result;
13
+ return [selected, ...result];
14
+ }
10
15
  function LookupSelect({
11
16
  value,
12
17
  onChange,
@@ -56,9 +61,14 @@ function LookupSelect({
56
61
  const setQueryRef = React.useRef(setQuery);
57
62
  const onReadyRef = React.useRef(onReady);
58
63
  const optionsWasArrayRef = React.useRef(Array.isArray(options));
64
+ const valueRef = React.useRef(value);
65
+ const selectedItemRef = React.useRef(null);
59
66
  React.useEffect(() => {
60
67
  fetchItemsRef.current = fetchItems ?? fetchOptions;
61
68
  }, [fetchItems, fetchOptions]);
69
+ React.useEffect(() => {
70
+ valueRef.current = value;
71
+ }, [value]);
62
72
  React.useEffect(() => {
63
73
  onReadyRef.current = onReady;
64
74
  }, [onReady]);
@@ -158,9 +168,12 @@ function LookupSelect({
158
168
  const requestId = Date.now();
159
169
  const fetcher = fetchItemsRef.current;
160
170
  const loader = fetcher ?? (() => Promise.resolve(options ?? []));
161
- loader(query.trim()).then((result) => {
171
+ const trimmedQuery = query.trim();
172
+ loader(trimmedQuery).then((result) => {
162
173
  if (cancelled) return;
163
- setItems(result);
174
+ setItems(
175
+ trimmedQuery ? result : keepSelectedItem(result, selectedItemRef.current, valueRef.current)
176
+ );
164
177
  }).catch((err) => {
165
178
  if (cancelled) return;
166
179
  logger.error("Failed to fetch lookup items", { err });
@@ -178,11 +191,15 @@ function LookupSelect({
178
191
  const [selectedItem, setSelectedItem] = React.useState(null);
179
192
  React.useEffect(() => {
180
193
  if (!value) {
194
+ selectedItemRef.current = null;
181
195
  setSelectedItem(null);
182
196
  return;
183
197
  }
184
198
  const match = items.find((item) => item.id === value);
185
- if (match) setSelectedItem(match);
199
+ if (match) {
200
+ selectedItemRef.current = match;
201
+ setSelectedItem(match);
202
+ }
186
203
  }, [items, value]);
187
204
  const collapsedSelectionLabel = React.useMemo(() => {
188
205
  if (!value || !selectedHintLabel) return null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/inputs/LookupSelect.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Check, Loader2, Search, X } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui').child({ component: 'LookupSelect' })\n\nexport type LookupSelectItem = {\n id: string\n title: string\n subtitle?: string | null\n badge?: string | null\n icon?: React.ReactNode\n disabled?: boolean\n rightLabel?: string | null\n description?: string | null\n}\n\ntype LookupSelectProps = {\n value: string | null\n onChange: (next: string | null) => void\n fetchItems?: (query: string) => Promise<LookupSelectItem[]>\n fetchOptions?: (query?: string) => Promise<LookupSelectItem[]>\n options?: LookupSelectItem[]\n minQuery?: number\n actionSlot?: React.ReactNode\n onReady?: (controls: { setQuery: (value: string) => void }) => void\n searchPlaceholder?: string\n placeholder?: string\n clearLabel?: string\n emptyLabel?: string\n loadingLabel?: string\n selectLabel?: string\n selectedLabel?: string\n minQueryHintLabel?: string\n startTypingLabel?: string\n selectedHintLabel?: (id: string) => string\n disabled?: boolean\n loading?: boolean\n defaultOpen?: boolean\n}\n\nexport function LookupSelect({\n value,\n onChange,\n fetchItems,\n fetchOptions,\n options,\n minQuery = 2,\n actionSlot,\n onReady,\n placeholder,\n searchPlaceholder,\n clearLabel,\n emptyLabel,\n loadingLabel,\n selectLabel,\n selectedLabel,\n minQueryHintLabel,\n startTypingLabel,\n selectedHintLabel,\n disabled = false,\n loading: loadingProp = false,\n defaultOpen = false,\n}: LookupSelectProps) {\n const t = useT()\n const resolvedSearchPlaceholder = searchPlaceholder ?? placeholder ?? t('ui.lookupSelect.searchPlaceholder', 'Search\u2026')\n const resolvedClearLabel = clearLabel ?? t('ui.lookupSelect.clearSelection', 'Clear selection')\n const resolvedEmptyLabel = emptyLabel ?? t('ui.lookupSelect.noResults', 'No results')\n const resolvedLoadingLabel = loadingLabel ?? t('ui.lookupSelect.searching', 'Searching\u2026')\n const resolvedSelectLabel = selectLabel ?? t('ui.lookupSelect.select', 'Select')\n const resolvedSelectedLabel = selectedLabel ?? t('ui.lookupSelect.selected', 'Selected')\n const resolvedStartTypingLabel = startTypingLabel ?? t('ui.lookupSelect.startTyping', 'Start typing to search.')\n const resolvedMinQueryHintLabel = minQueryHintLabel ?? t(\n 'ui.lookupSelect.minQueryHint',\n 'Type at least {minQuery} characters or paste an id to search.',\n { minQuery: String(minQuery) }\n )\n const [query, setQuery] = React.useState('')\n const [items, setItems] = React.useState<LookupSelectItem[]>(options ?? [])\n const [loading, setLoading] = React.useState(false)\n // Escape collapses the list without clearing the selection. Derived state is not\n // enough here: Escape empties the query but the fetched `items` linger, so the\n // list would stay open and the collapsed summary \u2014 the branch that keeps a raw\n // record id out of the DOM (TC-EUDR-013) \u2014 would never render.\n const [collapsed, setCollapsed] = React.useState(false)\n const [hasTyped, setHasTyped] = React.useState(defaultOpen)\n const [error, setError] = React.useState<string | null>(null)\n const [fetchKey, setFetchKey] = React.useState(0)\n const [activeIndex, setActiveIndex] = React.useState(-1)\n const listboxId = React.useId()\n const fetchItemsRef = React.useRef(fetchItems ?? fetchOptions)\n const setQueryRef = React.useRef(setQuery)\n const onReadyRef = React.useRef(onReady)\n const optionsWasArrayRef = React.useRef(Array.isArray(options))\n\n React.useEffect(() => {\n fetchItemsRef.current = fetchItems ?? fetchOptions\n }, [fetchItems, fetchOptions])\n\n React.useEffect(() => {\n onReadyRef.current = onReady\n }, [onReady])\n\n React.useEffect(() => {\n if (Array.isArray(options)) {\n optionsWasArrayRef.current = true\n setItems(options)\n } else if (optionsWasArrayRef.current) {\n optionsWasArrayRef.current = false\n setFetchKey((k) => k + 1)\n }\n }, [options])\n\n React.useEffect(() => {\n setQueryRef.current = setQuery\n if (onReadyRef.current) onReadyRef.current({ setQuery })\n }, [setQuery])\n\n // A set `value` always opens the list: it is the only place the selected row,\n // its checkmark and the clear control render, so collapsing over a selection\n // would hide it with no way to see or undo it. This used to also require an\n // `options` prop, which left every caller that resolves its selection through\n // `fetchItems` blind whenever minQuery kept the list shut.\n const shouldSearch = defaultOpen || query.trim().length >= minQuery || Boolean(value)\n\n React.useEffect(() => {\n setActiveIndex(-1)\n }, [items])\n\n const optionDomId = React.useCallback(\n (index: number) => `${listboxId}-option-${index}`,\n [listboxId],\n )\n\n const isInteractiveItem = React.useCallback(\n (item: LookupSelectItem) => !disabled && (!item.disabled || value === item.id),\n [disabled, value],\n )\n\n const moveActiveIndex = React.useCallback((direction: 1 | -1) => {\n setActiveIndex((current) => {\n if (!items.length) return -1\n let next = current\n for (let step = 0; step < items.length; step += 1) {\n next = (next + direction + items.length) % items.length\n if (isInteractiveItem(items[next])) return next\n }\n return current\n })\n }, [isInteractiveItem, items])\n\n React.useEffect(() => {\n if (activeIndex < 0) return\n const activeElement = typeof document !== 'undefined'\n ? document.getElementById(optionDomId(activeIndex))\n : null\n if (typeof activeElement?.scrollIntoView === 'function') {\n activeElement.scrollIntoView({ block: 'nearest' })\n }\n }, [activeIndex, optionDomId])\n\n const listboxVisible = shouldSearch && !disabled\n const handleInputKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLInputElement>) => {\n if (!listboxVisible) return\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n moveActiveIndex(1)\n return\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault()\n moveActiveIndex(-1)\n return\n }\n if (event.key === 'Enter') {\n if (activeIndex < 0 || activeIndex >= items.length) return\n const item = items[activeIndex]\n if (!isInteractiveItem(item)) return\n event.preventDefault()\n onChange(item.id)\n setActiveIndex(-1)\n return\n }\n if (event.key === 'Escape') {\n if (query.length === 0 && activeIndex < 0) return\n event.preventDefault()\n event.stopPropagation()\n setQuery('')\n setActiveIndex(-1)\n setCollapsed(true)\n }\n }, [activeIndex, items, isInteractiveItem, listboxVisible, moveActiveIndex, onChange, query])\n React.useEffect(() => {\n if (disabled) {\n setItems(options ?? [])\n setLoading(false)\n return\n }\n let cancelled = false\n let timer: ReturnType<typeof setTimeout> | null = null\n if (!shouldSearch) {\n setItems(options ?? [])\n setLoading(false)\n setError(null)\n return () => { cancelled = true }\n }\n setLoading(true)\n setError(null)\n timer = setTimeout(() => {\n const requestId = Date.now()\n const fetcher = fetchItemsRef.current\n const loader = fetcher ?? (() => Promise.resolve(options ?? []))\n loader(query.trim())\n .then((result) => {\n if (cancelled) return\n setItems(result)\n })\n .catch((err) => {\n if (cancelled) return\n logger.error('Failed to fetch lookup items', { err })\n setError('error')\n })\n .finally(() => {\n if (!cancelled) setLoading(false)\n })\n return requestId\n }, 220)\n return () => {\n cancelled = true\n if (timer) clearTimeout(timer)\n }\n }, [query, shouldSearch, fetchKey])\n\n const [selectedItem, setSelectedItem] = React.useState<LookupSelectItem | null>(null)\n\n React.useEffect(() => {\n if (!value) {\n setSelectedItem(null)\n return\n }\n const match = items.find((item) => item.id === value)\n if (match) setSelectedItem(match)\n }, [items, value])\n\n /*\n * The collapsed summary is opt-in: only a consumer that supplies\n * `selectedHintLabel` asks this component to display the selection, so\n * consumers rendering their own selected-value label keep their layout and\n * never get a second summary. A resolver that falls back to the id\n * (`known.get(id) ?? id`) is treated as unresolved and falls through to the\n * title of the item that was actually fetched \u2014 a record id must never reach\n * the DOM as a label.\n */\n const collapsedSelectionLabel = React.useMemo(() => {\n if (!value || !selectedHintLabel) return null\n const hinted = selectedHintLabel(value)\n if (hinted && hinted !== value) return hinted\n const fetchedTitle = selectedItem && selectedItem.id === value ? selectedItem.title : null\n return fetchedTitle && fetchedTitle !== value ? fetchedTitle : null\n }, [selectedHintLabel, selectedItem, value])\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <input\n className=\"w-full h-10 rounded-lg border border-input bg-background pl-10 pr-3 text-sm shadow-xs transition-colors outline-none placeholder:text-muted-foreground hover:border-foreground/20 focus-visible:shadow-focus focus-visible:border-brand-violet disabled:bg-bg-disabled disabled:border-border-disabled disabled:text-muted-foreground disabled:cursor-not-allowed\"\n value={query}\n onChange={(event) => {\n setQuery(event.target.value)\n setHasTyped(true)\n setCollapsed(false)\n }}\n onKeyDown={handleInputKeyDown}\n placeholder={resolvedSearchPlaceholder}\n disabled={disabled}\n role=\"combobox\"\n aria-expanded={listboxVisible}\n aria-controls={listboxId}\n aria-autocomplete=\"list\"\n aria-activedescendant={activeIndex >= 0 ? optionDomId(activeIndex) : undefined}\n />\n </div>\n {actionSlot && !disabled ? <div className=\"sm:self-start\">{actionSlot}</div> : null}\n </div>\n {/*\n * `shouldSearch` decides whether to FETCH; this decides whether to RENDER a\n * list. They differ for a set value whose lookup comes back empty: develop's\n * #5248 work needs the list (and its selected option) whenever items exist,\n * while the collapsed summary \u2014 which keeps a selection visible after the\n * search box reverts to its placeholder, and keeps a raw record id out of the\n * DOM (TC-EUDR-013) \u2014 is only reachable when they do not. Falling through on\n * an empty result satisfies both. A non-empty query still renders the list so\n * its spinner and empty-state are reachable while the user is searching.\n */}\n {shouldSearch && !collapsed && (items.length > 0 || query.trim().length > 0) ? (\n <div className=\"space-y-2\">\n {loading || loadingProp ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n {resolvedLoadingLabel}\n </div>\n ) : null}\n {!loading && !loadingProp && !items.length ? (\n <p className=\"text-xs text-muted-foreground\">{resolvedEmptyLabel}</p>\n ) : null}\n <div\n id={listboxId}\n role=\"listbox\"\n className=\"flex flex-col gap-1.5 max-h-80 overflow-y-auto -mx-0.5 px-0.5 py-0.5\"\n >\n {items.map((item, index) => {\n const isSelected = value === item.id\n const isInteractive = isInteractiveItem(item)\n const isActive = index === activeIndex\n return (\n <div\n key={item.id}\n id={optionDomId(index)}\n className={cn(\n 'group flex items-center gap-4 rounded-xl border p-4 transition-all duration-150 focus-visible:outline-none focus-visible:shadow-focus',\n isInteractive ? 'cursor-pointer' : 'cursor-not-allowed opacity-60',\n isSelected\n ? 'border-brand-violet bg-brand-violet/5 shadow-sm'\n : 'border-input bg-card hover:border-foreground/20 hover:bg-muted/30 hover:shadow-sm',\n isActive && !isSelected ? 'border-foreground/20 bg-muted/30 shadow-sm' : null\n )}\n role=\"option\"\n tabIndex={isInteractive ? 0 : -1}\n onClick={() => {\n if (!isInteractive) return\n onChange(item.id)\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n if (!isInteractive) return\n onChange(item.id)\n }\n }}\n aria-selected={isSelected}\n aria-disabled={isInteractive ? undefined : true}\n title={isSelected ? resolvedSelectedLabel : isInteractive ? resolvedSelectLabel : undefined}\n >\n {item.icon ? (\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden [&>svg]:size-6 [&_svg]:text-muted-foreground\">\n {item.icon}\n </div>\n ) : (\n <div className={cn(\n 'flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-lg border transition-colors',\n isSelected\n ? 'border-brand-violet/40 bg-brand-violet/10 text-brand-violet'\n : 'border-input bg-muted text-muted-foreground group-hover:border-foreground/20'\n )}>\n <span className=\"text-base font-semibold uppercase\">{item.title.slice(0, 1)}</span>\n </div>\n )}\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"truncate text-sm font-semibold text-foreground\">{item.title}</div>\n {item.rightLabel ? (\n <div className=\"shrink-0 text-overline font-medium uppercase tracking-wider text-muted-foreground\">\n {item.rightLabel}\n </div>\n ) : null}\n </div>\n {item.subtitle ? (\n <div className=\"text-xs text-muted-foreground truncate\">{item.subtitle}</div>\n ) : null}\n {item.description ? (\n <div className=\"text-xs text-muted-foreground/70 truncate\">{item.description}</div>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center justify-center\">\n {isSelected ? (\n <Check className=\"size-5 text-brand-violet\" aria-hidden=\"true\" />\n ) : (\n <div className=\"size-5\" aria-hidden=\"true\" />\n )}\n </div>\n </div>\n )\n })}\n </div>\n {value && !disabled ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-fit gap-1 text-sm font-normal\"\n onClick={() => onChange(null)}\n >\n <X className=\"h-4 w-4\" />\n {resolvedClearLabel}\n </Button>\n ) : null}\n </div>\n ) : collapsedSelectionLabel ? (\n /*\n * The visible input is the *search box*, not a value display: once the\n * list closes it shows its placeholder again, so a selection made and\n * then collapsed left the control looking empty even though the form\n * held the id. `selectedHintLabel` existed for exactly this and was\n * never rendered.\n */\n <div\n className=\"flex items-center justify-between gap-3 rounded-lg border border-input bg-muted/40 px-3 py-2\"\n data-testid=\"lookup-select-selected\"\n >\n <span className=\"truncate text-sm font-medium text-foreground\">\n {collapsedSelectionLabel}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"shrink-0 gap-1 text-sm font-normal\"\n disabled={disabled}\n onClick={() => onChange(null)}\n >\n <X className=\"h-4 w-4\" />\n {resolvedClearLabel}\n </Button>\n </div>\n ) : hasTyped ? (\n <p className=\"text-xs text-muted-foreground\">\n {resolvedMinQueryHintLabel}\n </p>\n ) : (\n <p className=\"text-xs text-muted-foreground\">{resolvedStartTypingLabel}</p>\n )}\n {error ? <p className=\"text-xs text-status-error-text\" role=\"alert\">{resolvedEmptyLabel}</p> : null}\n </div>\n )\n}\n"],
5
- "mappings": ";AA6QQ,SACE,KADF;AA3QR,YAAY,WAAW;AACvB,SAAS,OAAO,SAAS,QAAQ,SAAS;AAC1C,SAAS,YAAY;AACrB,SAAS,cAAc;AACvB,SAAS,UAAU;AACnB,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,IAAI,EAAE,MAAM,EAAE,WAAW,eAAe,CAAC;AAqC9D,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,SAAS,cAAc;AAAA,EACvB,cAAc;AAChB,GAAsB;AACpB,QAAM,IAAI,KAAK;AACf,QAAM,4BAA4B,qBAAqB,eAAe,EAAE,qCAAqC,cAAS;AACtH,QAAM,qBAAqB,cAAc,EAAE,kCAAkC,iBAAiB;AAC9F,QAAM,qBAAqB,cAAc,EAAE,6BAA6B,YAAY;AACpF,QAAM,uBAAuB,gBAAgB,EAAE,6BAA6B,iBAAY;AACxF,QAAM,sBAAsB,eAAe,EAAE,0BAA0B,QAAQ;AAC/E,QAAM,wBAAwB,iBAAiB,EAAE,4BAA4B,UAAU;AACvF,QAAM,2BAA2B,oBAAoB,EAAE,+BAA+B,yBAAyB;AAC/G,QAAM,4BAA4B,qBAAqB;AAAA,IACrD;AAAA,IACA;AAAA,IACA,EAAE,UAAU,OAAO,QAAQ,EAAE;AAAA,EAC/B;AACA,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,EAAE;AAC3C,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAA6B,WAAW,CAAC,CAAC;AAC1E,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAKlD,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,KAAK;AACtD,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,WAAW;AAC1D,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,CAAC;AAChD,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,EAAE;AACvD,QAAM,YAAY,MAAM,MAAM;AAC9B,QAAM,gBAAgB,MAAM,OAAO,cAAc,YAAY;AAC7D,QAAM,cAAc,MAAM,OAAO,QAAQ;AACzC,QAAM,aAAa,MAAM,OAAO,OAAO;AACvC,QAAM,qBAAqB,MAAM,OAAO,MAAM,QAAQ,OAAO,CAAC;AAE9D,QAAM,UAAU,MAAM;AACpB,kBAAc,UAAU,cAAc;AAAA,EACxC,GAAG,CAAC,YAAY,YAAY,CAAC;AAE7B,QAAM,UAAU,MAAM;AACpB,eAAW,UAAU;AAAA,EACvB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,UAAU,MAAM;AACpB,QAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,yBAAmB,UAAU;AAC7B,eAAS,OAAO;AAAA,IAClB,WAAW,mBAAmB,SAAS;AACrC,yBAAmB,UAAU;AAC7B,kBAAY,CAAC,MAAM,IAAI,CAAC;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,UAAU,MAAM;AACpB,gBAAY,UAAU;AACtB,QAAI,WAAW,QAAS,YAAW,QAAQ,EAAE,SAAS,CAAC;AAAA,EACzD,GAAG,CAAC,QAAQ,CAAC;AAOb,QAAM,eAAe,eAAe,MAAM,KAAK,EAAE,UAAU,YAAY,QAAQ,KAAK;AAEpF,QAAM,UAAU,MAAM;AACpB,mBAAe,EAAE;AAAA,EACnB,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,cAAc,MAAM;AAAA,IACxB,CAAC,UAAkB,GAAG,SAAS,WAAW,KAAK;AAAA,IAC/C,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,oBAAoB,MAAM;AAAA,IAC9B,CAAC,SAA2B,CAAC,aAAa,CAAC,KAAK,YAAY,UAAU,KAAK;AAAA,IAC3E,CAAC,UAAU,KAAK;AAAA,EAClB;AAEA,QAAM,kBAAkB,MAAM,YAAY,CAAC,cAAsB;AAC/D,mBAAe,CAAC,YAAY;AAC1B,UAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,UAAI,OAAO;AACX,eAAS,OAAO,GAAG,OAAO,MAAM,QAAQ,QAAQ,GAAG;AACjD,gBAAQ,OAAO,YAAY,MAAM,UAAU,MAAM;AACjD,YAAI,kBAAkB,MAAM,IAAI,CAAC,EAAG,QAAO;AAAA,MAC7C;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,mBAAmB,KAAK,CAAC;AAE7B,QAAM,UAAU,MAAM;AACpB,QAAI,cAAc,EAAG;AACrB,UAAM,gBAAgB,OAAO,aAAa,cACtC,SAAS,eAAe,YAAY,WAAW,CAAC,IAChD;AACJ,QAAI,OAAO,eAAe,mBAAmB,YAAY;AACvD,oBAAc,eAAe,EAAE,OAAO,UAAU,CAAC;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,aAAa,WAAW,CAAC;AAE7B,QAAM,iBAAiB,gBAAgB,CAAC;AACxC,QAAM,qBAAqB,MAAM,YAAY,CAAC,UAAiD;AAC7F,QAAI,CAAC,eAAgB;AACrB,QAAI,MAAM,QAAQ,aAAa;AAC7B,YAAM,eAAe;AACrB,sBAAgB,CAAC;AACjB;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,WAAW;AAC3B,YAAM,eAAe;AACrB,sBAAgB,EAAE;AAClB;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,SAAS;AACzB,UAAI,cAAc,KAAK,eAAe,MAAM,OAAQ;AACpD,YAAM,OAAO,MAAM,WAAW;AAC9B,UAAI,CAAC,kBAAkB,IAAI,EAAG;AAC9B,YAAM,eAAe;AACrB,eAAS,KAAK,EAAE;AAChB,qBAAe,EAAE;AACjB;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,UAAU;AAC1B,UAAI,MAAM,WAAW,KAAK,cAAc,EAAG;AAC3C,YAAM,eAAe;AACrB,YAAM,gBAAgB;AACtB,eAAS,EAAE;AACX,qBAAe,EAAE;AACjB,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,aAAa,OAAO,mBAAmB,gBAAgB,iBAAiB,UAAU,KAAK,CAAC;AAC5F,QAAM,UAAU,MAAM;AACpB,QAAI,UAAU;AACZ,eAAS,WAAW,CAAC,CAAC;AACtB,iBAAW,KAAK;AAChB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,QAAI,QAA8C;AAClD,QAAI,CAAC,cAAc;AACjB,eAAS,WAAW,CAAC,CAAC;AACtB,iBAAW,KAAK;AAChB,eAAS,IAAI;AACb,aAAO,MAAM;AAAE,oBAAY;AAAA,MAAK;AAAA,IAClC;AACA,eAAW,IAAI;AACf,aAAS,IAAI;AACb,YAAQ,WAAW,MAAM;AACvB,YAAM,YAAY,KAAK,IAAI;AAC3B,YAAM,UAAU,cAAc;AAC9B,YAAM,SAAS,YAAY,MAAM,QAAQ,QAAQ,WAAW,CAAC,CAAC;AAC9D,aAAO,MAAM,KAAK,CAAC,EAChB,KAAK,CAAC,WAAW;AAChB,YAAI,UAAW;AACf,iBAAS,MAAM;AAAA,MACjB,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,YAAI,UAAW;AACf,eAAO,MAAM,gCAAgC,EAAE,IAAI,CAAC;AACpD,iBAAS,OAAO;AAAA,MAClB,CAAC,EACA,QAAQ,MAAM;AACb,YAAI,CAAC,UAAW,YAAW,KAAK;AAAA,MAClC,CAAC;AACH,aAAO;AAAA,IACT,GAAG,GAAG;AACN,WAAO,MAAM;AACX,kBAAY;AACZ,UAAI,MAAO,cAAa,KAAK;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,OAAO,cAAc,QAAQ,CAAC;AAElC,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAkC,IAAI;AAEpF,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,OAAO;AACV,sBAAgB,IAAI;AACpB;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,KAAK;AACpD,QAAI,MAAO,iBAAgB,KAAK;AAAA,EAClC,GAAG,CAAC,OAAO,KAAK,CAAC;AAWjB,QAAM,0BAA0B,MAAM,QAAQ,MAAM;AAClD,QAAI,CAAC,SAAS,CAAC,kBAAmB,QAAO;AACzC,UAAM,SAAS,kBAAkB,KAAK;AACtC,QAAI,UAAU,WAAW,MAAO,QAAO;AACvC,UAAM,eAAe,gBAAgB,aAAa,OAAO,QAAQ,aAAa,QAAQ;AACtF,WAAO,gBAAgB,iBAAiB,QAAQ,eAAe;AAAA,EACjE,GAAG,CAAC,mBAAmB,cAAc,KAAK,CAAC;AAE3C,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,4DACb;AAAA,2BAAC,SAAI,WAAU,mBACb;AAAA,4BAAC,UAAO,WAAU,gGAA+F;AAAA,QACjH;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,YACP,UAAU,CAAC,UAAU;AACnB,uBAAS,MAAM,OAAO,KAAK;AAC3B,0BAAY,IAAI;AAChB,2BAAa,KAAK;AAAA,YACpB;AAAA,YACA,WAAW;AAAA,YACX,aAAa;AAAA,YACb;AAAA,YACA,MAAK;AAAA,YACL,iBAAe;AAAA,YACf,iBAAe;AAAA,YACf,qBAAkB;AAAA,YAClB,yBAAuB,eAAe,IAAI,YAAY,WAAW,IAAI;AAAA;AAAA,QACvE;AAAA,SACF;AAAA,MACC,cAAc,CAAC,WAAW,oBAAC,SAAI,WAAU,iBAAiB,sBAAW,IAAS;AAAA,OACjF;AAAA,IAWC,gBAAgB,CAAC,cAAc,MAAM,SAAS,KAAK,MAAM,KAAK,EAAE,SAAS,KACxE,qBAAC,SAAI,WAAU,aACZ;AAAA,iBAAW,cACV,qBAAC,SAAI,WAAU,yDACb;AAAA,4BAAC,WAAQ,WAAU,wBAAuB;AAAA,QACzC;AAAA,SACH,IACE;AAAA,MACH,CAAC,WAAW,CAAC,eAAe,CAAC,MAAM,SAClC,oBAAC,OAAE,WAAU,iCAAiC,8BAAmB,IAC/D;AAAA,MACJ;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ,MAAK;AAAA,UACL,WAAU;AAAA,UAET,gBAAM,IAAI,CAAC,MAAM,UAAU;AAC1B,kBAAM,aAAa,UAAU,KAAK;AAClC,kBAAM,gBAAgB,kBAAkB,IAAI;AAC5C,kBAAM,WAAW,UAAU;AAC3B,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,IAAI,YAAY,KAAK;AAAA,gBACrB,WAAW;AAAA,kBACT;AAAA,kBACA,gBAAgB,mBAAmB;AAAA,kBACnC,aACI,oDACA;AAAA,kBACJ,YAAY,CAAC,aAAa,+CAA+C;AAAA,gBAC3E;AAAA,gBACA,MAAK;AAAA,gBACL,UAAU,gBAAgB,IAAI;AAAA,gBAC9B,SAAS,MAAM;AACb,sBAAI,CAAC,cAAe;AACpB,2BAAS,KAAK,EAAE;AAAA,gBAClB;AAAA,gBACA,WAAW,CAAC,UAAU;AACpB,sBAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,0BAAM,eAAe;AACrB,wBAAI,CAAC,cAAe;AACpB,6BAAS,KAAK,EAAE;AAAA,kBAClB;AAAA,gBACF;AAAA,gBACA,iBAAe;AAAA,gBACf,iBAAe,gBAAgB,SAAY;AAAA,gBAC3C,OAAO,aAAa,wBAAwB,gBAAgB,sBAAsB;AAAA,gBAEjF;AAAA,uBAAK,OACJ,oBAAC,SAAI,WAAU,oHACZ,eAAK,MACR,IAEA,oBAAC,SAAI,WAAW;AAAA,oBACd;AAAA,oBACA,aACI,gEACA;AAAA,kBACN,GACE,8BAAC,UAAK,WAAU,qCAAqC,eAAK,MAAM,MAAM,GAAG,CAAC,GAAE,GAC9E;AAAA,kBAEF,qBAAC,SAAI,WAAU,wCACb;AAAA,yCAAC,SAAI,WAAU,2CACb;AAAA,0CAAC,SAAI,WAAU,kDAAkD,eAAK,OAAM;AAAA,sBAC3E,KAAK,aACJ,oBAAC,SAAI,WAAU,qFACZ,eAAK,YACR,IACE;AAAA,uBACN;AAAA,oBACC,KAAK,WACJ,oBAAC,SAAI,WAAU,0CAA0C,eAAK,UAAS,IACrE;AAAA,oBACH,KAAK,cACJ,oBAAC,SAAI,WAAU,6CAA6C,eAAK,aAAY,IAC3E;AAAA,qBACN;AAAA,kBACA,oBAAC,SAAI,WAAU,6CACZ,uBACC,oBAAC,SAAM,WAAU,4BAA2B,eAAY,QAAO,IAE/D,oBAAC,SAAI,WAAU,UAAS,eAAY,QAAO,GAE/C;AAAA;AAAA;AAAA,cA/DK,KAAK;AAAA,YAgEZ;AAAA,UAEJ,CAAC;AAAA;AAAA,MACH;AAAA,MACC,SAAS,CAAC,WACT;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,SAAS,IAAI;AAAA,UAE5B;AAAA,gCAAC,KAAE,WAAU,WAAU;AAAA,YACtB;AAAA;AAAA;AAAA,MACH,IACE;AAAA,OACN,IACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQF;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,eAAY;AAAA,UAEZ;AAAA,gCAAC,UAAK,WAAU,gDACb,mCACH;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV;AAAA,gBACA,SAAS,MAAM,SAAS,IAAI;AAAA,gBAE5B;AAAA,sCAAC,KAAE,WAAU,WAAU;AAAA,kBACtB;AAAA;AAAA;AAAA,YACH;AAAA;AAAA;AAAA,MACF;AAAA,QACE,WACF,oBAAC,OAAE,WAAU,iCACV,qCACH,IAEA,oBAAC,OAAE,WAAU,iCAAiC,oCAAyB;AAAA,IAExE,QAAQ,oBAAC,OAAE,WAAU,kCAAiC,MAAK,SAAS,8BAAmB,IAAO;AAAA,KACjG;AAEJ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Check, Loader2, Search, X } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui').child({ component: 'LookupSelect' })\n\nexport type LookupSelectItem = {\n id: string\n title: string\n subtitle?: string | null\n badge?: string | null\n icon?: React.ReactNode\n disabled?: boolean\n rightLabel?: string | null\n description?: string | null\n}\n\ntype LookupSelectProps = {\n value: string | null\n onChange: (next: string | null) => void\n fetchItems?: (query: string) => Promise<LookupSelectItem[]>\n fetchOptions?: (query?: string) => Promise<LookupSelectItem[]>\n options?: LookupSelectItem[]\n minQuery?: number\n actionSlot?: React.ReactNode\n onReady?: (controls: { setQuery: (value: string) => void }) => void\n searchPlaceholder?: string\n placeholder?: string\n clearLabel?: string\n emptyLabel?: string\n loadingLabel?: string\n selectLabel?: string\n selectedLabel?: string\n minQueryHintLabel?: string\n startTypingLabel?: string\n selectedHintLabel?: (id: string) => string\n disabled?: boolean\n loading?: boolean\n defaultOpen?: boolean\n}\n\nfunction keepSelectedItem(\n result: LookupSelectItem[],\n selected: LookupSelectItem | null,\n value: string | null,\n): LookupSelectItem[] {\n if (!value || !selected || selected.id !== value) return result\n if (result.some((item) => item.id === value)) return result\n return [selected, ...result]\n}\n\nexport function LookupSelect({\n value,\n onChange,\n fetchItems,\n fetchOptions,\n options,\n minQuery = 2,\n actionSlot,\n onReady,\n placeholder,\n searchPlaceholder,\n clearLabel,\n emptyLabel,\n loadingLabel,\n selectLabel,\n selectedLabel,\n minQueryHintLabel,\n startTypingLabel,\n selectedHintLabel,\n disabled = false,\n loading: loadingProp = false,\n defaultOpen = false,\n}: LookupSelectProps) {\n const t = useT()\n const resolvedSearchPlaceholder = searchPlaceholder ?? placeholder ?? t('ui.lookupSelect.searchPlaceholder', 'Search\u2026')\n const resolvedClearLabel = clearLabel ?? t('ui.lookupSelect.clearSelection', 'Clear selection')\n const resolvedEmptyLabel = emptyLabel ?? t('ui.lookupSelect.noResults', 'No results')\n const resolvedLoadingLabel = loadingLabel ?? t('ui.lookupSelect.searching', 'Searching\u2026')\n const resolvedSelectLabel = selectLabel ?? t('ui.lookupSelect.select', 'Select')\n const resolvedSelectedLabel = selectedLabel ?? t('ui.lookupSelect.selected', 'Selected')\n const resolvedStartTypingLabel = startTypingLabel ?? t('ui.lookupSelect.startTyping', 'Start typing to search.')\n const resolvedMinQueryHintLabel = minQueryHintLabel ?? t(\n 'ui.lookupSelect.minQueryHint',\n 'Type at least {minQuery} characters or paste an id to search.',\n { minQuery: String(minQuery) }\n )\n const [query, setQuery] = React.useState('')\n const [items, setItems] = React.useState<LookupSelectItem[]>(options ?? [])\n const [loading, setLoading] = React.useState(false)\n // Escape collapses the list without clearing the selection. Derived state is not\n // enough here: Escape empties the query but the fetched `items` linger, so the\n // list would stay open and the collapsed summary \u2014 the branch that keeps a raw\n // record id out of the DOM (TC-EUDR-013) \u2014 would never render.\n const [collapsed, setCollapsed] = React.useState(false)\n const [hasTyped, setHasTyped] = React.useState(defaultOpen)\n const [error, setError] = React.useState<string | null>(null)\n const [fetchKey, setFetchKey] = React.useState(0)\n const [activeIndex, setActiveIndex] = React.useState(-1)\n const listboxId = React.useId()\n const fetchItemsRef = React.useRef(fetchItems ?? fetchOptions)\n const setQueryRef = React.useRef(setQuery)\n const onReadyRef = React.useRef(onReady)\n const optionsWasArrayRef = React.useRef(Array.isArray(options))\n const valueRef = React.useRef(value)\n const selectedItemRef = React.useRef<LookupSelectItem | null>(null)\n\n React.useEffect(() => {\n fetchItemsRef.current = fetchItems ?? fetchOptions\n }, [fetchItems, fetchOptions])\n\n React.useEffect(() => {\n valueRef.current = value\n }, [value])\n\n React.useEffect(() => {\n onReadyRef.current = onReady\n }, [onReady])\n\n React.useEffect(() => {\n if (Array.isArray(options)) {\n optionsWasArrayRef.current = true\n setItems(options)\n } else if (optionsWasArrayRef.current) {\n optionsWasArrayRef.current = false\n setFetchKey((k) => k + 1)\n }\n }, [options])\n\n React.useEffect(() => {\n setQueryRef.current = setQuery\n if (onReadyRef.current) onReadyRef.current({ setQuery })\n }, [setQuery])\n\n // A set `value` always opens the list: it is the only place the selected row,\n // its checkmark and the clear control render, so collapsing over a selection\n // would hide it with no way to see or undo it. This used to also require an\n // `options` prop, which left every caller that resolves its selection through\n // `fetchItems` blind whenever minQuery kept the list shut.\n const shouldSearch = defaultOpen || query.trim().length >= minQuery || Boolean(value)\n\n React.useEffect(() => {\n setActiveIndex(-1)\n }, [items])\n\n const optionDomId = React.useCallback(\n (index: number) => `${listboxId}-option-${index}`,\n [listboxId],\n )\n\n const isInteractiveItem = React.useCallback(\n (item: LookupSelectItem) => !disabled && (!item.disabled || value === item.id),\n [disabled, value],\n )\n\n const moveActiveIndex = React.useCallback((direction: 1 | -1) => {\n setActiveIndex((current) => {\n if (!items.length) return -1\n let next = current\n for (let step = 0; step < items.length; step += 1) {\n next = (next + direction + items.length) % items.length\n if (isInteractiveItem(items[next])) return next\n }\n return current\n })\n }, [isInteractiveItem, items])\n\n React.useEffect(() => {\n if (activeIndex < 0) return\n const activeElement = typeof document !== 'undefined'\n ? document.getElementById(optionDomId(activeIndex))\n : null\n if (typeof activeElement?.scrollIntoView === 'function') {\n activeElement.scrollIntoView({ block: 'nearest' })\n }\n }, [activeIndex, optionDomId])\n\n const listboxVisible = shouldSearch && !disabled\n const handleInputKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLInputElement>) => {\n if (!listboxVisible) return\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n moveActiveIndex(1)\n return\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault()\n moveActiveIndex(-1)\n return\n }\n if (event.key === 'Enter') {\n if (activeIndex < 0 || activeIndex >= items.length) return\n const item = items[activeIndex]\n if (!isInteractiveItem(item)) return\n event.preventDefault()\n onChange(item.id)\n setActiveIndex(-1)\n return\n }\n if (event.key === 'Escape') {\n if (query.length === 0 && activeIndex < 0) return\n event.preventDefault()\n event.stopPropagation()\n setQuery('')\n setActiveIndex(-1)\n setCollapsed(true)\n }\n }, [activeIndex, items, isInteractiveItem, listboxVisible, moveActiveIndex, onChange, query])\n React.useEffect(() => {\n if (disabled) {\n setItems(options ?? [])\n setLoading(false)\n return\n }\n let cancelled = false\n let timer: ReturnType<typeof setTimeout> | null = null\n if (!shouldSearch) {\n setItems(options ?? [])\n setLoading(false)\n setError(null)\n return () => { cancelled = true }\n }\n setLoading(true)\n setError(null)\n timer = setTimeout(() => {\n const requestId = Date.now()\n const fetcher = fetchItemsRef.current\n const loader = fetcher ?? (() => Promise.resolve(options ?? []))\n const trimmedQuery = query.trim()\n loader(trimmedQuery)\n .then((result) => {\n if (cancelled) return\n // A set `value` opens the list with an empty query, and that browse\n // fetch returns an arbitrary first page. Without this the stored\n // selection vanished from the list whenever it fell outside that page,\n // while the form still held its id. A typed query stays a pure search.\n setItems(\n trimmedQuery\n ? result\n : keepSelectedItem(result, selectedItemRef.current, valueRef.current),\n )\n })\n .catch((err) => {\n if (cancelled) return\n logger.error('Failed to fetch lookup items', { err })\n setError('error')\n })\n .finally(() => {\n if (!cancelled) setLoading(false)\n })\n return requestId\n }, 220)\n return () => {\n cancelled = true\n if (timer) clearTimeout(timer)\n }\n }, [query, shouldSearch, fetchKey])\n\n const [selectedItem, setSelectedItem] = React.useState<LookupSelectItem | null>(null)\n\n React.useEffect(() => {\n if (!value) {\n selectedItemRef.current = null\n setSelectedItem(null)\n return\n }\n const match = items.find((item) => item.id === value)\n if (match) {\n selectedItemRef.current = match\n setSelectedItem(match)\n }\n }, [items, value])\n\n /*\n * The collapsed summary is opt-in: only a consumer that supplies\n * `selectedHintLabel` asks this component to display the selection, so\n * consumers rendering their own selected-value label keep their layout and\n * never get a second summary. A resolver that falls back to the id\n * (`known.get(id) ?? id`) is treated as unresolved and falls through to the\n * title of the item that was actually fetched \u2014 a record id must never reach\n * the DOM as a label.\n */\n const collapsedSelectionLabel = React.useMemo(() => {\n if (!value || !selectedHintLabel) return null\n const hinted = selectedHintLabel(value)\n if (hinted && hinted !== value) return hinted\n const fetchedTitle = selectedItem && selectedItem.id === value ? selectedItem.title : null\n return fetchedTitle && fetchedTitle !== value ? fetchedTitle : null\n }, [selectedHintLabel, selectedItem, value])\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <input\n className=\"w-full h-10 rounded-lg border border-input bg-background pl-10 pr-3 text-sm shadow-xs transition-colors outline-none placeholder:text-muted-foreground hover:border-foreground/20 focus-visible:shadow-focus focus-visible:border-brand-violet disabled:bg-bg-disabled disabled:border-border-disabled disabled:text-muted-foreground disabled:cursor-not-allowed\"\n value={query}\n onChange={(event) => {\n setQuery(event.target.value)\n setHasTyped(true)\n setCollapsed(false)\n }}\n onKeyDown={handleInputKeyDown}\n placeholder={resolvedSearchPlaceholder}\n disabled={disabled}\n role=\"combobox\"\n aria-expanded={listboxVisible}\n aria-controls={listboxId}\n aria-autocomplete=\"list\"\n aria-activedescendant={activeIndex >= 0 ? optionDomId(activeIndex) : undefined}\n />\n </div>\n {actionSlot && !disabled ? <div className=\"sm:self-start\">{actionSlot}</div> : null}\n </div>\n {/*\n * `shouldSearch` decides whether to FETCH; this decides whether to RENDER a\n * list. They differ for a set value whose lookup comes back empty: develop's\n * #5248 work needs the list (and its selected option) whenever items exist,\n * while the collapsed summary \u2014 which keeps a selection visible after the\n * search box reverts to its placeholder, and keeps a raw record id out of the\n * DOM (TC-EUDR-013) \u2014 is only reachable when they do not. Falling through on\n * an empty result satisfies both. A non-empty query still renders the list so\n * its spinner and empty-state are reachable while the user is searching.\n */}\n {shouldSearch && !collapsed && (items.length > 0 || query.trim().length > 0) ? (\n <div className=\"space-y-2\">\n {loading || loadingProp ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n {resolvedLoadingLabel}\n </div>\n ) : null}\n {!loading && !loadingProp && !items.length ? (\n <p className=\"text-xs text-muted-foreground\">{resolvedEmptyLabel}</p>\n ) : null}\n <div\n id={listboxId}\n role=\"listbox\"\n className=\"flex flex-col gap-1.5 max-h-80 overflow-y-auto -mx-0.5 px-0.5 py-0.5\"\n >\n {items.map((item, index) => {\n const isSelected = value === item.id\n const isInteractive = isInteractiveItem(item)\n const isActive = index === activeIndex\n return (\n <div\n key={item.id}\n id={optionDomId(index)}\n className={cn(\n 'group flex items-center gap-4 rounded-xl border p-4 transition-all duration-150 focus-visible:outline-none focus-visible:shadow-focus',\n isInteractive ? 'cursor-pointer' : 'cursor-not-allowed opacity-60',\n isSelected\n ? 'border-brand-violet bg-brand-violet/5 shadow-sm'\n : 'border-input bg-card hover:border-foreground/20 hover:bg-muted/30 hover:shadow-sm',\n isActive && !isSelected ? 'border-foreground/20 bg-muted/30 shadow-sm' : null\n )}\n role=\"option\"\n tabIndex={isInteractive ? 0 : -1}\n onClick={() => {\n if (!isInteractive) return\n onChange(item.id)\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n if (!isInteractive) return\n onChange(item.id)\n }\n }}\n aria-selected={isSelected}\n aria-disabled={isInteractive ? undefined : true}\n title={isSelected ? resolvedSelectedLabel : isInteractive ? resolvedSelectLabel : undefined}\n >\n {item.icon ? (\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden [&>svg]:size-6 [&_svg]:text-muted-foreground\">\n {item.icon}\n </div>\n ) : (\n <div className={cn(\n 'flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-lg border transition-colors',\n isSelected\n ? 'border-brand-violet/40 bg-brand-violet/10 text-brand-violet'\n : 'border-input bg-muted text-muted-foreground group-hover:border-foreground/20'\n )}>\n <span className=\"text-base font-semibold uppercase\">{item.title.slice(0, 1)}</span>\n </div>\n )}\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"truncate text-sm font-semibold text-foreground\">{item.title}</div>\n {item.rightLabel ? (\n <div className=\"shrink-0 text-overline font-medium uppercase tracking-wider text-muted-foreground\">\n {item.rightLabel}\n </div>\n ) : null}\n </div>\n {item.subtitle ? (\n <div className=\"text-xs text-muted-foreground truncate\">{item.subtitle}</div>\n ) : null}\n {item.description ? (\n <div className=\"text-xs text-muted-foreground/70 truncate\">{item.description}</div>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center justify-center\">\n {isSelected ? (\n <Check className=\"size-5 text-brand-violet\" aria-hidden=\"true\" />\n ) : (\n <div className=\"size-5\" aria-hidden=\"true\" />\n )}\n </div>\n </div>\n )\n })}\n </div>\n {value && !disabled ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-fit gap-1 text-sm font-normal\"\n onClick={() => onChange(null)}\n >\n <X className=\"h-4 w-4\" />\n {resolvedClearLabel}\n </Button>\n ) : null}\n </div>\n ) : collapsedSelectionLabel ? (\n /*\n * The visible input is the *search box*, not a value display: once the\n * list closes it shows its placeholder again, so a selection made and\n * then collapsed left the control looking empty even though the form\n * held the id. `selectedHintLabel` existed for exactly this and was\n * never rendered.\n */\n <div\n className=\"flex items-center justify-between gap-3 rounded-lg border border-input bg-muted/40 px-3 py-2\"\n data-testid=\"lookup-select-selected\"\n >\n <span className=\"truncate text-sm font-medium text-foreground\">\n {collapsedSelectionLabel}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"shrink-0 gap-1 text-sm font-normal\"\n disabled={disabled}\n onClick={() => onChange(null)}\n >\n <X className=\"h-4 w-4\" />\n {resolvedClearLabel}\n </Button>\n </div>\n ) : hasTyped ? (\n <p className=\"text-xs text-muted-foreground\">\n {resolvedMinQueryHintLabel}\n </p>\n ) : (\n <p className=\"text-xs text-muted-foreground\">{resolvedStartTypingLabel}</p>\n )}\n {error ? <p className=\"text-xs text-status-error-text\" role=\"alert\">{resolvedEmptyLabel}</p> : null}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -556,7 +556,7 @@ function PhoneNumberField({
556
556
  ]
557
557
  }
558
558
  ),
559
- /* @__PURE__ */ jsx(SelectContent, { align: "start", children: countryOptions.map(({ country: c, label }) => /* @__PURE__ */ jsxs(SelectItem, { value: c.iso2, children: [
559
+ /* @__PURE__ */ jsx(SelectContent, { align: "start", children: countryOptions.map(({ country: c, label }) => /* @__PURE__ */ jsxs(SelectItem, { value: c.iso2, textValue: label, children: [
560
560
  /* @__PURE__ */ jsx(SelectItemLeading, { children: /* @__PURE__ */ jsx("span", { className: "text-base leading-none", children: c.flag }) }),
561
561
  /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: label }),
562
562
  /* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground tabular-nums", children: c.dialCode })