@open-mercato/ui 0.8.1-develop.7263.1.bbc6db3437 → 0.8.1-develop.7267.1.2e95af80a6

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.
@@ -122,7 +122,6 @@ import {
122
122
  Reply,
123
123
  RotateCcw,
124
124
  Ruler,
125
- Search,
126
125
  Send,
127
126
  Settings,
128
127
  Shapes,
@@ -294,7 +293,6 @@ const LUCIDE_ICON_REGISTRY = {
294
293
  "reply": Reply,
295
294
  "rotate-ccw": RotateCcw,
296
295
  "ruler": Ruler,
297
- "search": Search,
298
296
  "send": Send,
299
297
  "settings": Settings,
300
298
  "shapes": Shapes,
@@ -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 Search,\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 '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 '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 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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",
6
6
  "names": []
7
7
  }
@@ -52,7 +52,7 @@ function buildComposePayload(params, input) {
52
52
  sourceEntityId: sourceEntityId ?? void 0,
53
53
  objects: contextObjects,
54
54
  attachmentIds: input.attachmentIds.length > 0 ? input.attachmentIds : void 0,
55
- sendViaEmail: publicMessage ? true : params.sendViaEmail,
55
+ sendViaEmail: params.sendViaEmail,
56
56
  isDraft: input.isDraft
57
57
  };
58
58
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/messages/useMessageComposeOperations.ts"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n MessageComposerContextObject,\n MessageComposerProps,\n MessageTypeItem,\n} from './message-composer.types'\nimport type { MessagePriority } from './message-priority'\n\ntype Translator = ReturnType<typeof useT>\n\ntype MessageRecipient = {\n userId: string\n type: 'to'\n}\n\ntype SubmitRequest = {\n endpoint: string\n method: 'POST' | 'PATCH'\n payload: Record<string, unknown>\n}\n\ntype BaseOperation = {\n validate: () => string | null\n buildRequest: (input: { attachmentIds: string[] }) => SubmitRequest\n successMessage: string\n requiresAttachmentRefresh: boolean\n}\n\ntype ComposeOperationParams = {\n t: Translator\n messageType: string\n createableMessageTypes: MessageTypeItem[]\n priority: MessagePriority\n visibility: 'public' | 'internal'\n externalEmail: string\n recipientIds: string[]\n subject: string\n body: string\n bodyFormat: 'text' | 'markdown'\n sendViaEmail: boolean\n contextObject: MessageComposerContextObject | null\n defaultValues: MessageComposerProps['defaultValues']\n contextActionOptions: Array<{ id: string; label: string }>\n normalizedRequiredActionMode: 'none' | 'optional' | 'required'\n shouldShowContextActions: boolean\n contextActionRequired: boolean\n contextActionType: string\n}\n\ntype ReplyOperationParams = {\n t: Translator\n messageId?: string\n body: string\n bodyFormat: 'text' | 'markdown'\n replyAll: boolean\n recipientIds: string[]\n sendViaEmail: boolean\n}\n\ntype ForwardOperationParams = {\n t: Translator\n messageId?: string\n recipientIds: string[]\n body: string\n includeAttachments: boolean\n sendViaEmail: boolean\n}\n\ntype DraftOperationParams = Omit<ComposeOperationParams, 'createableMessageTypes'>\n & { messageId?: string }\n\nfunction isValidEmailAddress(value: string): boolean {\n const email = value.trim()\n if (!email || email.length > 254) return false\n\n const atIndex = email.indexOf('@')\n if (atIndex <= 0 || atIndex !== email.lastIndexOf('@') || atIndex === email.length - 1) return false\n\n for (const char of email) {\n if (char === ' ' || char === '\\t' || char === '\\n' || char === '\\r') return false\n }\n\n const localPart = email.slice(0, atIndex)\n const domainPart = email.slice(atIndex + 1)\n if (!localPart || !domainPart || localPart.length > 64) return false\n if (domainPart.length > 253 || !domainPart.includes('.')) return false\n if (domainPart.startsWith('.') || domainPart.endsWith('.') || domainPart.includes('..')) return false\n\n const domainLabels = domainPart.split('.')\n for (const label of domainLabels) {\n if (!label) return false\n if (label.startsWith('-') || label.endsWith('-')) return false\n }\n\n return true\n}\n\nfunction mapRecipients(recipientIds: string[]): MessageRecipient[] {\n return recipientIds.map((userId) => ({ userId, type: 'to' }))\n}\n\nfunction buildComposePayload(\n params: ComposeOperationParams | DraftOperationParams,\n input: { attachmentIds: string[]; isDraft: boolean },\n): Record<string, unknown> {\n const publicMessage = params.visibility === 'public'\n const sourceEntityType = params.contextObject?.sourceEntityType ?? params.defaultValues?.sourceEntityType\n const sourceEntityId = params.contextObject?.sourceEntityId ?? params.defaultValues?.sourceEntityId\n const selectedContextActionOption = params.contextActionOptions.find((option) => option.id === params.contextActionType)\n const resolvedContextActionRequired = params.shouldShowContextActions\n ? (params.normalizedRequiredActionMode === 'required' ? true : params.contextActionRequired)\n : (params.contextObject?.actionRequired ?? false)\n const resolvedContextActionType = resolvedContextActionRequired\n ? (params.shouldShowContextActions ? params.contextActionType : params.contextObject?.actionType)\n : undefined\n const resolvedContextActionLabel = resolvedContextActionRequired\n ? (params.shouldShowContextActions ? selectedContextActionOption?.label : params.contextObject?.actionLabel)\n : undefined\n const contextObjects = params.contextObject\n ? [{\n entityModule: params.contextObject.entityModule,\n entityType: params.contextObject.entityType,\n entityId: params.contextObject.entityId,\n actionRequired: resolvedContextActionRequired,\n actionType: resolvedContextActionType,\n actionLabel: resolvedContextActionLabel,\n }]\n : undefined\n\n return {\n type: params.messageType,\n priority: params.priority,\n visibility: params.visibility,\n externalEmail: publicMessage ? params.externalEmail.trim() : undefined,\n externalName: undefined,\n recipients: publicMessage ? [] : mapRecipients(params.recipientIds),\n subject: params.subject.trim(),\n body: params.body,\n bodyFormat: params.bodyFormat,\n sourceEntityType: sourceEntityType ?? undefined,\n sourceEntityId: sourceEntityId ?? undefined,\n objects: contextObjects,\n attachmentIds: input.attachmentIds.length > 0 ? input.attachmentIds : undefined,\n sendViaEmail: publicMessage ? true : params.sendViaEmail,\n isDraft: input.isDraft,\n }\n}\n\nexport function useComposeSendOperation(params: ComposeOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.visibility !== 'public' && params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (params.visibility === 'public' && !isValidEmailAddress(params.externalEmail.trim())) {\n return params.t('messages.errors.noExternalEmail', 'Please enter a valid external email.')\n }\n if (!params.subject.trim()) {\n return params.t('messages.errors.noSubject', 'Please enter a subject.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n if (!params.createableMessageTypes.some((item) => item.type === params.messageType)) {\n return params.t('messages.errors.sendFailed', 'Failed to send message.')\n }\n if (\n params.shouldShowContextActions\n && params.normalizedRequiredActionMode === 'required'\n && !params.contextActionType\n ) {\n return params.t('messages.composer.objectPicker.errors.actionRequired', 'Select an action.')\n }\n if (\n params.shouldShowContextActions\n && params.normalizedRequiredActionMode === 'optional'\n && params.contextActionRequired\n && !params.contextActionType\n ) {\n return params.t('messages.composer.objectPicker.errors.actionRequired', 'Select an action.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: '/api/messages',\n method: 'POST',\n payload: buildComposePayload(params, { attachmentIds, isDraft: false }),\n }),\n successMessage: params.t('messages.flash.sentSuccess', 'Message sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useComposeDraftOperation(params: DraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => null,\n buildRequest: ({ attachmentIds }) => {\n const payload = buildComposePayload(params, { attachmentIds, isDraft: true })\n if (!params.messageId) {\n return {\n endpoint: '/api/messages',\n method: 'POST',\n payload,\n }\n }\n\n const draftUpdatePayload = { ...payload }\n delete draftUpdatePayload.isDraft\n return {\n endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,\n method: 'PATCH',\n payload: draftUpdatePayload,\n }\n },\n successMessage: params.t('messages.flash.draftSaved', 'Draft saved.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useReplySubmitOperation(params: ReplyOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n if (!params.messageId) {\n return params.t('messages.errors.invalidContext', 'Message context is missing.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? '')}/reply`,\n method: 'POST',\n payload: {\n body: params.body,\n bodyFormat: params.bodyFormat,\n replyAll: params.replyAll,\n recipients: params.recipientIds.length > 0 ? mapRecipients(params.recipientIds) : undefined,\n attachmentIds: attachmentIds.length > 0 ? attachmentIds : undefined,\n sendViaEmail: params.sendViaEmail,\n },\n }),\n successMessage: params.t('messages.flash.replySuccess', 'Reply sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useForwardSubmitOperation(params: ForwardOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (!params.messageId) {\n return params.t('messages.errors.invalidContext', 'Message context is missing.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n return null\n },\n buildRequest: () => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? '')}/forward`,\n method: 'POST',\n payload: {\n recipients: mapRecipients(params.recipientIds),\n body: params.body,\n includeAttachments: params.includeAttachments,\n sendViaEmail: params.sendViaEmail,\n },\n }),\n successMessage: params.t('messages.flash.forwardSuccess', 'Message forwarded.'),\n requiresAttachmentRefresh: false,\n }), [params])\n}\n\ntype SendDraftOperationParams = DraftOperationParams & { messageId: string }\n\nexport function useSendDraftOperation(params: SendDraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.visibility !== 'public' && params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (params.visibility === 'public' && !isValidEmailAddress(params.externalEmail.trim())) {\n return params.t('messages.errors.noExternalEmail', 'Please enter a valid external email.')\n }\n if (!params.subject.trim()) {\n return params.t('messages.errors.noSubject', 'Please enter a subject.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,\n method: 'PATCH' as const,\n payload: buildComposePayload(params, { attachmentIds, isDraft: false }),\n }),\n successMessage: params.t('messages.flash.sentSuccess', 'Message sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\ntype UpdateDraftOperationParams = DraftOperationParams & { messageId: string }\n\nexport function useUpdateDraftOperation(params: UpdateDraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => null,\n buildRequest: ({ attachmentIds }) => {\n const { isDraft: _omit, ...payload } = buildComposePayload(params, { attachmentIds, isDraft: true })\n return {\n endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,\n method: 'PATCH' as const,\n payload,\n }\n },\n successMessage: params.t('messages.flash.draftSaved', 'Draft saved.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport type MessageComposeSubmitOperation = BaseOperation\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n MessageComposerContextObject,\n MessageComposerProps,\n MessageTypeItem,\n} from './message-composer.types'\nimport type { MessagePriority } from './message-priority'\n\ntype Translator = ReturnType<typeof useT>\n\ntype MessageRecipient = {\n userId: string\n type: 'to'\n}\n\ntype SubmitRequest = {\n endpoint: string\n method: 'POST' | 'PATCH'\n payload: Record<string, unknown>\n}\n\ntype BaseOperation = {\n validate: () => string | null\n buildRequest: (input: { attachmentIds: string[] }) => SubmitRequest\n successMessage: string\n requiresAttachmentRefresh: boolean\n}\n\ntype ComposeOperationParams = {\n t: Translator\n messageType: string\n createableMessageTypes: MessageTypeItem[]\n priority: MessagePriority\n visibility: 'public' | 'internal'\n externalEmail: string\n recipientIds: string[]\n subject: string\n body: string\n bodyFormat: 'text' | 'markdown'\n sendViaEmail: boolean\n contextObject: MessageComposerContextObject | null\n defaultValues: MessageComposerProps['defaultValues']\n contextActionOptions: Array<{ id: string; label: string }>\n normalizedRequiredActionMode: 'none' | 'optional' | 'required'\n shouldShowContextActions: boolean\n contextActionRequired: boolean\n contextActionType: string\n}\n\ntype ReplyOperationParams = {\n t: Translator\n messageId?: string\n body: string\n bodyFormat: 'text' | 'markdown'\n replyAll: boolean\n recipientIds: string[]\n sendViaEmail: boolean\n}\n\ntype ForwardOperationParams = {\n t: Translator\n messageId?: string\n recipientIds: string[]\n body: string\n includeAttachments: boolean\n sendViaEmail: boolean\n}\n\ntype DraftOperationParams = Omit<ComposeOperationParams, 'createableMessageTypes'>\n & { messageId?: string }\n\nfunction isValidEmailAddress(value: string): boolean {\n const email = value.trim()\n if (!email || email.length > 254) return false\n\n const atIndex = email.indexOf('@')\n if (atIndex <= 0 || atIndex !== email.lastIndexOf('@') || atIndex === email.length - 1) return false\n\n for (const char of email) {\n if (char === ' ' || char === '\\t' || char === '\\n' || char === '\\r') return false\n }\n\n const localPart = email.slice(0, atIndex)\n const domainPart = email.slice(atIndex + 1)\n if (!localPart || !domainPart || localPart.length > 64) return false\n if (domainPart.length > 253 || !domainPart.includes('.')) return false\n if (domainPart.startsWith('.') || domainPart.endsWith('.') || domainPart.includes('..')) return false\n\n const domainLabels = domainPart.split('.')\n for (const label of domainLabels) {\n if (!label) return false\n if (label.startsWith('-') || label.endsWith('-')) return false\n }\n\n return true\n}\n\nfunction mapRecipients(recipientIds: string[]): MessageRecipient[] {\n return recipientIds.map((userId) => ({ userId, type: 'to' }))\n}\n\nfunction buildComposePayload(\n params: ComposeOperationParams | DraftOperationParams,\n input: { attachmentIds: string[]; isDraft: boolean },\n): Record<string, unknown> {\n const publicMessage = params.visibility === 'public'\n const sourceEntityType = params.contextObject?.sourceEntityType ?? params.defaultValues?.sourceEntityType\n const sourceEntityId = params.contextObject?.sourceEntityId ?? params.defaultValues?.sourceEntityId\n const selectedContextActionOption = params.contextActionOptions.find((option) => option.id === params.contextActionType)\n const resolvedContextActionRequired = params.shouldShowContextActions\n ? (params.normalizedRequiredActionMode === 'required' ? true : params.contextActionRequired)\n : (params.contextObject?.actionRequired ?? false)\n const resolvedContextActionType = resolvedContextActionRequired\n ? (params.shouldShowContextActions ? params.contextActionType : params.contextObject?.actionType)\n : undefined\n const resolvedContextActionLabel = resolvedContextActionRequired\n ? (params.shouldShowContextActions ? selectedContextActionOption?.label : params.contextObject?.actionLabel)\n : undefined\n const contextObjects = params.contextObject\n ? [{\n entityModule: params.contextObject.entityModule,\n entityType: params.contextObject.entityType,\n entityId: params.contextObject.entityId,\n actionRequired: resolvedContextActionRequired,\n actionType: resolvedContextActionType,\n actionLabel: resolvedContextActionLabel,\n }]\n : undefined\n\n return {\n type: params.messageType,\n priority: params.priority,\n visibility: params.visibility,\n externalEmail: publicMessage ? params.externalEmail.trim() : undefined,\n externalName: undefined,\n recipients: publicMessage ? [] : mapRecipients(params.recipientIds),\n subject: params.subject.trim(),\n body: params.body,\n bodyFormat: params.bodyFormat,\n sourceEntityType: sourceEntityType ?? undefined,\n sourceEntityId: sourceEntityId ?? undefined,\n objects: contextObjects,\n attachmentIds: input.attachmentIds.length > 0 ? input.attachmentIds : undefined,\n sendViaEmail: params.sendViaEmail,\n isDraft: input.isDraft,\n }\n}\n\nexport function useComposeSendOperation(params: ComposeOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.visibility !== 'public' && params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (params.visibility === 'public' && !isValidEmailAddress(params.externalEmail.trim())) {\n return params.t('messages.errors.noExternalEmail', 'Please enter a valid external email.')\n }\n if (!params.subject.trim()) {\n return params.t('messages.errors.noSubject', 'Please enter a subject.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n if (!params.createableMessageTypes.some((item) => item.type === params.messageType)) {\n return params.t('messages.errors.sendFailed', 'Failed to send message.')\n }\n if (\n params.shouldShowContextActions\n && params.normalizedRequiredActionMode === 'required'\n && !params.contextActionType\n ) {\n return params.t('messages.composer.objectPicker.errors.actionRequired', 'Select an action.')\n }\n if (\n params.shouldShowContextActions\n && params.normalizedRequiredActionMode === 'optional'\n && params.contextActionRequired\n && !params.contextActionType\n ) {\n return params.t('messages.composer.objectPicker.errors.actionRequired', 'Select an action.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: '/api/messages',\n method: 'POST',\n payload: buildComposePayload(params, { attachmentIds, isDraft: false }),\n }),\n successMessage: params.t('messages.flash.sentSuccess', 'Message sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useComposeDraftOperation(params: DraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => null,\n buildRequest: ({ attachmentIds }) => {\n const payload = buildComposePayload(params, { attachmentIds, isDraft: true })\n if (!params.messageId) {\n return {\n endpoint: '/api/messages',\n method: 'POST',\n payload,\n }\n }\n\n const draftUpdatePayload = { ...payload }\n delete draftUpdatePayload.isDraft\n return {\n endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,\n method: 'PATCH',\n payload: draftUpdatePayload,\n }\n },\n successMessage: params.t('messages.flash.draftSaved', 'Draft saved.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useReplySubmitOperation(params: ReplyOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n if (!params.messageId) {\n return params.t('messages.errors.invalidContext', 'Message context is missing.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? '')}/reply`,\n method: 'POST',\n payload: {\n body: params.body,\n bodyFormat: params.bodyFormat,\n replyAll: params.replyAll,\n recipients: params.recipientIds.length > 0 ? mapRecipients(params.recipientIds) : undefined,\n attachmentIds: attachmentIds.length > 0 ? attachmentIds : undefined,\n sendViaEmail: params.sendViaEmail,\n },\n }),\n successMessage: params.t('messages.flash.replySuccess', 'Reply sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useForwardSubmitOperation(params: ForwardOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (!params.messageId) {\n return params.t('messages.errors.invalidContext', 'Message context is missing.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n return null\n },\n buildRequest: () => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? '')}/forward`,\n method: 'POST',\n payload: {\n recipients: mapRecipients(params.recipientIds),\n body: params.body,\n includeAttachments: params.includeAttachments,\n sendViaEmail: params.sendViaEmail,\n },\n }),\n successMessage: params.t('messages.flash.forwardSuccess', 'Message forwarded.'),\n requiresAttachmentRefresh: false,\n }), [params])\n}\n\ntype SendDraftOperationParams = DraftOperationParams & { messageId: string }\n\nexport function useSendDraftOperation(params: SendDraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.visibility !== 'public' && params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (params.visibility === 'public' && !isValidEmailAddress(params.externalEmail.trim())) {\n return params.t('messages.errors.noExternalEmail', 'Please enter a valid external email.')\n }\n if (!params.subject.trim()) {\n return params.t('messages.errors.noSubject', 'Please enter a subject.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,\n method: 'PATCH' as const,\n payload: buildComposePayload(params, { attachmentIds, isDraft: false }),\n }),\n successMessage: params.t('messages.flash.sentSuccess', 'Message sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\ntype UpdateDraftOperationParams = DraftOperationParams & { messageId: string }\n\nexport function useUpdateDraftOperation(params: UpdateDraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => null,\n buildRequest: ({ attachmentIds }) => {\n const { isDraft: _omit, ...payload } = buildComposePayload(params, { attachmentIds, isDraft: true })\n return {\n endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,\n method: 'PATCH' as const,\n payload,\n }\n },\n successMessage: params.t('messages.flash.draftSaved', 'Draft saved.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport type MessageComposeSubmitOperation = BaseOperation\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ui",
3
- "version": "0.8.1-develop.7263.1.bbc6db3437",
3
+ "version": "0.8.1-develop.7267.1.2e95af80a6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -155,14 +155,14 @@
155
155
  "remark-gfm": "^4.0.1"
156
156
  },
