@open-mercato/ui 0.5.1-develop.2860.07af3a6a9d → 0.5.1-develop.2874.77704bccbd
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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +204 -121
- package/dist/backend/AppShell.js +25 -28
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/ContextHelp.js +1 -1
- package/dist/backend/ContextHelp.js.map +1 -1
- package/dist/backend/CrudForm.js +12 -15
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/DataTable.js +9 -10
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/FilterBar.js +6 -8
- package/dist/backend/FilterBar.js.map +2 -2
- package/dist/backend/FilterOverlay.js +10 -10
- package/dist/backend/FilterOverlay.js.map +2 -2
- package/dist/backend/FlashMessages.js +1 -1
- package/dist/backend/FlashMessages.js.map +2 -2
- package/dist/backend/JsonBuilder.js +6 -6
- package/dist/backend/JsonBuilder.js.map +1 -1
- package/dist/backend/NextStepCallout.js +1 -1
- package/dist/backend/NextStepCallout.js.map +1 -1
- package/dist/backend/PerspectiveSidebar.js +2 -2
- package/dist/backend/PerspectiveSidebar.js.map +2 -2
- package/dist/backend/ProfileDropdown.js +1 -1
- package/dist/backend/ProfileDropdown.js.map +1 -1
- package/dist/backend/RowActions.js +1 -1
- package/dist/backend/RowActions.js.map +1 -1
- package/dist/backend/UserMenu.js +2 -2
- package/dist/backend/UserMenu.js.map +1 -1
- package/dist/backend/WebhookSetupGuide.js +11 -11
- package/dist/backend/WebhookSetupGuide.js.map +2 -2
- package/dist/backend/charts/KpiCard.js +3 -3
- package/dist/backend/charts/KpiCard.js.map +1 -1
- package/dist/backend/columns/ColumnChooserPanel.js +1 -1
- package/dist/backend/columns/ColumnChooserPanel.js.map +2 -2
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js +3 -3
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js.map +2 -2
- package/dist/backend/dashboard/DashboardScreen.js +1 -1
- package/dist/backend/dashboard/DashboardScreen.js.map +1 -1
- package/dist/backend/date-range/DateRangeSelect.js +1 -1
- package/dist/backend/date-range/DateRangeSelect.js.map +1 -1
- package/dist/backend/date-range/InlineDateRangeSelect.js +1 -1
- package/dist/backend/date-range/InlineDateRangeSelect.js.map +1 -1
- package/dist/backend/detail/AccessDeniedMessage.js +1 -1
- package/dist/backend/detail/AccessDeniedMessage.js.map +1 -1
- package/dist/backend/detail/ActivitiesSection.js +5 -5
- package/dist/backend/detail/ActivitiesSection.js.map +1 -1
- package/dist/backend/detail/AddressEditor.js +3 -3
- package/dist/backend/detail/AddressEditor.js.map +2 -2
- package/dist/backend/detail/AddressTiles.js +3 -3
- package/dist/backend/detail/AddressTiles.js.map +2 -2
- package/dist/backend/detail/AttachmentMetadataDialog.js +1 -1
- package/dist/backend/detail/AttachmentMetadataDialog.js.map +1 -1
- package/dist/backend/detail/CustomDataSection.js +1 -1
- package/dist/backend/detail/CustomDataSection.js.map +1 -1
- package/dist/backend/detail/InlineEditors.js +5 -5
- package/dist/backend/detail/InlineEditors.js.map +1 -1
- package/dist/backend/detail/NotesSection.js +6 -6
- package/dist/backend/detail/NotesSection.js.map +1 -1
- package/dist/backend/detail/TagsSection.js +1 -1
- package/dist/backend/detail/TagsSection.js.map +1 -1
- package/dist/backend/devtools/UmesDevToolsPanel.js +6 -6
- package/dist/backend/devtools/UmesDevToolsPanel.js.map +2 -2
- package/dist/backend/devtools/components/ConflictWarnings.js +3 -3
- package/dist/backend/devtools/components/ConflictWarnings.js.map +2 -2
- package/dist/backend/devtools/components/EnricherTiming.js +2 -2
- package/dist/backend/devtools/components/EnricherTiming.js.map +2 -2
- package/dist/backend/devtools/components/EventFlow.js +5 -5
- package/dist/backend/devtools/components/EventFlow.js.map +2 -2
- package/dist/backend/devtools/components/ExtensionPointList.js +3 -3
- package/dist/backend/devtools/components/ExtensionPointList.js.map +2 -2
- package/dist/backend/devtools/components/InterceptorActivity.js +6 -6
- package/dist/backend/devtools/components/InterceptorActivity.js.map +2 -2
- package/dist/backend/forms/ActionsDropdown.js +1 -1
- package/dist/backend/forms/ActionsDropdown.js.map +1 -1
- package/dist/backend/forms/FormActionButtons.js +2 -3
- package/dist/backend/forms/FormActionButtons.js.map +2 -2
- package/dist/backend/indexes/PartialIndexBanner.js +8 -8
- package/dist/backend/indexes/PartialIndexBanner.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js +1 -1
- package/dist/backend/inputs/ComboboxInput.js.map +2 -2
- package/dist/backend/inputs/DatePicker.js +3 -3
- package/dist/backend/inputs/DatePicker.js.map +1 -1
- package/dist/backend/inputs/DateTimePicker.js +3 -3
- package/dist/backend/inputs/DateTimePicker.js.map +1 -1
- package/dist/backend/inputs/EventSelect.js +1 -1
- package/dist/backend/inputs/EventSelect.js.map +2 -2
- package/dist/backend/inputs/LookupSelect.js +1 -1
- package/dist/backend/inputs/LookupSelect.js.map +1 -1
- package/dist/backend/inputs/SwitchableMarkdownInput.js +1 -1
- package/dist/backend/inputs/SwitchableMarkdownInput.js.map +1 -1
- package/dist/backend/inputs/TagsInput.js +2 -2
- package/dist/backend/inputs/TagsInput.js.map +2 -2
- package/dist/backend/inputs/TimeInput.js +1 -1
- package/dist/backend/inputs/TimeInput.js.map +1 -1
- package/dist/backend/inputs/TimePicker.js +3 -3
- package/dist/backend/inputs/TimePicker.js.map +1 -1
- package/dist/backend/messages/MessageObjectDetail.js +1 -1
- package/dist/backend/messages/MessageObjectDetail.js.map +1 -1
- package/dist/backend/messages/MessageObjectPreview.js +1 -1
- package/dist/backend/messages/MessageObjectPreview.js.map +1 -1
- package/dist/backend/messages/message-compose-form-groups.js +3 -3
- package/dist/backend/messages/message-compose-form-groups.js.map +1 -1
- package/dist/backend/notifications/NotificationCountBadge.js +1 -1
- package/dist/backend/notifications/NotificationCountBadge.js.map +2 -2
- package/dist/backend/notifications/NotificationPanel.js +3 -3
- package/dist/backend/notifications/NotificationPanel.js.map +1 -1
- package/dist/backend/progress/ProgressTopBar.js +4 -4
- package/dist/backend/progress/ProgressTopBar.js.map +2 -2
- package/dist/backend/schedule/ScheduleAgenda.js +1 -1
- package/dist/backend/schedule/ScheduleAgenda.js.map +2 -2
- package/dist/backend/schedule/ScheduleCalendar.js +1 -1
- package/dist/backend/schedule/ScheduleCalendar.js.map +1 -1
- package/dist/backend/schedule/ScheduleGrid.js +1 -1
- package/dist/backend/schedule/ScheduleGrid.js.map +2 -2
- package/dist/backend/version-history/VersionHistoryPanel.js +4 -4
- package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
- package/dist/frontend/AuthFooter.js +1 -1
- package/dist/frontend/AuthFooter.js.map +1 -1
- package/dist/frontend/LanguageSwitcher.js +1 -1
- package/dist/frontend/LanguageSwitcher.js.map +1 -1
- package/dist/frontend/Layout.js +2 -2
- package/dist/frontend/Layout.js.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +2 -2
- package/dist/portal/PortalShell.js +15 -15
- package/dist/portal/PortalShell.js.map +2 -2
- package/dist/portal/components/PortalCard.js +2 -2
- package/dist/portal/components/PortalCard.js.map +2 -2
- package/dist/portal/components/PortalNotificationPanel.js +18 -18
- package/dist/portal/components/PortalNotificationPanel.js.map +2 -2
- package/dist/portal/components/PortalPageHeader.js +1 -1
- package/dist/portal/components/PortalPageHeader.js.map +2 -2
- package/dist/primitives/avatar.js +11 -1
- package/dist/primitives/avatar.js.map +2 -2
- package/dist/primitives/badge.js +1 -1
- package/dist/primitives/badge.js.map +1 -1
- package/dist/primitives/button.js +9 -5
- package/dist/primitives/button.js.map +2 -2
- package/dist/primitives/calendar.js +1 -1
- package/dist/primitives/calendar.js.map +1 -1
- package/dist/primitives/checkbox-field.js +63 -0
- package/dist/primitives/checkbox-field.js.map +7 -0
- package/dist/primitives/checkbox.js +31 -17
- package/dist/primitives/checkbox.js.map +2 -2
- package/dist/primitives/dialog.js +4 -4
- package/dist/primitives/dialog.js.map +1 -1
- package/dist/primitives/fancy-button.js +72 -0
- package/dist/primitives/fancy-button.js.map +7 -0
- package/dist/primitives/icon-button.js +20 -4
- package/dist/primitives/icon-button.js.map +2 -2
- package/dist/primitives/kbd.js +27 -0
- package/dist/primitives/kbd.js.map +7 -0
- package/dist/primitives/link-button.js +56 -0
- package/dist/primitives/link-button.js.map +7 -0
- package/dist/primitives/popover.js +1 -1
- package/dist/primitives/popover.js.map +1 -1
- package/dist/primitives/social-button.js +61 -0
- package/dist/primitives/social-button.js.map +7 -0
- package/dist/primitives/tabs.js +1 -1
- package/dist/primitives/tabs.js.map +1 -1
- package/dist/primitives/tag.js +45 -0
- package/dist/primitives/tag.js.map +7 -0
- package/dist/primitives/tooltip.js +1 -1
- package/dist/primitives/tooltip.js.map +1 -1
- package/package.json +3 -3
- package/src/backend/AppShell.tsx +25 -28
- package/src/backend/ContextHelp.tsx +1 -1
- package/src/backend/CrudForm.tsx +12 -15
- package/src/backend/DataTable.tsx +9 -10
- package/src/backend/FilterBar.tsx +6 -5
- package/src/backend/FilterOverlay.tsx +10 -10
- package/src/backend/FlashMessages.tsx +1 -1
- package/src/backend/JsonBuilder.tsx +6 -6
- package/src/backend/NextStepCallout.tsx +1 -1
- package/src/backend/PerspectiveSidebar.tsx +2 -2
- package/src/backend/ProfileDropdown.tsx +1 -1
- package/src/backend/RowActions.tsx +1 -1
- package/src/backend/UserMenu.tsx +2 -2
- package/src/backend/WebhookSetupGuide.tsx +11 -11
- package/src/backend/charts/KpiCard.tsx +3 -3
- package/src/backend/columns/ColumnChooserPanel.tsx +1 -1
- package/src/backend/custom-fields/FieldDefinitionsEditor.tsx +3 -3
- package/src/backend/dashboard/DashboardScreen.tsx +1 -1
- package/src/backend/date-range/DateRangeSelect.tsx +1 -1
- package/src/backend/date-range/InlineDateRangeSelect.tsx +1 -1
- package/src/backend/detail/AccessDeniedMessage.tsx +1 -1
- package/src/backend/detail/ActivitiesSection.tsx +5 -5
- package/src/backend/detail/AddressEditor.tsx +3 -3
- package/src/backend/detail/AddressTiles.tsx +3 -3
- package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
- package/src/backend/detail/CustomDataSection.tsx +1 -1
- package/src/backend/detail/InlineEditors.tsx +5 -5
- package/src/backend/detail/NotesSection.tsx +6 -6
- package/src/backend/detail/TagsSection.tsx +1 -1
- package/src/backend/devtools/UmesDevToolsPanel.tsx +6 -6
- package/src/backend/devtools/components/ConflictWarnings.tsx +4 -4
- package/src/backend/devtools/components/EnricherTiming.tsx +2 -2
- package/src/backend/devtools/components/EventFlow.tsx +5 -5
- package/src/backend/devtools/components/ExtensionPointList.tsx +3 -3
- package/src/backend/devtools/components/InterceptorActivity.tsx +6 -6
- package/src/backend/forms/ActionsDropdown.tsx +1 -1
- package/src/backend/forms/FormActionButtons.tsx +4 -5
- package/src/backend/indexes/PartialIndexBanner.tsx +8 -8
- package/src/backend/inputs/ComboboxInput.tsx +1 -1
- package/src/backend/inputs/DatePicker.tsx +3 -3
- package/src/backend/inputs/DateTimePicker.tsx +3 -3
- package/src/backend/inputs/EventSelect.tsx +1 -1
- package/src/backend/inputs/LookupSelect.tsx +1 -1
- package/src/backend/inputs/SwitchableMarkdownInput.tsx +1 -1
- package/src/backend/inputs/TagsInput.tsx +2 -2
- package/src/backend/inputs/TimeInput.tsx +1 -1
- package/src/backend/inputs/TimePicker.tsx +3 -3
- package/src/backend/messages/MessageObjectDetail.tsx +1 -1
- package/src/backend/messages/MessageObjectPreview.tsx +1 -1
- package/src/backend/messages/message-compose-form-groups.tsx +3 -3
- package/src/backend/notifications/NotificationCountBadge.tsx +1 -1
- package/src/backend/notifications/NotificationPanel.tsx +3 -3
- package/src/backend/progress/ProgressTopBar.tsx +4 -4
- package/src/backend/schedule/ScheduleAgenda.tsx +1 -1
- package/src/backend/schedule/ScheduleCalendar.tsx +1 -1
- package/src/backend/schedule/ScheduleGrid.tsx +1 -1
- package/src/backend/version-history/VersionHistoryPanel.tsx +4 -4
- package/src/frontend/AuthFooter.tsx +1 -1
- package/src/frontend/LanguageSwitcher.tsx +1 -1
- package/src/frontend/Layout.tsx +2 -2
- package/src/index.ts +6 -1
- package/src/portal/PortalShell.tsx +15 -15
- package/src/portal/components/PortalCard.tsx +2 -2
- package/src/portal/components/PortalNotificationPanel.tsx +18 -18
- package/src/portal/components/PortalPageHeader.tsx +1 -1
- package/src/primitives/avatar.tsx +22 -0
- package/src/primitives/badge.tsx +1 -1
- package/src/primitives/button.tsx +12 -5
- package/src/primitives/calendar.tsx +1 -1
- package/src/primitives/checkbox-field.tsx +85 -0
- package/src/primitives/checkbox.tsx +44 -18
- package/src/primitives/dialog.tsx +4 -4
- package/src/primitives/fancy-button.tsx +89 -0
- package/src/primitives/icon-button.tsx +19 -2
- package/src/primitives/kbd.tsx +38 -0
- package/src/primitives/link-button.tsx +55 -0
- package/src/primitives/popover.tsx +1 -1
- package/src/primitives/social-button.tsx +80 -0
- package/src/primitives/tabs.tsx +1 -1
- package/src/primitives/tag.tsx +66 -0
- package/src/primitives/tooltip.tsx +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/inputs/DateTimePicker.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { format } from 'date-fns'\nimport type { Locale } from 'date-fns'\nimport { CalendarIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover'\nimport { Calendar } from '../../primitives/calendar'\nimport { TimeInput } from './TimeInput'\n\nexport type DateTimePickerProps = {\n value?: Date | null\n onChange: (date: Date | null) => void\n placeholder?: string\n disabled?: boolean\n readOnly?: boolean\n className?: string\n locale?: Locale\n displayFormat?: string\n minuteStep?: number\n showTodayButton?: boolean\n showClearButton?: boolean\n minDate?: Date\n maxDate?: Date\n}\n\nconst DAY_FIRST_LOCALE_CODES = new Set([\n 'pl', 'de', 'fr', 'es', 'it', 'pt', 'nl', 'ru', 'cs', 'sk', 'hu', 'ro',\n])\n\nfunction deriveDisplayFormat(locale?: Locale): string {\n if (!locale) return 'MMM d, yyyy HH:mm'\n const code = locale.code?.split('-')[0]?.toLowerCase() ?? ''\n return DAY_FIRST_LOCALE_CODES.has(code) ? 'd MMM yyyy HH:mm' : 'MMM d, yyyy HH:mm'\n}\n\nfunction extractTime(date: Date): string {\n const hour = String(date.getHours()).padStart(2, '0')\n const minute = String(date.getMinutes()).padStart(2, '0')\n return `${hour}:${minute}`\n}\n\nfunction applyTimeToDate(base: Date, time: string): Date {\n const parts = time.split(':')\n const hour = parseInt(parts[0] ?? '0', 10)\n const minute = parseInt(parts[1] ?? '0', 10)\n const next = new Date(base)\n next.setHours(isNaN(hour) ? 0 : hour)\n next.setMinutes(isNaN(minute) ? 0 : minute)\n next.setSeconds(0)\n next.setMilliseconds(0)\n return next\n}\n\nexport function DateTimePicker({\n value,\n onChange,\n placeholder,\n disabled = false,\n readOnly = false,\n className,\n locale,\n displayFormat,\n minuteStep = 1,\n showTodayButton = true,\n showClearButton = true,\n minDate,\n maxDate,\n}: DateTimePickerProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n\n const resolvedFormat = displayFormat ?? deriveDisplayFormat(locale)\n const placeholderText = placeholder ?? t('ui.dateTimePicker.placeholder', 'Pick date and time')\n const timeLabelText = t('ui.dateTimePicker.timeLabel', 'Time')\n const todayText = t('ui.dateTimePicker.todayButton', 'Today')\n const clearText = t('ui.dateTimePicker.clearButton', 'Clear')\n\n const formattedValue = React.useMemo(() => {\n if (!value) return null\n try {\n return format(value, resolvedFormat, locale ? { locale } : undefined)\n } catch {\n return null\n }\n }, [value, resolvedFormat, locale])\n\n const handleDaySelect = React.useCallback(\n (day: Date | undefined) => {\n if (!day) return\n const currentTime = value ? extractTime(value) : '00:00'\n onChange(applyTimeToDate(day, currentTime))\n },\n [onChange, value]\n )\n\n const handleTimeChange = React.useCallback(\n (time: string) => {\n const base = value ?? null\n if (!base) return\n onChange(applyTimeToDate(base, time))\n },\n [onChange, value]\n )\n\n const handleToday = React.useCallback(() => {\n const now = new Date()\n const currentTime = value ? extractTime(value) : extractTime(now)\n onChange(applyTimeToDate(now, currentTime))\n setOpen(false)\n }, [onChange, value])\n\n const handleClear = React.useCallback(() => {\n onChange(null)\n setOpen(false)\n }, [onChange])\n\n const isInteractive = !disabled && !readOnly\n\n const disabledMatcher = React.useMemo(() => {\n if (!minDate && !maxDate) return undefined\n const matchers: import('react-day-picker').Matcher[] = []\n if (minDate) matchers.push({ before: minDate })\n if (maxDate) matchers.push({ after: maxDate })\n return matchers\n }, [minDate, maxDate])\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n data-crud-focus-target=\"\"\n disabled={disabled}\n aria-haspopup=\"dialog\"\n className={cn(\n 'w-full h-9 flex items-center gap-2 rounded border px-3 text-sm text-left',\n 'bg-background transition-colors',\n 'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1',\n 'disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed',\n readOnly && 'cursor-default opacity-70',\n !formattedValue && 'text-muted-foreground',\n className\n )}\n onClick={isInteractive ? undefined : (e) => e.preventDefault()}\n >\n <CalendarIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 truncate\">\n {formattedValue ?? placeholderText}\n </span>\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"p-0 w-auto\">\n <Calendar\n mode=\"single\"\n selected={value ?? undefined}\n onSelect={handleDaySelect}\n locale={locale}\n disabled={disabledMatcher}\n initialFocus\n />\n <div className=\"border-t px-3 py-2 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground shrink-0\">{timeLabelText}:</span>\n <TimeInput\n value={value ? extractTime(value) : undefined}\n onChange={handleTimeChange}\n minuteStep={minuteStep}\n />\n </div>\n {(showTodayButton || showClearButton) && (\n <div className=\"flex items-center justify-between gap-2\">\n {showTodayButton && (\n <button\n type=\"button\"\n onClick={handleToday}\n className=\"text-sm text-primary hover:underline focus:outline-none\"\n >\n {todayText}\n </button>\n )}\n {showClearButton && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"text-sm text-muted-foreground hover:text-foreground hover:underline focus:outline-none ml-auto\"\n >\n {clearText}\n </button>\n )}\n </div>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { format } from 'date-fns'\nimport type { Locale } from 'date-fns'\nimport { CalendarIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover'\nimport { Calendar } from '../../primitives/calendar'\nimport { TimeInput } from './TimeInput'\n\nexport type DateTimePickerProps = {\n value?: Date | null\n onChange: (date: Date | null) => void\n placeholder?: string\n disabled?: boolean\n readOnly?: boolean\n className?: string\n locale?: Locale\n displayFormat?: string\n minuteStep?: number\n showTodayButton?: boolean\n showClearButton?: boolean\n minDate?: Date\n maxDate?: Date\n}\n\nconst DAY_FIRST_LOCALE_CODES = new Set([\n 'pl', 'de', 'fr', 'es', 'it', 'pt', 'nl', 'ru', 'cs', 'sk', 'hu', 'ro',\n])\n\nfunction deriveDisplayFormat(locale?: Locale): string {\n if (!locale) return 'MMM d, yyyy HH:mm'\n const code = locale.code?.split('-')[0]?.toLowerCase() ?? ''\n return DAY_FIRST_LOCALE_CODES.has(code) ? 'd MMM yyyy HH:mm' : 'MMM d, yyyy HH:mm'\n}\n\nfunction extractTime(date: Date): string {\n const hour = String(date.getHours()).padStart(2, '0')\n const minute = String(date.getMinutes()).padStart(2, '0')\n return `${hour}:${minute}`\n}\n\nfunction applyTimeToDate(base: Date, time: string): Date {\n const parts = time.split(':')\n const hour = parseInt(parts[0] ?? '0', 10)\n const minute = parseInt(parts[1] ?? '0', 10)\n const next = new Date(base)\n next.setHours(isNaN(hour) ? 0 : hour)\n next.setMinutes(isNaN(minute) ? 0 : minute)\n next.setSeconds(0)\n next.setMilliseconds(0)\n return next\n}\n\nexport function DateTimePicker({\n value,\n onChange,\n placeholder,\n disabled = false,\n readOnly = false,\n className,\n locale,\n displayFormat,\n minuteStep = 1,\n showTodayButton = true,\n showClearButton = true,\n minDate,\n maxDate,\n}: DateTimePickerProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n\n const resolvedFormat = displayFormat ?? deriveDisplayFormat(locale)\n const placeholderText = placeholder ?? t('ui.dateTimePicker.placeholder', 'Pick date and time')\n const timeLabelText = t('ui.dateTimePicker.timeLabel', 'Time')\n const todayText = t('ui.dateTimePicker.todayButton', 'Today')\n const clearText = t('ui.dateTimePicker.clearButton', 'Clear')\n\n const formattedValue = React.useMemo(() => {\n if (!value) return null\n try {\n return format(value, resolvedFormat, locale ? { locale } : undefined)\n } catch {\n return null\n }\n }, [value, resolvedFormat, locale])\n\n const handleDaySelect = React.useCallback(\n (day: Date | undefined) => {\n if (!day) return\n const currentTime = value ? extractTime(value) : '00:00'\n onChange(applyTimeToDate(day, currentTime))\n },\n [onChange, value]\n )\n\n const handleTimeChange = React.useCallback(\n (time: string) => {\n const base = value ?? null\n if (!base) return\n onChange(applyTimeToDate(base, time))\n },\n [onChange, value]\n )\n\n const handleToday = React.useCallback(() => {\n const now = new Date()\n const currentTime = value ? extractTime(value) : extractTime(now)\n onChange(applyTimeToDate(now, currentTime))\n setOpen(false)\n }, [onChange, value])\n\n const handleClear = React.useCallback(() => {\n onChange(null)\n setOpen(false)\n }, [onChange])\n\n const isInteractive = !disabled && !readOnly\n\n const disabledMatcher = React.useMemo(() => {\n if (!minDate && !maxDate) return undefined\n const matchers: import('react-day-picker').Matcher[] = []\n if (minDate) matchers.push({ before: minDate })\n if (maxDate) matchers.push({ after: maxDate })\n return matchers\n }, [minDate, maxDate])\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n data-crud-focus-target=\"\"\n disabled={disabled}\n aria-haspopup=\"dialog\"\n className={cn(\n 'w-full h-9 flex items-center gap-2 rounded border px-3 text-sm text-left',\n 'bg-background transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n 'disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed',\n readOnly && 'cursor-default opacity-70',\n !formattedValue && 'text-muted-foreground',\n className\n )}\n onClick={isInteractive ? undefined : (e) => e.preventDefault()}\n >\n <CalendarIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 truncate\">\n {formattedValue ?? placeholderText}\n </span>\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"p-0 w-auto\">\n <Calendar\n mode=\"single\"\n selected={value ?? undefined}\n onSelect={handleDaySelect}\n locale={locale}\n disabled={disabledMatcher}\n initialFocus\n />\n <div className=\"border-t px-3 py-2 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground shrink-0\">{timeLabelText}:</span>\n <TimeInput\n value={value ? extractTime(value) : undefined}\n onChange={handleTimeChange}\n minuteStep={minuteStep}\n />\n </div>\n {(showTodayButton || showClearButton) && (\n <div className=\"flex items-center justify-between gap-2\">\n {showTodayButton && (\n <button\n type=\"button\"\n onClick={handleToday}\n className=\"text-sm text-primary hover:underline focus-visible:outline-none\"\n >\n {todayText}\n </button>\n )}\n {showClearButton && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"text-sm text-muted-foreground hover:text-foreground hover:underline focus-visible:outline-none ml-auto\"\n >\n {clearText}\n </button>\n )}\n </div>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -55,7 +55,7 @@ function EventSelect({
|
|
|
55
55
|
{
|
|
56
56
|
value,
|
|
57
57
|
onChange: (e) => onChange(e.target.value),
|
|
58
|
-
className: `h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className || ""}`,
|
|
58
|
+
className: `h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className || ""}`,
|
|
59
59
|
disabled: disabled || isLoading,
|
|
60
60
|
children: [
|
|
61
61
|
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: isLoading ? "Loading..." : isEmpty ? "No events available" : placeholder }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/inputs/EventSelect.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useMemo } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { apiCall } from '../utils/apiCall'\n\n/**\n * Event definition returned by the API\n */\nexport interface EventDefinition {\n id: string\n label: string\n description?: string\n category?: 'crud' | 'lifecycle' | 'system' | 'custom'\n module?: string\n entity?: string\n excludeFromTriggers?: boolean\n}\n\nexport interface EventSelectProps {\n /** Current selected event ID */\n value: string\n /** Called when event is selected */\n onChange: (eventId: string) => void\n /** Placeholder text when no event selected */\n placeholder?: string\n /** Additional CSS classes */\n className?: string\n /** Whether the select is disabled */\n disabled?: boolean\n /** Filter events by category */\n categories?: Array<'crud' | 'lifecycle' | 'system' | 'custom'>\n /** Filter events by module */\n modules?: string[]\n /** Whether to exclude events marked as excludeFromTriggers (default: true) */\n excludeTriggerExcluded?: boolean\n}\n\n/**\n * EventSelect - A reusable select component for choosing declared events\n *\n * Fetches available events from the API and groups them by module.\n */\nexport function EventSelect({\n value,\n onChange,\n placeholder = 'Select an event...',\n className,\n disabled,\n categories,\n modules,\n excludeTriggerExcluded = true,\n}: EventSelectProps) {\n // Fetch events from the API\n const { data: allEvents = [], isLoading } = useQuery({\n queryKey: ['declared-events', excludeTriggerExcluded],\n queryFn: async () => {\n const result = await apiCall<{ data: EventDefinition[]; total: number }>(\n `/api/events?excludeTriggerExcluded=${excludeTriggerExcluded}`\n )\n if (!result.ok) return []\n return result.result?.data || []\n },\n staleTime: 5 * 60 * 1000, // Cache for 5 minutes\n })\n\n // Filter events based on props\n const filteredEvents = useMemo(() => {\n let events = allEvents\n\n if (categories?.length) {\n events = events.filter(e => e.category && categories.includes(e.category))\n }\n if (modules?.length) {\n events = events.filter(e => e.module && modules.includes(e.module))\n }\n\n return events\n }, [allEvents, categories, modules])\n\n // Group events by module for better UX\n const eventsByModule = useMemo(() => {\n const grouped: Record<string, EventDefinition[]> = {}\n for (const event of filteredEvents) {\n const module = event.module || 'other'\n if (!grouped[module]) grouped[module] = []\n grouped[module].push(event)\n }\n // Sort modules alphabetically\n return Object.fromEntries(\n Object.entries(grouped).sort(([a], [b]) => a.localeCompare(b))\n )\n }, [filteredEvents])\n\n // Format module name for display\n const formatModuleName = (module: string): string => {\n return module.charAt(0).toUpperCase() + module.slice(1).replace(/_/g, ' ')\n }\n\n const isEmpty = !isLoading && filteredEvents.length === 0\n\n return (\n <select\n value={value}\n onChange={(e) => onChange(e.target.value)}\n className={`h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className || ''}`}\n disabled={disabled || isLoading}\n >\n <option value=\"\" disabled>\n {isLoading ? 'Loading...' : isEmpty ? 'No events available' : placeholder}\n </option>\n {Object.entries(eventsByModule).map(([module, moduleEvents]) => (\n <optgroup key={module} label={formatModuleName(module)}>\n {moduleEvents.map(event => (\n <option key={event.id} value={event.id}>\n {event.label}\n </option>\n ))}\n </optgroup>\n ))}\n </select>\n )\n}\n\n/**\n * Hook for getting available events\n */\nexport function useAvailableEvents(options?: {\n categories?: Array<'crud' | 'lifecycle' | 'system' | 'custom'>\n modules?: string[]\n excludeTriggerExcluded?: boolean\n}) {\n const excludeTriggerExcluded = options?.excludeTriggerExcluded !== false\n\n const { data: allEvents = [], isLoading, error, refetch } = useQuery({\n queryKey: ['declared-events', excludeTriggerExcluded],\n queryFn: async () => {\n const result = await apiCall<{ data: EventDefinition[]; total: number }>(\n `/api/events?excludeTriggerExcluded=${excludeTriggerExcluded}`\n )\n if (!result.ok) return []\n return result.result?.data || []\n },\n staleTime: 5 * 60 * 1000,\n })\n\n const filteredEvents = useMemo(() => {\n let events = allEvents\n\n if (options?.categories?.length) {\n events = events.filter(e => e.category && options.categories!.includes(e.category))\n }\n if (options?.modules?.length) {\n events = events.filter(e => e.module && options.modules!.includes(e.module))\n }\n\n return events\n }, [allEvents, options])\n\n // Group by module\n const eventsByModule = useMemo(() => {\n const grouped: Record<string, EventDefinition[]> = {}\n for (const event of filteredEvents) {\n const module = event.module || 'other'\n if (!grouped[module]) grouped[module] = []\n grouped[module].push(event)\n }\n return Object.fromEntries(\n Object.entries(grouped).sort(([a], [b]) => a.localeCompare(b))\n )\n }, [filteredEvents])\n\n return {\n events: filteredEvents,\n eventsByModule,\n isLoading,\n error,\n refetch,\n }\n}\n\nexport default EventSelect\n"],
|
|
5
|
-
"mappings": ";AAuGI,SAME,KANF;AApGJ,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,eAAe;AAuCjB,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,yBAAyB;AAC3B,GAAqB;AAEnB,QAAM,EAAE,MAAM,YAAY,CAAC,GAAG,UAAU,IAAI,SAAS;AAAA,IACnD,UAAU,CAAC,mBAAmB,sBAAsB;AAAA,IACpD,SAAS,YAAY;AACnB,YAAM,SAAS,MAAM;AAAA,QACnB,sCAAsC,sBAAsB;AAAA,MAC9D;AACA,UAAI,CAAC,OAAO,GAAI,QAAO,CAAC;AACxB,aAAO,OAAO,QAAQ,QAAQ,CAAC;AAAA,IACjC;AAAA,IACA,WAAW,IAAI,KAAK;AAAA;AAAA,EACtB,CAAC;AAGD,QAAM,iBAAiB,QAAQ,MAAM;AACnC,QAAI,SAAS;AAEb,QAAI,YAAY,QAAQ;AACtB,eAAS,OAAO,OAAO,OAAK,EAAE,YAAY,WAAW,SAAS,EAAE,QAAQ,CAAC;AAAA,IAC3E;AACA,QAAI,SAAS,QAAQ;AACnB,eAAS,OAAO,OAAO,OAAK,EAAE,UAAU,QAAQ,SAAS,EAAE,MAAM,CAAC;AAAA,IACpE;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,WAAW,YAAY,OAAO,CAAC;AAGnC,QAAM,iBAAiB,QAAQ,MAAM;AACnC,UAAM,UAA6C,CAAC;AACpD,eAAW,SAAS,gBAAgB;AAClC,YAAM,SAAS,MAAM,UAAU;AAC/B,UAAI,CAAC,QAAQ,MAAM,EAAG,SAAQ,MAAM,IAAI,CAAC;AACzC,cAAQ,MAAM,EAAE,KAAK,KAAK;AAAA,IAC5B;AAEA,WAAO,OAAO;AAAA,MACZ,OAAO,QAAQ,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAAA,IAC/D;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAGnB,QAAM,mBAAmB,CAAC,WAA2B;AACnD,WAAO,OAAO,OAAO,CAAC,EAAE,YAAY,IAAI,OAAO,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG;AAAA,EAC3E;AAEA,QAAM,UAAU,CAAC,aAAa,eAAe,WAAW;AAExD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,MACxC,WAAW,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useMemo } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { apiCall } from '../utils/apiCall'\n\n/**\n * Event definition returned by the API\n */\nexport interface EventDefinition {\n id: string\n label: string\n description?: string\n category?: 'crud' | 'lifecycle' | 'system' | 'custom'\n module?: string\n entity?: string\n excludeFromTriggers?: boolean\n}\n\nexport interface EventSelectProps {\n /** Current selected event ID */\n value: string\n /** Called when event is selected */\n onChange: (eventId: string) => void\n /** Placeholder text when no event selected */\n placeholder?: string\n /** Additional CSS classes */\n className?: string\n /** Whether the select is disabled */\n disabled?: boolean\n /** Filter events by category */\n categories?: Array<'crud' | 'lifecycle' | 'system' | 'custom'>\n /** Filter events by module */\n modules?: string[]\n /** Whether to exclude events marked as excludeFromTriggers (default: true) */\n excludeTriggerExcluded?: boolean\n}\n\n/**\n * EventSelect - A reusable select component for choosing declared events\n *\n * Fetches available events from the API and groups them by module.\n */\nexport function EventSelect({\n value,\n onChange,\n placeholder = 'Select an event...',\n className,\n disabled,\n categories,\n modules,\n excludeTriggerExcluded = true,\n}: EventSelectProps) {\n // Fetch events from the API\n const { data: allEvents = [], isLoading } = useQuery({\n queryKey: ['declared-events', excludeTriggerExcluded],\n queryFn: async () => {\n const result = await apiCall<{ data: EventDefinition[]; total: number }>(\n `/api/events?excludeTriggerExcluded=${excludeTriggerExcluded}`\n )\n if (!result.ok) return []\n return result.result?.data || []\n },\n staleTime: 5 * 60 * 1000, // Cache for 5 minutes\n })\n\n // Filter events based on props\n const filteredEvents = useMemo(() => {\n let events = allEvents\n\n if (categories?.length) {\n events = events.filter(e => e.category && categories.includes(e.category))\n }\n if (modules?.length) {\n events = events.filter(e => e.module && modules.includes(e.module))\n }\n\n return events\n }, [allEvents, categories, modules])\n\n // Group events by module for better UX\n const eventsByModule = useMemo(() => {\n const grouped: Record<string, EventDefinition[]> = {}\n for (const event of filteredEvents) {\n const module = event.module || 'other'\n if (!grouped[module]) grouped[module] = []\n grouped[module].push(event)\n }\n // Sort modules alphabetically\n return Object.fromEntries(\n Object.entries(grouped).sort(([a], [b]) => a.localeCompare(b))\n )\n }, [filteredEvents])\n\n // Format module name for display\n const formatModuleName = (module: string): string => {\n return module.charAt(0).toUpperCase() + module.slice(1).replace(/_/g, ' ')\n }\n\n const isEmpty = !isLoading && filteredEvents.length === 0\n\n return (\n <select\n value={value}\n onChange={(e) => onChange(e.target.value)}\n className={`h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className || ''}`}\n disabled={disabled || isLoading}\n >\n <option value=\"\" disabled>\n {isLoading ? 'Loading...' : isEmpty ? 'No events available' : placeholder}\n </option>\n {Object.entries(eventsByModule).map(([module, moduleEvents]) => (\n <optgroup key={module} label={formatModuleName(module)}>\n {moduleEvents.map(event => (\n <option key={event.id} value={event.id}>\n {event.label}\n </option>\n ))}\n </optgroup>\n ))}\n </select>\n )\n}\n\n/**\n * Hook for getting available events\n */\nexport function useAvailableEvents(options?: {\n categories?: Array<'crud' | 'lifecycle' | 'system' | 'custom'>\n modules?: string[]\n excludeTriggerExcluded?: boolean\n}) {\n const excludeTriggerExcluded = options?.excludeTriggerExcluded !== false\n\n const { data: allEvents = [], isLoading, error, refetch } = useQuery({\n queryKey: ['declared-events', excludeTriggerExcluded],\n queryFn: async () => {\n const result = await apiCall<{ data: EventDefinition[]; total: number }>(\n `/api/events?excludeTriggerExcluded=${excludeTriggerExcluded}`\n )\n if (!result.ok) return []\n return result.result?.data || []\n },\n staleTime: 5 * 60 * 1000,\n })\n\n const filteredEvents = useMemo(() => {\n let events = allEvents\n\n if (options?.categories?.length) {\n events = events.filter(e => e.category && options.categories!.includes(e.category))\n }\n if (options?.modules?.length) {\n events = events.filter(e => e.module && options.modules!.includes(e.module))\n }\n\n return events\n }, [allEvents, options])\n\n // Group by module\n const eventsByModule = useMemo(() => {\n const grouped: Record<string, EventDefinition[]> = {}\n for (const event of filteredEvents) {\n const module = event.module || 'other'\n if (!grouped[module]) grouped[module] = []\n grouped[module].push(event)\n }\n return Object.fromEntries(\n Object.entries(grouped).sort(([a], [b]) => a.localeCompare(b))\n )\n }, [filteredEvents])\n\n return {\n events: filteredEvents,\n eventsByModule,\n isLoading,\n error,\n refetch,\n }\n}\n\nexport default EventSelect\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -124,7 +124,7 @@ function LookupSelect({
|
|
|
124
124
|
"div",
|
|
125
125
|
{
|
|
126
126
|
className: cn(
|
|
127
|
-
"flex gap-3 rounded border bg-card p-3 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
127
|
+
"flex gap-3 rounded border bg-card p-3 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
128
128
|
isSelected ? "border-primary/70 bg-primary/5" : "hover:border-primary/50"
|
|
129
129
|
),
|
|
130
130
|
role: "button",
|
|
@@ -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 { Loader2, Search, X } from 'lucide-react'\nimport { Button } from '../../primitives/button'\nimport { cn } from '@open-mercato/shared/lib/utils'\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 = placeholder ?? 'Search\u2026',\n clearLabel = 'Clear selection',\n emptyLabel = 'No results',\n loadingLabel = 'Searching\u2026',\n selectLabel = 'Select',\n selectedLabel = 'Selected',\n minQueryHintLabel,\n startTypingLabel = 'Start typing to search.',\n selectedHintLabel,\n disabled = false,\n loading: loadingProp = false,\n defaultOpen = false,\n}: LookupSelectProps) {\n const [query, setQuery] = React.useState('')\n const [items, setItems] = React.useState<LookupSelectItem[]>(options ?? [])\n const [loading, setLoading] = 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 fetchItemsRef = React.useRef(fetchItems ?? fetchOptions)\n const setQueryRef = React.useRef(setQuery)\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 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 (onReady) onReady({ setQuery })\n }, [onReady, setQuery])\n\n const shouldSearch =\n defaultOpen || query.trim().length >= minQuery || Boolean(value && (options?.length ?? 0) > 0)\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 console.error('LookupSelect.fetchItems', 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 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-2 top-2.5 h-4 w-4 text-muted-foreground\" />\n <input\n className=\"w-full rounded border pl-8 pr-2 py-2 text-sm\"\n value={query}\n onChange={(event) => {\n setQuery(event.target.value)\n setHasTyped(true)\n }}\n placeholder={searchPlaceholder}\n disabled={disabled}\n />\n </div>\n {actionSlot ? <div className=\"sm:self-start\">{actionSlot}</div> : null}\n </div>\n {shouldSearch ? (\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 {loadingLabel}\n </div>\n ) : null}\n {!loading && !loadingProp && !items.length ? (\n <p className=\"text-xs text-muted-foreground\">{emptyLabel}</p>\n ) : null}\n <div className=\"space-y-2 max-h-80 overflow-y-auto\">\n {items.map((item) => {\n const isSelected = value === item.id\n return (\n <div\n key={item.id}\n className={cn(\n 'flex gap-3 rounded border bg-card p-3 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Loader2, Search, X } from 'lucide-react'\nimport { Button } from '../../primitives/button'\nimport { cn } from '@open-mercato/shared/lib/utils'\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 = placeholder ?? 'Search\u2026',\n clearLabel = 'Clear selection',\n emptyLabel = 'No results',\n loadingLabel = 'Searching\u2026',\n selectLabel = 'Select',\n selectedLabel = 'Selected',\n minQueryHintLabel,\n startTypingLabel = 'Start typing to search.',\n selectedHintLabel,\n disabled = false,\n loading: loadingProp = false,\n defaultOpen = false,\n}: LookupSelectProps) {\n const [query, setQuery] = React.useState('')\n const [items, setItems] = React.useState<LookupSelectItem[]>(options ?? [])\n const [loading, setLoading] = 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 fetchItemsRef = React.useRef(fetchItems ?? fetchOptions)\n const setQueryRef = React.useRef(setQuery)\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 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 (onReady) onReady({ setQuery })\n }, [onReady, setQuery])\n\n const shouldSearch =\n defaultOpen || query.trim().length >= minQuery || Boolean(value && (options?.length ?? 0) > 0)\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 console.error('LookupSelect.fetchItems', 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 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-2 top-2.5 h-4 w-4 text-muted-foreground\" />\n <input\n className=\"w-full rounded border pl-8 pr-2 py-2 text-sm\"\n value={query}\n onChange={(event) => {\n setQuery(event.target.value)\n setHasTyped(true)\n }}\n placeholder={searchPlaceholder}\n disabled={disabled}\n />\n </div>\n {actionSlot ? <div className=\"sm:self-start\">{actionSlot}</div> : null}\n </div>\n {shouldSearch ? (\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 {loadingLabel}\n </div>\n ) : null}\n {!loading && !loadingProp && !items.length ? (\n <p className=\"text-xs text-muted-foreground\">{emptyLabel}</p>\n ) : null}\n <div className=\"space-y-2 max-h-80 overflow-y-auto\">\n {items.map((item) => {\n const isSelected = value === item.id\n return (\n <div\n key={item.id}\n className={cn(\n 'flex gap-3 rounded border bg-card p-3 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n isSelected ? 'border-primary/70 bg-primary/5' : 'hover:border-primary/50'\n )}\n role=\"button\"\n tabIndex={item.disabled ? -1 : 0}\n onClick={() => {\n if (item.disabled && !isSelected) return\n onChange(item.id)\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n if (item.disabled && !isSelected) return\n onChange(item.id)\n }\n }}\n aria-pressed={isSelected}\n >\n <div className=\"flex h-10 w-10 sm:h-12 sm:w-12 shrink-0 items-center justify-center overflow-hidden rounded border bg-muted\">\n {item.icon ?? <span className=\"text-muted-foreground\">\u2022</span>}\n </div>\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium\">{item.title}</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 truncate\">{item.description}</div>\n ) : null}\n </div>\n {item.rightLabel ? (\n <div className=\"shrink-0 text-xs font-medium text-muted-foreground\">{item.rightLabel}</div>\n ) : null}\n </div>\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n variant={isSelected ? 'secondary' : 'outline'}\n size=\"sm\"\n className=\"shrink-0\"\n onClick={(event) => {\n event.stopPropagation()\n if (item.disabled && !isSelected) return\n onChange(item.id)\n }}\n disabled={item.disabled && !isSelected}\n >\n {isSelected ? selectedLabel : selectLabel}\n </Button>\n </div>\n </div>\n </div>\n )\n })}\n </div>\n {value ? (\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 {clearLabel}\n </Button>\n ) : null}\n </div>\n ) : hasTyped ? (\n <p className=\"text-xs text-muted-foreground\">\n {minQueryHintLabel ?? `Type at least ${minQuery} characters or paste an id to search.`}\n </p>\n ) : (\n <p className=\"text-xs text-muted-foreground\">{startTypingLabel}</p>\n )}\n {error ? <p className=\"text-xs text-destructive\">{emptyLabel}</p> : null}\n </div>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -43,7 +43,7 @@ function SwitchableMarkdownInput({
|
|
|
43
43
|
const { resolvedTheme } = useTheme();
|
|
44
44
|
const editorWrapperClasses = editorWrapperClassName ?? "w-full rounded-lg border border-muted-foreground/20 bg-background p-2";
|
|
45
45
|
const editorClasses = editorClassName ?? "w-full";
|
|
46
|
-
const textareaClasses = textareaClassName ?? "w-full resize-none overflow-hidden rounded-lg border border-muted-foreground/20 bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
46
|
+
const textareaClasses = textareaClassName ?? "w-full resize-none overflow-hidden rounded-lg border border-muted-foreground/20 bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
47
47
|
if (isMarkdownEnabled && !disableMarkdown) {
|
|
48
48
|
return /* @__PURE__ */ jsx("div", { className: editorWrapperClasses, children: /* @__PURE__ */ jsx("div", { "data-color-mode": resolvedTheme, className: editorClasses, children: /* @__PURE__ */ jsx(
|
|
49
49
|
UiMarkdownEditor,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/inputs/SwitchableMarkdownInput.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport dynamic from 'next/dynamic'\nimport type { PluggableList } from 'unified'\nimport { LoadingMessage } from '../detail/LoadingMessage'\nimport { useMarkdownRemarkPlugins } from '../markdown/useMarkdownRemarkPlugins'\nimport { useTheme } from '../../theme'\n\nexport type SwitchableMarkdownInputProps = {\n value: string\n onChange: (value: string) => void\n isMarkdownEnabled: boolean\n disableMarkdown?: boolean\n height?: number\n placeholder?: string\n rows?: number\n textareaRef?: React.Ref<HTMLTextAreaElement>\n onTextareaInput?: React.FormEventHandler<HTMLTextAreaElement>\n textareaClassName?: string\n editorWrapperClassName?: string\n editorClassName?: string\n disabled?: boolean\n remarkPlugins?: PluggableList\n}\n\ntype UiMarkdownEditorProps = {\n value?: string\n height?: number\n onChange?: (value?: string) => void\n previewOptions?: { remarkPlugins?: unknown[] }\n}\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nconst MarkdownEditorTestStub: React.ComponentType<UiMarkdownEditorProps> = ({ value, onChange }) => (\n <textarea\n className=\"min-h-[160px] w-full rounded border px-3 py-2 text-sm\"\n value={value ?? ''}\n onChange={(event) => onChange?.(event.target.value)}\n />\n)\n\nconst UiMarkdownEditor = isTestEnv\n ? MarkdownEditorTestStub\n : (dynamic(() => import('@uiw/react-md-editor'), {\n ssr: false,\n loading: () => (\n <LoadingMessage\n label=\"Loading editor...\"\n className=\"min-h-[220px] justify-center\"\n />\n ),\n }) as unknown as React.ComponentType<UiMarkdownEditorProps>)\n\nexport function SwitchableMarkdownInput({\n value,\n onChange,\n isMarkdownEnabled,\n disableMarkdown,\n height = 220,\n placeholder,\n rows = 3,\n textareaRef,\n onTextareaInput,\n textareaClassName,\n editorWrapperClassName,\n editorClassName,\n disabled,\n remarkPlugins,\n}: SwitchableMarkdownInputProps) {\n const resolvedPlugins = useMarkdownRemarkPlugins(remarkPlugins)\n const { resolvedTheme } = useTheme()\n const editorWrapperClasses =\n editorWrapperClassName ?? 'w-full rounded-lg border border-muted-foreground/20 bg-background p-2'\n const editorClasses = editorClassName ?? 'w-full'\n const textareaClasses =\n textareaClassName\n ?? 'w-full resize-none overflow-hidden rounded-lg border border-muted-foreground/20 bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport dynamic from 'next/dynamic'\nimport type { PluggableList } from 'unified'\nimport { LoadingMessage } from '../detail/LoadingMessage'\nimport { useMarkdownRemarkPlugins } from '../markdown/useMarkdownRemarkPlugins'\nimport { useTheme } from '../../theme'\n\nexport type SwitchableMarkdownInputProps = {\n value: string\n onChange: (value: string) => void\n isMarkdownEnabled: boolean\n disableMarkdown?: boolean\n height?: number\n placeholder?: string\n rows?: number\n textareaRef?: React.Ref<HTMLTextAreaElement>\n onTextareaInput?: React.FormEventHandler<HTMLTextAreaElement>\n textareaClassName?: string\n editorWrapperClassName?: string\n editorClassName?: string\n disabled?: boolean\n remarkPlugins?: PluggableList\n}\n\ntype UiMarkdownEditorProps = {\n value?: string\n height?: number\n onChange?: (value?: string) => void\n previewOptions?: { remarkPlugins?: unknown[] }\n}\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nconst MarkdownEditorTestStub: React.ComponentType<UiMarkdownEditorProps> = ({ value, onChange }) => (\n <textarea\n className=\"min-h-[160px] w-full rounded border px-3 py-2 text-sm\"\n value={value ?? ''}\n onChange={(event) => onChange?.(event.target.value)}\n />\n)\n\nconst UiMarkdownEditor = isTestEnv\n ? MarkdownEditorTestStub\n : (dynamic(() => import('@uiw/react-md-editor'), {\n ssr: false,\n loading: () => (\n <LoadingMessage\n label=\"Loading editor...\"\n className=\"min-h-[220px] justify-center\"\n />\n ),\n }) as unknown as React.ComponentType<UiMarkdownEditorProps>)\n\nexport function SwitchableMarkdownInput({\n value,\n onChange,\n isMarkdownEnabled,\n disableMarkdown,\n height = 220,\n placeholder,\n rows = 3,\n textareaRef,\n onTextareaInput,\n textareaClassName,\n editorWrapperClassName,\n editorClassName,\n disabled,\n remarkPlugins,\n}: SwitchableMarkdownInputProps) {\n const resolvedPlugins = useMarkdownRemarkPlugins(remarkPlugins)\n const { resolvedTheme } = useTheme()\n const editorWrapperClasses =\n editorWrapperClassName ?? 'w-full rounded-lg border border-muted-foreground/20 bg-background p-2'\n const editorClasses = editorClassName ?? 'w-full'\n const textareaClasses =\n textareaClassName\n ?? 'w-full resize-none overflow-hidden rounded-lg border border-muted-foreground/20 bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'\n\n if (isMarkdownEnabled && !disableMarkdown) {\n return (\n <div className={editorWrapperClasses}>\n <div data-color-mode={resolvedTheme} className={editorClasses}>\n <UiMarkdownEditor\n value={value}\n height={height}\n onChange={(nextValue) => onChange(typeof nextValue === 'string' ? nextValue : '')}\n previewOptions={resolvedPlugins.length ? { remarkPlugins: resolvedPlugins } : undefined}\n />\n </div>\n </div>\n )\n }\n\n return (\n <textarea\n ref={textareaRef}\n rows={rows}\n className={textareaClasses}\n placeholder={placeholder}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onInput={onTextareaInput}\n disabled={disabled}\n />\n )\n}\n"],
|
|
5
5
|
"mappings": ";AAsCE;AAnCF,OAAO,aAAa;AAEpB,SAAS,sBAAsB;AAC/B,SAAS,gCAAgC;AACzC,SAAS,gBAAgB;AA0BzB,MAAM,YACJ,OAAO,YAAY,gBAClB,QAAQ,IAAI,aAAa,UAAU,OAAO,QAAQ,IAAI,mBAAmB;AAE5E,MAAM,yBAAqE,CAAC,EAAE,OAAO,SAAS,MAC5F;AAAA,EAAC;AAAA;AAAA,IACC,WAAU;AAAA,IACV,OAAO,SAAS;AAAA,IAChB,UAAU,CAAC,UAAU,WAAW,MAAM,OAAO,KAAK;AAAA;AACpD;AAGF,MAAM,mBAAmB,YACrB,yBACC,QAAQ,MAAM,OAAO,sBAAsB,GAAG;AAAA,EAC7C,KAAK;AAAA,EACL,SAAS,MACP;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,WAAU;AAAA;AAAA,EACZ;AAEJ,CAAC;AAEE,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,kBAAkB,yBAAyB,aAAa;AAC9D,QAAM,EAAE,cAAc,IAAI,SAAS;AACnC,QAAM,uBACJ,0BAA0B;AAC5B,QAAM,gBAAgB,mBAAmB;AACzC,QAAM,kBACJ,qBACG;AAEL,MAAI,qBAAqB,CAAC,iBAAiB;AACzC,WACE,oBAAC,SAAI,WAAW,sBACd,8BAAC,SAAI,mBAAiB,eAAe,WAAW,eAC9C;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,UAAU,CAAC,cAAc,SAAS,OAAO,cAAc,WAAW,YAAY,EAAE;AAAA,QAChF,gBAAgB,gBAAgB,SAAS,EAAE,eAAe,gBAAgB,IAAI;AAAA;AAAA,IAChF,GACF,GACF;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,MAChD,SAAS;AAAA,MACT;AAAA;AAAA,EACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -165,7 +165,7 @@ function TagsInput({
|
|
|
165
165
|
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 rounded-sm bg-muted px-2 py-0.5 text-xs", children: [
|
|
166
166
|
/* @__PURE__ */ jsxs("span", { className: "flex flex-col items-start leading-tight", children: [
|
|
167
167
|
/* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: label }),
|
|
168
|
-
description ? /* @__PURE__ */ jsx("span", { className: "text-
|
|
168
|
+
description ? /* @__PURE__ */ jsx("span", { className: "text-overline text-muted-foreground", children: description }) : null
|
|
169
169
|
] }),
|
|
170
170
|
/* @__PURE__ */ jsx(
|
|
171
171
|
IconButton,
|
|
@@ -243,7 +243,7 @@ function TagsInput({
|
|
|
243
243
|
},
|
|
244
244
|
children: [
|
|
245
245
|
/* @__PURE__ */ jsx("span", { children: option.label }),
|
|
246
|
-
option.description ? /* @__PURE__ */ jsx("span", { className: "text-
|
|
246
|
+
option.description ? /* @__PURE__ */ jsx("span", { className: "text-overline text-muted-foreground", children: option.description }) : null
|
|
247
247
|
]
|
|
248
248
|
},
|
|
249
249
|
option.value
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/inputs/TagsInput.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport { IconButton } from '../../primitives/icon-button'\n\nexport type TagsInputOption = {\n value: string\n label: string\n description?: string | null\n}\n\nexport type TagsInputProps = {\n value: string[]\n onChange: (next: string[]) => void\n placeholder?: string\n suggestions?: Array<string | TagsInputOption>\n loadSuggestions?: (query?: string) => Promise<Array<string | TagsInputOption>>\n selectedOptions?: TagsInputOption[]\n resolveLabel?: (value: string) => string\n resolveDescription?: (value: string) => string | null | undefined\n autoFocus?: boolean\n disabled?: boolean\n allowCustomValues?: boolean\n showSuggestionsOnFocus?: boolean\n}\n\nfunction normalizeOptions(input?: Array<string | TagsInputOption>): TagsInputOption[] {\n if (!Array.isArray(input)) return []\n return input\n .map((option) => {\n if (typeof option === 'string') {\n const trimmed = option.trim()\n if (!trimmed) return null\n return { value: trimmed, label: trimmed }\n }\n const value = typeof option.value === 'string' ? option.value.trim() : ''\n if (!value) return null\n return {\n value,\n label: option.label?.trim() || value,\n description: option.description ?? null,\n }\n })\n .filter((option): option is TagsInputOption => !!option)\n}\n\nexport function TagsInput({\n value,\n onChange,\n placeholder,\n suggestions,\n loadSuggestions,\n selectedOptions,\n resolveLabel,\n resolveDescription,\n autoFocus,\n disabled = false,\n allowCustomValues = true,\n showSuggestionsOnFocus = true,\n}: TagsInputProps) {\n const t = useT()\n const [input, setInput] = React.useState('')\n const [asyncOptions, setAsyncOptions] = React.useState<TagsInputOption[]>([])\n const [loading, setLoading] = React.useState(false)\n const [touched, setTouched] = React.useState(false)\n const suppressBlurCommitRef = React.useRef(false)\n const valueRef = React.useRef(value)\n\n React.useEffect(() => {\n valueRef.current = value\n }, [value])\n\n const staticOptions = React.useMemo(() => normalizeOptions(suggestions), [suggestions])\n const selectedOptionList = React.useMemo(\n () => normalizeOptions(selectedOptions),\n [selectedOptions]\n )\n\n const optionMap = React.useMemo(() => {\n const map = new Map<string, TagsInputOption>()\n const register = (option: TagsInputOption) => {\n if (!map.has(option.value)) {\n map.set(option.value, option)\n }\n }\n staticOptions.forEach(register)\n asyncOptions.forEach(register)\n selectedOptionList.forEach(register)\n value.forEach((val) => {\n if (map.has(val)) return\n map.set(val, {\n value: val,\n label: resolveLabel?.(val) ?? val,\n description: resolveDescription?.(val) ?? null,\n })\n })\n return map\n }, [asyncOptions, resolveDescription, resolveLabel, selectedOptionList, staticOptions, value])\n\n const availableOptions = React.useMemo(() => {\n return Array.from(optionMap.values()).filter((option) => !value.includes(option.value))\n }, [optionMap, value])\n\n const filteredSuggestions = React.useMemo(() => {\n const query = input.toLowerCase().trim()\n if (!query) return availableOptions.slice(0, 8)\n return availableOptions.filter((option) => {\n const labelMatch = option.label.toLowerCase().includes(query)\n const descMatch = option.description?.toLowerCase().includes(query)\n return labelMatch || Boolean(descMatch)\n })\n }, [availableOptions, input])\n\n React.useEffect(() => {\n if (!loadSuggestions || !touched || disabled) return\n const query = input.trim()\n let cancelled = false\n const handle = window.setTimeout(async () => {\n setLoading(true)\n try {\n const items = await loadSuggestions(query)\n if (!cancelled) {\n setAsyncOptions(normalizeOptions(items))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n window.clearTimeout(handle)\n }\n }, [disabled, input, loadSuggestions, touched])\n\n const addValue = React.useCallback(\n (nextValue: string) => {\n if (disabled) return\n const trimmed = nextValue.trim()\n if (!trimmed) return\n const currentValue = valueRef.current\n if (currentValue.includes(trimmed)) return\n const next = [...currentValue, trimmed]\n valueRef.current = next\n onChange(next)\n },\n [disabled, onChange]\n )\n\n const findOptionForInput = React.useCallback(\n (raw: string): TagsInputOption | null => {\n const query = raw.trim().toLowerCase()\n if (!query) return null\n for (const option of optionMap.values()) {\n if (option.value === raw.trim()) return option\n if (option.label.toLowerCase() === query) return option\n }\n return null\n },\n [optionMap]\n )\n\n const addTag = React.useCallback(\n (raw: string) => {\n if (disabled) return\n const option = findOptionForInput(raw)\n if (option) {\n addValue(option.value)\n return\n }\n if (!allowCustomValues) return\n addValue(raw)\n },\n [addValue, allowCustomValues, disabled, findOptionForInput]\n )\n\n const removeTag = React.useCallback(\n (tag: string) => {\n if (disabled) return\n const next = valueRef.current.filter((candidate) => candidate !== tag)\n valueRef.current = next\n onChange(next)\n },\n [disabled, onChange]\n )\n\n return (\n <div\n className={[\n 'w-full rounded border px-2 py-1',\n disabled ? 'bg-muted text-muted-foreground/80 cursor-not-allowed' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n aria-disabled={disabled || undefined}\n >\n <div className=\"flex flex-wrap gap-1\">\n {value.map((tag) => {\n const option = optionMap.get(tag)\n const label = option?.label ?? tag\n const description = option?.description\n return (\n <span key={tag} className=\"inline-flex items-center gap-2 rounded-sm bg-muted px-2 py-0.5 text-xs\">\n <span className=\"flex flex-col items-start leading-tight\">\n <span className=\"whitespace-nowrap\">{label}</span>\n {description ? (\n <span className=\"text-
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport { IconButton } from '../../primitives/icon-button'\n\nexport type TagsInputOption = {\n value: string\n label: string\n description?: string | null\n}\n\nexport type TagsInputProps = {\n value: string[]\n onChange: (next: string[]) => void\n placeholder?: string\n suggestions?: Array<string | TagsInputOption>\n loadSuggestions?: (query?: string) => Promise<Array<string | TagsInputOption>>\n selectedOptions?: TagsInputOption[]\n resolveLabel?: (value: string) => string\n resolveDescription?: (value: string) => string | null | undefined\n autoFocus?: boolean\n disabled?: boolean\n allowCustomValues?: boolean\n showSuggestionsOnFocus?: boolean\n}\n\nfunction normalizeOptions(input?: Array<string | TagsInputOption>): TagsInputOption[] {\n if (!Array.isArray(input)) return []\n return input\n .map((option) => {\n if (typeof option === 'string') {\n const trimmed = option.trim()\n if (!trimmed) return null\n return { value: trimmed, label: trimmed }\n }\n const value = typeof option.value === 'string' ? option.value.trim() : ''\n if (!value) return null\n return {\n value,\n label: option.label?.trim() || value,\n description: option.description ?? null,\n }\n })\n .filter((option): option is TagsInputOption => !!option)\n}\n\nexport function TagsInput({\n value,\n onChange,\n placeholder,\n suggestions,\n loadSuggestions,\n selectedOptions,\n resolveLabel,\n resolveDescription,\n autoFocus,\n disabled = false,\n allowCustomValues = true,\n showSuggestionsOnFocus = true,\n}: TagsInputProps) {\n const t = useT()\n const [input, setInput] = React.useState('')\n const [asyncOptions, setAsyncOptions] = React.useState<TagsInputOption[]>([])\n const [loading, setLoading] = React.useState(false)\n const [touched, setTouched] = React.useState(false)\n const suppressBlurCommitRef = React.useRef(false)\n const valueRef = React.useRef(value)\n\n React.useEffect(() => {\n valueRef.current = value\n }, [value])\n\n const staticOptions = React.useMemo(() => normalizeOptions(suggestions), [suggestions])\n const selectedOptionList = React.useMemo(\n () => normalizeOptions(selectedOptions),\n [selectedOptions]\n )\n\n const optionMap = React.useMemo(() => {\n const map = new Map<string, TagsInputOption>()\n const register = (option: TagsInputOption) => {\n if (!map.has(option.value)) {\n map.set(option.value, option)\n }\n }\n staticOptions.forEach(register)\n asyncOptions.forEach(register)\n selectedOptionList.forEach(register)\n value.forEach((val) => {\n if (map.has(val)) return\n map.set(val, {\n value: val,\n label: resolveLabel?.(val) ?? val,\n description: resolveDescription?.(val) ?? null,\n })\n })\n return map\n }, [asyncOptions, resolveDescription, resolveLabel, selectedOptionList, staticOptions, value])\n\n const availableOptions = React.useMemo(() => {\n return Array.from(optionMap.values()).filter((option) => !value.includes(option.value))\n }, [optionMap, value])\n\n const filteredSuggestions = React.useMemo(() => {\n const query = input.toLowerCase().trim()\n if (!query) return availableOptions.slice(0, 8)\n return availableOptions.filter((option) => {\n const labelMatch = option.label.toLowerCase().includes(query)\n const descMatch = option.description?.toLowerCase().includes(query)\n return labelMatch || Boolean(descMatch)\n })\n }, [availableOptions, input])\n\n React.useEffect(() => {\n if (!loadSuggestions || !touched || disabled) return\n const query = input.trim()\n let cancelled = false\n const handle = window.setTimeout(async () => {\n setLoading(true)\n try {\n const items = await loadSuggestions(query)\n if (!cancelled) {\n setAsyncOptions(normalizeOptions(items))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n window.clearTimeout(handle)\n }\n }, [disabled, input, loadSuggestions, touched])\n\n const addValue = React.useCallback(\n (nextValue: string) => {\n if (disabled) return\n const trimmed = nextValue.trim()\n if (!trimmed) return\n const currentValue = valueRef.current\n if (currentValue.includes(trimmed)) return\n const next = [...currentValue, trimmed]\n valueRef.current = next\n onChange(next)\n },\n [disabled, onChange]\n )\n\n const findOptionForInput = React.useCallback(\n (raw: string): TagsInputOption | null => {\n const query = raw.trim().toLowerCase()\n if (!query) return null\n for (const option of optionMap.values()) {\n if (option.value === raw.trim()) return option\n if (option.label.toLowerCase() === query) return option\n }\n return null\n },\n [optionMap]\n )\n\n const addTag = React.useCallback(\n (raw: string) => {\n if (disabled) return\n const option = findOptionForInput(raw)\n if (option) {\n addValue(option.value)\n return\n }\n if (!allowCustomValues) return\n addValue(raw)\n },\n [addValue, allowCustomValues, disabled, findOptionForInput]\n )\n\n const removeTag = React.useCallback(\n (tag: string) => {\n if (disabled) return\n const next = valueRef.current.filter((candidate) => candidate !== tag)\n valueRef.current = next\n onChange(next)\n },\n [disabled, onChange]\n )\n\n return (\n <div\n className={[\n 'w-full rounded border px-2 py-1',\n disabled ? 'bg-muted text-muted-foreground/80 cursor-not-allowed' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n aria-disabled={disabled || undefined}\n >\n <div className=\"flex flex-wrap gap-1\">\n {value.map((tag) => {\n const option = optionMap.get(tag)\n const label = option?.label ?? tag\n const description = option?.description\n return (\n <span key={tag} className=\"inline-flex items-center gap-2 rounded-sm bg-muted px-2 py-0.5 text-xs\">\n <span className=\"flex flex-col items-start leading-tight\">\n <span className=\"whitespace-nowrap\">{label}</span>\n {description ? (\n <span className=\"text-overline text-muted-foreground\">{description}</span>\n ) : null}\n </span>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n className=\"opacity-60 hover:opacity-100\"\n onClick={() => removeTag(tag)}\n disabled={disabled}\n >\n \u00D7\n </IconButton>\n </span>\n )\n })}\n <input\n className=\"flex-1 min-w-[80px] sm:min-w-[120px] border-0 py-1 text-sm outline-none disabled:bg-transparent\"\n value={input}\n placeholder={placeholder || t('ui.inputs.tagsInput.placeholder', 'Add tag and press Enter')}\n autoFocus={autoFocus}\n data-crud-focus-target=\"\"\n disabled={disabled}\n onFocus={() => {\n if (showSuggestionsOnFocus) {\n setTouched(true)\n }\n }}\n onMouseDown={() => {\n setTouched(true)\n }}\n onChange={(event) => {\n setTouched(true)\n setInput(event.target.value)\n }}\n onKeyDown={(event) => {\n if (disabled) return\n if (event.key === 'Enter' || event.key === ',') {\n event.preventDefault()\n addTag(input)\n setInput('')\n } else if (event.key === 'Backspace' && input === '' && value.length > 0) {\n removeTag(value[value.length - 1])\n }\n }}\n onBlur={() => {\n if (disabled) return\n if (suppressBlurCommitRef.current) {\n suppressBlurCommitRef.current = false\n setInput('')\n return\n }\n addTag(input)\n setInput('')\n }}\n />\n {loading && touched ? (\n <div className=\"basis-full mt-1 text-xs text-muted-foreground\">Loading suggestions\u2026</div>\n ) : null}\n {!loading && filteredSuggestions.length ? (\n <div className=\"basis-full mt-1 flex flex-col gap-1\">\n {filteredSuggestions.map((option) => (\n <Button\n key={option.value}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-full justify-start font-normal flex flex-col items-start text-xs px-1.5 py-1\"\n onMouseDown={(event) => {\n suppressBlurCommitRef.current = true\n event.preventDefault()\n }}\n onClick={() => {\n suppressBlurCommitRef.current = false\n addValue(option.value)\n setInput('')\n }}\n >\n <span>{option.label}</span>\n {option.description ? (\n <span className=\"text-overline text-muted-foreground\">{option.description}</span>\n ) : null}\n </Button>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -88,7 +88,7 @@ function TimeInput({
|
|
|
88
88
|
);
|
|
89
89
|
const inputClass = cn(
|
|
90
90
|
"w-14 h-9 rounded border text-center text-sm tabular-nums",
|
|
91
|
-
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1",
|
|
91
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
92
92
|
disabled && "bg-muted text-muted-foreground cursor-not-allowed",
|
|
93
93
|
"disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed"
|
|
94
94
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/inputs/TimeInput.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type TimeInputProps = {\n value?: string | null\n onChange: (time: string) => void\n disabled?: boolean\n className?: string\n minuteStep?: number\n hourLabel?: string\n minuteLabel?: string\n}\n\nfunction padTwo(n: number): string {\n return String(n).padStart(2, '0')\n}\n\nfunction parseTime(value: string | null | undefined): { hour: number; minute: number } {\n if (!value) return { hour: 0, minute: 0 }\n const parts = value.split(':')\n const hour = parseInt(parts[0] ?? '0', 10)\n const minute = parseInt(parts[1] ?? '0', 10)\n return {\n hour: isNaN(hour) ? 0 : Math.max(0, Math.min(23, hour)),\n minute: isNaN(minute) ? 0 : Math.max(0, Math.min(59, minute)),\n }\n}\n\nfunction snapMinute(minute: number, step: number): number {\n if (step <= 1) return minute\n return Math.round(minute / step) * step % 60\n}\n\nexport function TimeInput({\n value,\n onChange,\n disabled = false,\n className,\n minuteStep = 1,\n hourLabel: hourLabelProp,\n minuteLabel: minuteLabelProp,\n}: TimeInputProps) {\n const t = useT()\n const hourLabel = hourLabelProp ?? t('ui.timePicker.hourLabel', 'Hour')\n const minuteLabel = minuteLabelProp ?? t('ui.timePicker.minuteLabel', 'Minute')\n\n const { hour, minute } = parseTime(value)\n\n const emitChange = React.useCallback(\n (nextHour: number, nextMinute: number) => {\n onChange(`${padTwo(nextHour)}:${padTwo(nextMinute)}`)\n },\n [onChange]\n )\n\n const handleHourKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n emitChange((hour + 1) % 24, minute)\n } else if (e.key === 'ArrowDown') {\n e.preventDefault()\n emitChange((hour + 23) % 24, minute)\n }\n },\n [disabled, emitChange, hour, minute]\n )\n\n const handleMinuteKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n const step = minuteStep > 1 ? minuteStep : 1\n emitChange(hour, (minute + step) % 60)\n } else if (e.key === 'ArrowDown') {\n e.preventDefault()\n const step = minuteStep > 1 ? minuteStep : 1\n emitChange(hour, (minute + 60 - step) % 60)\n }\n },\n [disabled, emitChange, hour, minute, minuteStep]\n )\n\n const handleHourChange = React.useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n if (disabled) return\n const raw = parseInt(e.target.value, 10)\n if (isNaN(raw)) return\n emitChange(Math.max(0, Math.min(23, raw)), minute)\n },\n [disabled, emitChange, minute]\n )\n\n const handleMinuteChange = React.useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n if (disabled) return\n const raw = parseInt(e.target.value, 10)\n if (isNaN(raw)) return\n const snapped = minuteStep > 1 ? snapMinute(raw, minuteStep) : Math.max(0, Math.min(59, raw))\n emitChange(hour, snapped)\n },\n [disabled, emitChange, hour, minuteStep]\n )\n\n const inputClass = cn(\n 'w-14 h-9 rounded border text-center text-sm tabular-nums',\n 'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1',\n disabled && 'bg-muted text-muted-foreground cursor-not-allowed',\n 'disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed'\n )\n\n return (\n <div className={cn('flex items-center gap-1', className)}>\n <input\n type=\"number\"\n min={0}\n max={23}\n value={padTwo(hour)}\n onChange={handleHourChange}\n onKeyDown={handleHourKeyDown}\n disabled={disabled}\n aria-label={hourLabel}\n data-crud-focus-target=\"\"\n className={inputClass}\n />\n <span className=\"text-sm font-medium select-none\">:</span>\n <input\n type=\"number\"\n min={0}\n max={59}\n step={minuteStep}\n value={padTwo(minute)}\n onChange={handleMinuteChange}\n onKeyDown={handleMinuteKeyDown}\n disabled={disabled}\n aria-label={minuteLabel}\n className={inputClass}\n />\n </div>\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type TimeInputProps = {\n value?: string | null\n onChange: (time: string) => void\n disabled?: boolean\n className?: string\n minuteStep?: number\n hourLabel?: string\n minuteLabel?: string\n}\n\nfunction padTwo(n: number): string {\n return String(n).padStart(2, '0')\n}\n\nfunction parseTime(value: string | null | undefined): { hour: number; minute: number } {\n if (!value) return { hour: 0, minute: 0 }\n const parts = value.split(':')\n const hour = parseInt(parts[0] ?? '0', 10)\n const minute = parseInt(parts[1] ?? '0', 10)\n return {\n hour: isNaN(hour) ? 0 : Math.max(0, Math.min(23, hour)),\n minute: isNaN(minute) ? 0 : Math.max(0, Math.min(59, minute)),\n }\n}\n\nfunction snapMinute(minute: number, step: number): number {\n if (step <= 1) return minute\n return Math.round(minute / step) * step % 60\n}\n\nexport function TimeInput({\n value,\n onChange,\n disabled = false,\n className,\n minuteStep = 1,\n hourLabel: hourLabelProp,\n minuteLabel: minuteLabelProp,\n}: TimeInputProps) {\n const t = useT()\n const hourLabel = hourLabelProp ?? t('ui.timePicker.hourLabel', 'Hour')\n const minuteLabel = minuteLabelProp ?? t('ui.timePicker.minuteLabel', 'Minute')\n\n const { hour, minute } = parseTime(value)\n\n const emitChange = React.useCallback(\n (nextHour: number, nextMinute: number) => {\n onChange(`${padTwo(nextHour)}:${padTwo(nextMinute)}`)\n },\n [onChange]\n )\n\n const handleHourKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n emitChange((hour + 1) % 24, minute)\n } else if (e.key === 'ArrowDown') {\n e.preventDefault()\n emitChange((hour + 23) % 24, minute)\n }\n },\n [disabled, emitChange, hour, minute]\n )\n\n const handleMinuteKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n const step = minuteStep > 1 ? minuteStep : 1\n emitChange(hour, (minute + step) % 60)\n } else if (e.key === 'ArrowDown') {\n e.preventDefault()\n const step = minuteStep > 1 ? minuteStep : 1\n emitChange(hour, (minute + 60 - step) % 60)\n }\n },\n [disabled, emitChange, hour, minute, minuteStep]\n )\n\n const handleHourChange = React.useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n if (disabled) return\n const raw = parseInt(e.target.value, 10)\n if (isNaN(raw)) return\n emitChange(Math.max(0, Math.min(23, raw)), minute)\n },\n [disabled, emitChange, minute]\n )\n\n const handleMinuteChange = React.useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n if (disabled) return\n const raw = parseInt(e.target.value, 10)\n if (isNaN(raw)) return\n const snapped = minuteStep > 1 ? snapMinute(raw, minuteStep) : Math.max(0, Math.min(59, raw))\n emitChange(hour, snapped)\n },\n [disabled, emitChange, hour, minuteStep]\n )\n\n const inputClass = cn(\n 'w-14 h-9 rounded border text-center text-sm tabular-nums',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n disabled && 'bg-muted text-muted-foreground cursor-not-allowed',\n 'disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed'\n )\n\n return (\n <div className={cn('flex items-center gap-1', className)}>\n <input\n type=\"number\"\n min={0}\n max={23}\n value={padTwo(hour)}\n onChange={handleHourChange}\n onKeyDown={handleHourKeyDown}\n disabled={disabled}\n aria-label={hourLabel}\n data-crud-focus-target=\"\"\n className={inputClass}\n />\n <span className=\"text-sm font-medium select-none\">:</span>\n <input\n type=\"number\"\n min={0}\n max={59}\n step={minuteStep}\n value={padTwo(minute)}\n onChange={handleMinuteChange}\n onKeyDown={handleMinuteKeyDown}\n disabled={disabled}\n aria-label={minuteLabel}\n className={inputClass}\n />\n </div>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -54,7 +54,7 @@ function TimePicker({
|
|
|
54
54
|
className: cn(
|
|
55
55
|
"w-full h-9 flex items-center gap-2 rounded border px-3 text-sm text-left",
|
|
56
56
|
"bg-background transition-colors",
|
|
57
|
-
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1",
|
|
57
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
58
58
|
"disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
|
|
59
59
|
readOnly && "cursor-default opacity-70",
|
|
60
60
|
!value && "text-muted-foreground",
|
|
@@ -82,7 +82,7 @@ function TimePicker({
|
|
|
82
82
|
{
|
|
83
83
|
type: "button",
|
|
84
84
|
onClick: handleNow,
|
|
85
|
-
className: "text-sm text-primary hover:underline focus:outline-none",
|
|
85
|
+
className: "text-sm text-primary hover:underline focus-visible:outline-none",
|
|
86
86
|
children: nowText
|
|
87
87
|
}
|
|
88
88
|
),
|
|
@@ -91,7 +91,7 @@ function TimePicker({
|
|
|
91
91
|
{
|
|
92
92
|
type: "button",
|
|
93
93
|
onClick: handleClear,
|
|
94
|
-
className: "text-sm text-muted-foreground hover:text-foreground hover:underline focus:outline-none ml-auto",
|
|
94
|
+
className: "text-sm text-muted-foreground hover:text-foreground hover:underline focus-visible:outline-none ml-auto",
|
|
95
95
|
children: clearText
|
|
96
96
|
}
|
|
97
97
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/inputs/TimePicker.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ClockIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover'\nimport { TimeInput } from './TimeInput'\n\nexport type TimePickerProps = {\n value?: string | null\n onChange: (time: string | null) => void\n placeholder?: string\n disabled?: boolean\n readOnly?: boolean\n className?: string\n minuteStep?: number\n showNowButton?: boolean\n showClearButton?: boolean\n}\n\nfunction currentHHMM(): string {\n const now = new Date()\n const hour = String(now.getHours()).padStart(2, '0')\n const minute = String(now.getMinutes()).padStart(2, '0')\n return `${hour}:${minute}`\n}\n\nexport function TimePicker({\n value,\n onChange,\n placeholder,\n disabled = false,\n readOnly = false,\n className,\n minuteStep = 1,\n showNowButton = true,\n showClearButton = true,\n}: TimePickerProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n\n const placeholderText = placeholder ?? t('ui.timePicker.placeholder', 'Pick a time')\n const nowText = t('ui.timePicker.nowButton', 'Now')\n const clearText = t('ui.timePicker.clearButton', 'Clear')\n\n const handleTimeChange = React.useCallback(\n (time: string) => {\n onChange(time)\n },\n [onChange]\n )\n\n const handleNow = React.useCallback(() => {\n onChange(currentHHMM())\n setOpen(false)\n }, [onChange])\n\n const handleClear = React.useCallback(() => {\n onChange(null)\n setOpen(false)\n }, [onChange])\n\n const isInteractive = !disabled && !readOnly\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n data-crud-focus-target=\"\"\n disabled={disabled}\n aria-haspopup=\"dialog\"\n className={cn(\n 'w-full h-9 flex items-center gap-2 rounded border px-3 text-sm text-left',\n 'bg-background transition-colors',\n 'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1',\n 'disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed',\n readOnly && 'cursor-default opacity-70',\n !value && 'text-muted-foreground',\n className\n )}\n onClick={isInteractive ? undefined : (e) => e.preventDefault()}\n >\n <ClockIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 truncate\">{value ?? placeholderText}</span>\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"p-3 w-auto min-w-[180px]\">\n <TimeInput\n value={value}\n onChange={handleTimeChange}\n minuteStep={minuteStep}\n />\n {(showNowButton || showClearButton) && (\n <div className=\"flex items-center justify-between gap-2 mt-3 pt-2 border-t\">\n {showNowButton && (\n <button\n type=\"button\"\n onClick={handleNow}\n className=\"text-sm text-primary hover:underline focus:outline-none\"\n >\n {nowText}\n </button>\n )}\n {showClearButton && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"text-sm text-muted-foreground hover:text-foreground hover:underline focus:outline-none ml-auto\"\n >\n {clearText}\n </button>\n )}\n </div>\n )}\n </PopoverContent>\n </Popover>\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ClockIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover'\nimport { TimeInput } from './TimeInput'\n\nexport type TimePickerProps = {\n value?: string | null\n onChange: (time: string | null) => void\n placeholder?: string\n disabled?: boolean\n readOnly?: boolean\n className?: string\n minuteStep?: number\n showNowButton?: boolean\n showClearButton?: boolean\n}\n\nfunction currentHHMM(): string {\n const now = new Date()\n const hour = String(now.getHours()).padStart(2, '0')\n const minute = String(now.getMinutes()).padStart(2, '0')\n return `${hour}:${minute}`\n}\n\nexport function TimePicker({\n value,\n onChange,\n placeholder,\n disabled = false,\n readOnly = false,\n className,\n minuteStep = 1,\n showNowButton = true,\n showClearButton = true,\n}: TimePickerProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n\n const placeholderText = placeholder ?? t('ui.timePicker.placeholder', 'Pick a time')\n const nowText = t('ui.timePicker.nowButton', 'Now')\n const clearText = t('ui.timePicker.clearButton', 'Clear')\n\n const handleTimeChange = React.useCallback(\n (time: string) => {\n onChange(time)\n },\n [onChange]\n )\n\n const handleNow = React.useCallback(() => {\n onChange(currentHHMM())\n setOpen(false)\n }, [onChange])\n\n const handleClear = React.useCallback(() => {\n onChange(null)\n setOpen(false)\n }, [onChange])\n\n const isInteractive = !disabled && !readOnly\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n data-crud-focus-target=\"\"\n disabled={disabled}\n aria-haspopup=\"dialog\"\n className={cn(\n 'w-full h-9 flex items-center gap-2 rounded border px-3 text-sm text-left',\n 'bg-background transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n 'disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed',\n readOnly && 'cursor-default opacity-70',\n !value && 'text-muted-foreground',\n className\n )}\n onClick={isInteractive ? undefined : (e) => e.preventDefault()}\n >\n <ClockIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 truncate\">{value ?? placeholderText}</span>\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"p-3 w-auto min-w-[180px]\">\n <TimeInput\n value={value}\n onChange={handleTimeChange}\n minuteStep={minuteStep}\n />\n {(showNowButton || showClearButton) && (\n <div className=\"flex items-center justify-between gap-2 mt-3 pt-2 border-t\">\n {showNowButton && (\n <button\n type=\"button\"\n onClick={handleNow}\n className=\"text-sm text-primary hover:underline focus-visible:outline-none\"\n >\n {nowText}\n </button>\n )}\n {showClearButton && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"text-sm text-muted-foreground hover:text-foreground hover:underline focus-visible:outline-none ml-auto\"\n >\n {clearText}\n </button>\n )}\n </div>\n )}\n </PopoverContent>\n </Popover>\n )\n}\n"],
|
|
5
5
|
"mappings": ";AAoEQ,SAgBE,KAhBF;AAlER,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,UAAU;AACnB,SAAS,YAAY;AACrB,SAAS,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,iBAAiB;AAc1B,SAAS,cAAsB;AAC7B,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,OAAO,OAAO,IAAI,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG;AACnD,QAAM,SAAS,OAAO,IAAI,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;AACvD,SAAO,GAAG,IAAI,IAAI,MAAM;AAC1B;AAEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,WAAW;AAAA,EACX;AAAA,EACA,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,kBAAkB;AACpB,GAAoB;AAClB,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAE5C,QAAM,kBAAkB,eAAe,EAAE,6BAA6B,aAAa;AACnF,QAAM,UAAU,EAAE,2BAA2B,KAAK;AAClD,QAAM,YAAY,EAAE,6BAA6B,OAAO;AAExD,QAAM,mBAAmB,MAAM;AAAA,IAC7B,CAAC,SAAiB;AAChB,eAAS,IAAI;AAAA,IACf;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,YAAY,MAAM,YAAY,MAAM;AACxC,aAAS,YAAY,CAAC;AACtB,YAAQ,KAAK;AAAA,EACf,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,aAAS,IAAI;AACb,YAAQ,KAAK;AAAA,EACf,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,gBAAgB,CAAC,YAAY,CAAC;AAEpC,SACE,qBAAC,WAAQ,MAAY,cAAc,gBAAgB,UAAU,QAC3D;AAAA,wBAAC,kBAAe,SAAO,MACrB;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,0BAAuB;AAAA,QACvB;AAAA,QACA,iBAAc;AAAA,QACd,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,YAAY;AAAA,UACZ,CAAC,SAAS;AAAA,UACV;AAAA,QACF;AAAA,QACA,SAAS,gBAAgB,SAAY,CAAC,MAAM,EAAE,eAAe;AAAA,QAE7D;AAAA,8BAAC,aAAU,WAAU,0CAAyC;AAAA,UAC9D,oBAAC,UAAK,WAAU,mBAAmB,mBAAS,iBAAgB;AAAA;AAAA;AAAA,IAC9D,GACF;AAAA,IACA,qBAAC,kBAAe,WAAU,4BACxB;AAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,UAAU;AAAA,UACV;AAAA;AAAA,MACF;AAAA,OACE,iBAAiB,oBACjB,qBAAC,SAAI,WAAU,8DACZ;AAAA,yBACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YACT,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,QAED,mBACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YACT,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,SAEJ;AAAA,OAEJ;AAAA,KACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -32,7 +32,7 @@ function MessageObjectDetail(props) {
|
|
|
32
32
|
Link,
|
|
33
33
|
{
|
|
34
34
|
href: resolveActionHref(viewAction.href, props.entityId),
|
|
35
|
-
className: "block rounded-md transition-opacity hover:opacity-
|
|
35
|
+
className: "block rounded-md transition-opacity hover:opacity-80",
|
|
36
36
|
children: preview
|
|
37
37
|
}
|
|
38
38
|
) : preview,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/messages/MessageObjectDetail.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectDetailProps } from '@open-mercato/shared/modules/messages/types'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { MessageObjectPreview } from './MessageObjectPreview'\n\nfunction resolveActionHref(template: string, entityId: string): string {\n return template.replace('{entityId}', encodeURIComponent(entityId))\n}\n\nexport function MessageObjectDetail(props: ObjectDetailProps) {\n const t = useT()\n const [executingActionId, setExecutingActionId] = React.useState<string | null>(null)\n\n const viewAction = props.actions.find((a) => a.id === 'view')\n const otherActions = props.actions.filter((a) => a.id !== 'view')\n\n const preview = (\n <MessageObjectPreview\n entityId={props.entityId}\n entityModule={props.entityModule}\n entityType={props.entityType}\n snapshot={props.snapshot}\n previewData={props.previewData}\n actionRequired={props.actionRequired}\n actionType={props.actionType}\n actionLabel={props.actionLabel}\n icon={props.icon}\n />\n )\n\n return (\n <div className=\"space-y-3 rounded border p-3\">\n {viewAction?.href ? (\n <Link\n href={resolveActionHref(viewAction.href, props.entityId)}\n className=\"block rounded-md transition-opacity hover:opacity-
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectDetailProps } from '@open-mercato/shared/modules/messages/types'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { MessageObjectPreview } from './MessageObjectPreview'\n\nfunction resolveActionHref(template: string, entityId: string): string {\n return template.replace('{entityId}', encodeURIComponent(entityId))\n}\n\nexport function MessageObjectDetail(props: ObjectDetailProps) {\n const t = useT()\n const [executingActionId, setExecutingActionId] = React.useState<string | null>(null)\n\n const viewAction = props.actions.find((a) => a.id === 'view')\n const otherActions = props.actions.filter((a) => a.id !== 'view')\n\n const preview = (\n <MessageObjectPreview\n entityId={props.entityId}\n entityModule={props.entityModule}\n entityType={props.entityType}\n snapshot={props.snapshot}\n previewData={props.previewData}\n actionRequired={props.actionRequired}\n actionType={props.actionType}\n actionLabel={props.actionLabel}\n icon={props.icon}\n />\n )\n\n return (\n <div className=\"space-y-3 rounded border p-3\">\n {viewAction?.href ? (\n <Link\n href={resolveActionHref(viewAction.href, props.entityId)}\n className=\"block rounded-md transition-opacity hover:opacity-80\"\n >\n {preview}\n </Link>\n ) : (\n preview\n )}\n\n {otherActions.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {otherActions.map((action) => {\n if (action.href) {\n return (\n <Button\n key={action.id}\n type=\"button\"\n size=\"sm\"\n variant={action.variant ?? 'default'}\n asChild\n >\n <Link href={resolveActionHref(action.href, props.entityId)}>\n {t(action.labelKey ?? action.id, action.id)}\n </Link>\n </Button>\n )\n }\n return (\n <Button\n key={action.id}\n type=\"button\"\n size=\"sm\"\n variant={action.variant ?? 'default'}\n disabled={executingActionId !== null}\n onClick={async () => {\n if (executingActionId) return\n setExecutingActionId(action.id)\n try {\n await props.onAction(action.id, { id: props.entityId })\n } finally {\n setExecutingActionId(null)\n }\n }}\n >\n {executingActionId === action.id\n ? t('messages.actions.executing', 'Executing...')\n : t(action.labelKey ?? action.id, action.id)}\n </Button>\n )\n })}\n </div>\n ) : null}\n </div>\n )\n}\n\nexport default MessageObjectDetail\n"],
|
|
5
5
|
"mappings": ";AAqBI,cAcA,YAdA;AAnBJ,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,YAAY;AAErB,SAAS,cAAc;AACvB,SAAS,4BAA4B;AAErC,SAAS,kBAAkB,UAAkB,UAA0B;AACrE,SAAO,SAAS,QAAQ,cAAc,mBAAmB,QAAQ,CAAC;AACpE;AAEO,SAAS,oBAAoB,OAA0B;AAC5D,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAwB,IAAI;AAEpF,QAAM,aAAa,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AAC5D,QAAM,eAAe,MAAM,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,MAAM;AAEhE,QAAM,UACJ;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,MAAM;AAAA,MAChB,cAAc,MAAM;AAAA,MACpB,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM;AAAA,MACnB,gBAAgB,MAAM;AAAA,MACtB,YAAY,MAAM;AAAA,MAClB,aAAa,MAAM;AAAA,MACnB,MAAM,MAAM;AAAA;AAAA,EACd;AAGF,SACE,qBAAC,SAAI,WAAU,gCACZ;AAAA,gBAAY,OACX;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,kBAAkB,WAAW,MAAM,MAAM,QAAQ;AAAA,QACvD,WAAU;AAAA,QAET;AAAA;AAAA,IACH,IAEA;AAAA,IAGD,aAAa,SAAS,IACrB,oBAAC,SAAI,WAAU,wBACZ,uBAAa,IAAI,CAAC,WAAW;AAC5B,UAAI,OAAO,MAAM;AACf,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,SAAS,OAAO,WAAW;AAAA,YAC3B,SAAO;AAAA,YAEP,8BAAC,QAAK,MAAM,kBAAkB,OAAO,MAAM,MAAM,QAAQ,GACtD,YAAE,OAAO,YAAY,OAAO,IAAI,OAAO,EAAE,GAC5C;AAAA;AAAA,UARK,OAAO;AAAA,QASd;AAAA,MAEJ;AACA,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,SAAS,OAAO,WAAW;AAAA,UAC3B,UAAU,sBAAsB;AAAA,UAChC,SAAS,YAAY;AACnB,gBAAI,kBAAmB;AACvB,iCAAqB,OAAO,EAAE;AAC9B,gBAAI;AACF,oBAAM,MAAM,SAAS,OAAO,IAAI,EAAE,IAAI,MAAM,SAAS,CAAC;AAAA,YACxD,UAAE;AACA,mCAAqB,IAAI;AAAA,YAC3B;AAAA,UACF;AAAA,UAEC,gCAAsB,OAAO,KAC1B,EAAE,8BAA8B,cAAc,IAC9C,EAAE,OAAO,YAAY,OAAO,IAAI,OAAO,EAAE;AAAA;AAAA,QAjBxC,OAAO;AAAA,MAkBd;AAAA,IAEJ,CAAC,GACH,IACE;AAAA,KACN;AAEJ;AAEA,IAAO,8BAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -15,7 +15,7 @@ function MessageObjectPreview({
|
|
|
15
15
|
}) {
|
|
16
16
|
const t = useT();
|
|
17
17
|
const Icon = resolveIcon(icon);
|
|
18
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-md border bg-muted/
|
|
18
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-md border bg-muted/30 p-3", children: [
|
|
19
19
|
/* @__PURE__ */ jsx(Icon, { className: "mt-0.5 h-4 w-4 text-muted-foreground" }),
|
|
20
20
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
|
|
21
21
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/messages/MessageObjectPreview.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport { Box, type LucideIcon } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectPreviewProps } from '@open-mercato/shared/modules/messages/types'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { resolveRegisteredLucideIcon } from '../icons/lucideRegistry'\n\nfunction resolveIcon(name: string | undefined): LucideIcon {\n return resolveRegisteredLucideIcon(name) ?? Box\n}\n\nexport function MessageObjectPreview({\n previewData,\n actionRequired,\n actionLabel,\n icon,\n}: ObjectPreviewProps) {\n const t = useT()\n const Icon = resolveIcon(icon)\n\n return (\n <div className=\"flex items-start gap-3 rounded-md border bg-muted/
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport { Box, type LucideIcon } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectPreviewProps } from '@open-mercato/shared/modules/messages/types'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { resolveRegisteredLucideIcon } from '../icons/lucideRegistry'\n\nfunction resolveIcon(name: string | undefined): LucideIcon {\n return resolveRegisteredLucideIcon(name) ?? Box\n}\n\nexport function MessageObjectPreview({\n previewData,\n actionRequired,\n actionLabel,\n icon,\n}: ObjectPreviewProps) {\n const t = useT()\n const Icon = resolveIcon(icon)\n\n return (\n <div className=\"flex items-start gap-3 rounded-md border bg-muted/30 p-3\">\n <Icon className=\"mt-0.5 h-4 w-4 text-muted-foreground\" />\n <div className=\"min-w-0 flex-1 space-y-1\">\n <div className=\"flex items-center gap-2\">\n <p className=\"truncate text-sm font-medium\">{previewData?.title || ''}</p>\n {actionRequired ? (\n <Badge variant=\"secondary\" className=\"text-xs\">\n {actionLabel || t('messages.composer.objectActionRequired', 'Action required')}\n </Badge>\n ) : null}\n </div>\n {previewData?.subtitle ? (\n <p className=\"truncate text-xs text-muted-foreground\">{previewData.subtitle}</p>\n ) : null}\n {previewData?.status ? (\n <Badge variant=\"outline\" className=\"text-xs\">{previewData.status}</Badge>\n ) : null}\n {previewData?.metadata && Object.keys(previewData.metadata).length > 0 ? (\n <dl className=\"space-y-1 pt-1\">\n {Object.entries(previewData.metadata).map(([key, value]) => (\n <div key={key} className=\"flex items-start gap-2 text-xs text-muted-foreground\">\n <dt className=\"font-medium capitalize\">{key}:</dt>\n <dd className=\"truncate\">{value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport default MessageObjectPreview\n"],
|
|
5
5
|
"mappings": ";AAuBM,cAEE,YAFF;AArBN,SAAS,WAA4B;AACrC,SAAS,YAAY;AAErB,SAAS,aAAa;AACtB,SAAS,mCAAmC;AAE5C,SAAS,YAAY,MAAsC;AACzD,SAAO,4BAA4B,IAAI,KAAK;AAC9C;AAEO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,IAAI,KAAK;AACf,QAAM,OAAO,YAAY,IAAI;AAE7B,SACE,qBAAC,SAAI,WAAU,4DACb;AAAA,wBAAC,QAAK,WAAU,wCAAuC;AAAA,IACvD,qBAAC,SAAI,WAAU,4BACb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,OAAE,WAAU,gCAAgC,uBAAa,SAAS,IAAG;AAAA,QACrE,iBACC,oBAAC,SAAM,SAAQ,aAAY,WAAU,WAClC,yBAAe,EAAE,0CAA0C,iBAAiB,GAC/E,IACE;AAAA,SACN;AAAA,MACC,aAAa,WACZ,oBAAC,OAAE,WAAU,0CAA0C,sBAAY,UAAS,IAC1E;AAAA,MACH,aAAa,SACZ,oBAAC,SAAM,SAAQ,WAAU,WAAU,WAAW,sBAAY,QAAO,IAC/D;AAAA,MACH,aAAa,YAAY,OAAO,KAAK,YAAY,QAAQ,EAAE,SAAS,IACnE,oBAAC,QAAG,WAAU,kBACX,iBAAO,QAAQ,YAAY,QAAQ,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MACpD,qBAAC,SAAc,WAAU,wDACvB;AAAA,6BAAC,QAAG,WAAU,0BAA0B;AAAA;AAAA,UAAI;AAAA,WAAC;AAAA,QAC7C,oBAAC,QAAG,WAAU,YAAY,iBAAM;AAAA,WAFxB,GAGV,CACD,GACH,IACE;AAAA,OACN;AAAA,KACF;AAEJ;AAEA,IAAO,+BAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -194,7 +194,7 @@ function ComposeModeFields({ compose }) {
|
|
|
194
194
|
placeholder: compose.t("messages.placeholders.body", "Write your message..."),
|
|
195
195
|
inputId: "messages-compose-body",
|
|
196
196
|
rows: 8,
|
|
197
|
-
textareaClassName: "min-h-[180px] w-full rounded-md border bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
197
|
+
textareaClassName: "min-h-[180px] w-full rounded-md border bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
198
198
|
}
|
|
199
199
|
),
|
|
200
200
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
@@ -224,7 +224,7 @@ function ReplyModeFields({ compose }) {
|
|
|
224
224
|
placeholder: compose.t("messages.placeholders.replyBody", "Write your reply..."),
|
|
225
225
|
inputId: "messages-compose-body",
|
|
226
226
|
rows: 8,
|
|
227
|
-
textareaClassName: "min-h-[180px] w-full rounded-md border bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
227
|
+
textareaClassName: "min-h-[180px] w-full rounded-md border bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
228
228
|
}
|
|
229
229
|
),
|
|
230
230
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
@@ -258,7 +258,7 @@ function ForwardModeFields({ compose }) {
|
|
|
258
258
|
placeholder: compose.t("messages.placeholders.forwardContent", "Review and edit forwarded content..."),
|
|
259
259
|
inputId: "messages-forward-note",
|
|
260
260
|
rows: 6,
|
|
261
|
-
textareaClassName: "min-h-[140px] w-full rounded-md border bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
261
|
+
textareaClassName: "min-h-[140px] w-full rounded-md border bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
262
262
|
}
|
|
263
263
|
)
|
|
264
264
|
] });
|