157
157
  "peerDependencies": {
158
- "@open-mercato/shared": "0.8.1-develop.7263.1.bbc6db3437",
158
+ "@open-mercato/shared": "0.8.1-develop.7267.1.2e95af80a6",
159
159
  "react": ">=18.0.0",
160
160
  "react-dom": ">=18.0.0",
161
161
  "react-is": ">=18.0.0"
162
162
  },
163
163
  "devDependencies": {
164
164
  "@figma/code-connect": "^1.3.4",
165
- "@open-mercato/shared": "0.8.1-develop.7263.1.bbc6db3437",
165
+ "@open-mercato/shared": "0.8.1-develop.7267.1.2e95af80a6",
166
166
  "@testing-library/dom": "^10.4.1",
167
167
  "@testing-library/jest-dom": "^7.0.1",
168
168
  "@testing-library/react": "^16.3.3",
@@ -369,6 +369,32 @@ export type CrudFormProps<TValues extends Record<string, unknown>> = {
369
369
  entityIds?: string[]
370
370
  // Optional grouped layout rendered in two responsive columns (1 on mobile).
371
371
  groups?: CrudFormGroup[]
372
+ /**
373
+ * Hide the groups whose `CrudFormGroup.id` appears in this list, so a host can
374
+ * omit built-in cards without rebuilding the whole `groups` array.
375
+ *
376
+ * Presentation-only: hidden groups are removed before layout, so they reserve
377
+ * no column space and contribute no header, sortable entry, collapsible state
378
+ * or validation focus target. Fields that live only in a hidden group keep
379
+ * their current values and are still submitted unchanged — hiding a group
380
+ * never clears data. A `required` field that lives only in a hidden group is
381
+ * skipped by the built-in required check (mirroring `visibleWhen`), but a
382
+ * host-supplied zod `schema` is NOT bypassed: do not hide a group whose
383
+ * fields the schema requires unless defaults supply them.
384
+ *
385
+ * Hiding an injection widget's card (`widget:<widgetId>`) hides the card only —
386
+ * the widget's handlers belong to the spot, not to its card, so `onBeforeSave`
387
+ * and `transformFormData` still run and its fields stay required. Do not hide a
388
+ * widget group that gates saving; disable the widget instead.
389
+ *
390
+ * A user's persisted group order is preserved across hiding: a hidden group
391
+ * keeps its saved position and returns to it once it is shown again.
392
+ *
393
+ * Ids that match no declared group are ignored (dev-only warning). Strictly
394
+ * additive: when the prop is absent or empty the form behaves exactly as
395
+ * before.
396
+ */
397
+ hiddenGroupIds?: readonly string[]
372
398
  // Loading state for the entire form (e.g., when loading record data)
373
399
  isLoading?: boolean
374
400
  loadingMessage?: string
@@ -503,6 +529,10 @@ function readByDotPath(source: Record<string, unknown> | undefined, path: string
503
529
  // never reach into Object.prototype (prototype-pollution hardening).
504
530
  const PROTO_POLLUTING_KEYS = new Set(['__proto__', 'prototype', 'constructor'])
505
531
 
532
+ // Shared empty set so forms that pass no `hiddenGroupIds` keep a stable identity
533
+ // across renders and never invalidate the memos that depend on it.
534
+ const EMPTY_HIDDEN_GROUP_IDS: ReadonlySet<string> = new Set<string>()
535
+
506
536
  function isProtoPollutingKey(key: string): boolean {
507
537
  return PROTO_POLLUTING_KEYS.has(key)
508
538
  }
@@ -769,6 +799,7 @@ export function CrudForm<TValues extends Record<string, unknown>>({
769
799
  entityId,
770
800
  entityIds,
771
801
  groups,
802
+ hiddenGroupIds,
772
803
  isLoading = false,
773
804
  loadingMessage,
774
805
  customEntity = false,
@@ -1893,6 +1924,60 @@ export function CrudForm<TValues extends Record<string, unknown>>({
1893
1924
  return hidden
1894
1925
  }, [fields, values])
1895
1926
 
1927
+ // Joined key so a host passing a fresh array literal each render does not
1928
+ // invalidate every memo that depends on the resolved set.
1929
+ const hiddenGroupIdsKey = (hiddenGroupIds ?? []).join('\0')
1930
+ const hiddenGroupIdSet = React.useMemo(
1931
+ () => (hiddenGroupIdsKey ? new Set(hiddenGroupIdsKey.split('\0')) : EMPTY_HIDDEN_GROUP_IDS),
1932
+ [hiddenGroupIdsKey],
1933
+ )
1934
+
1935
+ const placedCustomFieldIds = React.useMemo(() => {
1936
+ const placed = new Set<string>()
1937
+ for (const other of (groups ?? [])) {
1938
+ if (other.kind === 'customFields' || !other.fields) continue
1939
+ for (const entry of other.fields) {
1940
+ if (typeof entry === 'string') placed.add(entry)
1941
+ else if (entry && typeof (entry as CrudField).id === 'string') placed.add((entry as CrudField).id)
1942
+ }
1943
+ }
1944
+ return placed
1945
+ }, [groups])
1946
+
1947
+ // Field ids that appear in a hidden group and in no visible one. Those controls
1948
+ // are never rendered, so gating submit on them would strand the user with an
1949
+ // error they cannot reach — the same reason `hiddenBaseFieldIds` exists for
1950
+ // `visibleWhen`. A field shared with a visible group stays validated.
1951
+ const hiddenGroupFieldIds = React.useMemo(() => {
1952
+ if (hiddenGroupIdSet.size === 0) return EMPTY_HIDDEN_GROUP_IDS
1953
+ const groupFieldIds = (group: CrudFormGroup): string[] => {
1954
+ if (group.kind === 'customFields') {
1955
+ return cfFields.filter((field) => !placedCustomFieldIds.has(field.id)).map((field) => field.id)
1956
+ }
1957
+ return (group.fields ?? []).map((entry) => (typeof entry === 'string' ? entry : entry.id))
1958
+ }
1959
+ const declaredGroups = groups ?? []
1960
+ const hidden = new Set<string>()
1961
+ const visible = new Set<string>()
1962
+ for (const group of declaredGroups) {
1963
+ const target = hiddenGroupIdSet.has(group.id) ? hidden : visible
1964
+ for (const fieldId of groupFieldIds(group)) target.add(fieldId)
1965
+ }
1966
+ // Mirror the injection fallback in `groupsWithInjectedFields`: a definition
1967
+ // whose target group does not exist is appended to the last declared group,
1968
+ // so it is hidden exactly when that fallback group is.
1969
+ const declaredGroupIds = new Set(declaredGroups.map((group) => group.id))
1970
+ const fallbackGroupId = declaredGroups[declaredGroups.length - 1]?.id
1971
+ for (const definition of injectedFieldDefinitions) {
1972
+ const targetGroupId = definition.group && declaredGroupIds.has(definition.group)
1973
+ ? definition.group
1974
+ : fallbackGroupId
1975
+ if (targetGroupId && hiddenGroupIdSet.has(targetGroupId)) hidden.add(definition.id)
1976
+ }
1977
+ for (const fieldId of visible) hidden.delete(fieldId)
1978
+ return hidden
1979
+ }, [cfFields, groups, hiddenGroupIdSet, injectedFieldDefinitions, placedCustomFieldIds])
1980
+
1896
1981
  const hiddenInjectedFieldIds = React.useMemo(() => {
1897
1982
  const hidden = new Set<string>()
1898
1983
  for (const definition of injectedFieldDefinitions) {
@@ -1984,6 +2069,7 @@ export function CrudForm<TValues extends Record<string, unknown>>({
1984
2069
  const field = fieldById.get(fieldId)
1985
2070
  if (!field || field.disabled) return
1986
2071
  if (hiddenBaseFieldIds.has(fieldId) || hiddenInjectedFieldIds.has(fieldId)) return
2072
+ if (hiddenGroupFieldIds.has(fieldId)) return
1987
2073
  if (!everEditedFieldIdsRef.current.has(fieldId)) return
1988
2074
 
1989
2075
  const nextValues = sourceValues ?? valuesRef.current
@@ -2093,6 +2179,7 @@ export function CrudForm<TValues extends Record<string, unknown>>({
2093
2179
  fieldById,
2094
2180
  formReadOnly,
2095
2181
  hiddenBaseFieldIds,
2182
+ hiddenGroupFieldIds,
2096
2183
  hiddenInjectedFieldIds,
2097
2184
  injectedFieldIdSet,
2098
2185
  mapDefsForValidation,
@@ -2167,15 +2254,44 @@ export function CrudForm<TValues extends Record<string, unknown>>({
2167
2254
  }, [groups, injectedFieldDefinitions])
2168
2255
 
2169
2256
  const shouldAutoGroup = (!groupsWithInjectedFields || groupsWithInjectedFields.length === 0) && injectionGroupCards.length > 0
2170
- const resolvedGroupsForLayout = React.useMemo(() => {
2257
+ const declaredGroupsForLayout = React.useMemo(() => {
2171
2258
  const baseGroups = groupsWithInjectedFields && groupsWithInjectedFields.length ? groupsWithInjectedFields : []
2172
2259
  const autoGroup = shouldAutoGroup ? [{ id: '__auto-fields__', fields: allFields }] as CrudFormGroup[] : []
2173
2260
  return [...(baseGroups.length ? baseGroups : autoGroup), ...injectionGroupCards]
2174
2261
  }, [allFields, groupsWithInjectedFields, injectionGroupCards, shouldAutoGroup])
2175
- const useGroupedLayout = resolvedGroupsForLayout.length > 0
2176
2262
 
2177
- // Sortable group order
2178
- const defaultGroupIds = React.useMemo(() => resolvedGroupsForLayout.map((g) => g.id), [resolvedGroupsForLayout])
2263
+ // Hiding is applied at this single chokepoint: the grouped layout, the sortable
2264
+ // order, collapsible auto-expand, the autofocus scan and both column renders all
2265
+ // derive from `resolvedGroupsForLayout`, so filtering here keeps hidden groups
2266
+ // out of every one of them without threading a flag through each call site.
2267
+ const resolvedGroupsForLayout = React.useMemo(() => {
2268
+ if (hiddenGroupIdSet.size === 0) return declaredGroupsForLayout
2269
+ return declaredGroupsForLayout.filter((group) => !hiddenGroupIdSet.has(group.id))
2270
+ }, [declaredGroupsForLayout, hiddenGroupIdSet])
2271
+
2272
+ React.useEffect(() => {
2273
+ if (process.env.NODE_ENV === 'production' || hiddenGroupIdSet.size === 0) return
2274
+ const declaredIds = new Set(declaredGroupsForLayout.map((group) => group.id))
2275
+ for (const hiddenId of hiddenGroupIdSet) {
2276
+ if (!declaredIds.has(hiddenId)) {
2277
+ logger.warn('hiddenGroupIds names a group that does not exist on this form', { groupId: hiddenId })
2278
+ }
2279
+ }
2280
+ }, [declaredGroupsForLayout, hiddenGroupIdSet])
2281
+
2282
+ // Keyed off the DECLARED groups, not the filtered ones: testing
2283
+ // `resolvedGroupsForLayout.length > 0` would drop a fully-hidden form into the
2284
+ // ungrouped branch and render every field flat — the opposite of hiding them.
2285
+ const useGroupedLayout = declaredGroupsForLayout.length > 0
2286
+
2287
+ // Sortable group order. Keyed off the DECLARED ids, not the filtered ones:
2288
+ // `useGroupOrder` prunes every saved id missing from its defaults and writes the
2289
+ // pruned list back to localStorage on the next reorder, so passing the filtered
2290
+ // list would permanently erase a hidden group's saved position — a persisted side
2291
+ // effect from a presentation-only prop. Hidden ids are inert downstream: the
2292
+ // rendered order sorts `resolvedGroupsForLayout` by index into this list, and the
2293
+ // sortable context is built from the visible groups only.
2294
+ const defaultGroupIds = React.useMemo(() => declaredGroupsForLayout.map((g) => g.id), [declaredGroupsForLayout])
2179
2295
  const { orderedIds: sortedGroupIds, reorder: reorderGroups } = useGroupOrder(
2180
2296
  sortablePageType,
2181
2297
  defaultGroupIds,
@@ -2210,18 +2326,6 @@ export function CrudForm<TValues extends Record<string, unknown>>({
2210
2326
  [injectionWidgets],
2211
2327
  )
2212
2328
 
2213
- const placedCustomFieldIds = React.useMemo(() => {
2214
- const placed = new Set<string>()
2215
- for (const other of (groups ?? [])) {
2216
- if (other.kind === 'customFields' || !other.fields) continue
2217
- for (const entry of other.fields) {
2218
- if (typeof entry === 'string') placed.add(entry)
2219
- else if (entry && typeof (entry as CrudField).id === 'string') placed.add((entry as CrudField).id)
2220
- }
2221
- }
2222
- return placed
2223
- }, [groups])
2224
-
2225
2329
  const resolveGroupFields = React.useCallback((g: CrudFormGroup): CrudField[] => {
2226
2330
  if (g.kind === 'customFields') {
2227
2331
  return cfFields.filter((f) => !placedCustomFieldIds.has(f.id))
@@ -2287,11 +2391,14 @@ export function CrudForm<TValues extends Record<string, unknown>>({
2287
2391
  if (fromCol2) return fromCol2
2288
2392
  }
2289
2393
 
2394
+ // `allFields` is unfiltered, so a form whose groups are all hidden would fall
2395
+ // through to a field that renders nowhere and hand autofocus a target the user
2396
+ // cannot reach.
2290
2397
  for (const field of allFields) {
2291
- if (field?.id && !field.disabled) return field.id
2398
+ if (field?.id && !field.disabled && !hiddenGroupFieldIds.has(field.id)) return field.id
2292
2399
  }
2293
2400
  return null
2294
- }, [allFields, resolveGroupFields, resolvedGroupsForLayout, useGroupedLayout])
2401
+ }, [allFields, hiddenGroupFieldIds, resolveGroupFields, resolvedGroupsForLayout, useGroupedLayout])
2295
2402
 
2296
2403
  const requestSubmit = React.useCallback(() => {
2297
2404
  if (formReadOnly) return
@@ -2872,6 +2979,7 @@ export function CrudForm<TValues extends Record<string, unknown>>({
2872
2979
  if (!field.required) continue
2873
2980
  if (field.disabled) continue
2874
2981
  if (hiddenBaseFieldIds.has(field.id) || hiddenInjectedFieldIds.has(field.id)) continue
2982
+ if (hiddenGroupFieldIds.has(field.id)) continue
2875
2983
  const v = values[field.id]
2876
2984
  const isArray = Array.isArray(v)
2877
2985
  const isString = typeof v === 'string'