@open-mercato/ui 0.6.7 → 0.6.8-develop.6874.1.982d6097d8
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 +4 -2
- package/agentic/standalone-guide.md +1 -1
- package/build.mjs +2 -54
- package/dist/ai/AiChat.js +26 -7
- package/dist/ai/AiChat.js.map +2 -2
- package/dist/ai/LoopDisabledBanner.js +1 -1
- package/dist/ai/LoopDisabledBanner.js.map +2 -2
- package/dist/ai/parts/ConfirmationCard.js +4 -4
- package/dist/ai/parts/ConfirmationCard.js.map +2 -2
- package/dist/ai/parts/FieldDiffCard.js +2 -2
- package/dist/ai/parts/FieldDiffCard.js.map +2 -2
- package/dist/ai/parts/MutationResultCard.js +3 -3
- package/dist/ai/parts/MutationResultCard.js.map +2 -2
- package/dist/ai/ui-parts/pending-phase3-placeholder.js +1 -1
- package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +2 -2
- package/dist/backend/AppShell.js +43 -4
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/AuthSessionGuard.js +14 -0
- package/dist/backend/AuthSessionGuard.js.map +2 -2
- package/dist/backend/BackendChromeProvider.js +6 -1
- package/dist/backend/BackendChromeProvider.js.map +2 -2
- package/dist/backend/CrudForm.js +6 -4
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/DataTable.js +20 -18
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/JsonBuilder.js +3 -3
- package/dist/backend/JsonBuilder.js.map +2 -2
- package/dist/backend/NextStepCallout.js +4 -4
- package/dist/backend/NextStepCallout.js.map +1 -1
- package/dist/backend/PerspectiveSidebar.js +1 -1
- package/dist/backend/PerspectiveSidebar.js.map +2 -2
- package/dist/backend/ProfileDropdown.js +2 -1
- package/dist/backend/ProfileDropdown.js.map +2 -2
- package/dist/backend/RowActions.js +2 -2
- package/dist/backend/RowActions.js.map +2 -2
- package/dist/backend/confirm-dialog/ConfirmDialog.js +13 -1
- package/dist/backend/confirm-dialog/ConfirmDialog.js.map +2 -2
- package/dist/backend/crud/useGroupOrder.js +14 -21
- package/dist/backend/crud/useGroupOrder.js.map +2 -2
- package/dist/backend/custom-fields/FieldDefinitionsManager.js +1 -1
- package/dist/backend/custom-fields/FieldDefinitionsManager.js.map +2 -2
- package/dist/backend/dashboard/DashboardScreen.js +3 -3
- package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
- package/dist/backend/detail/AddressTiles.js +4 -5
- package/dist/backend/detail/AddressTiles.js.map +2 -2
- package/dist/backend/detail/AttachmentDeleteDialog.js +1 -1
- package/dist/backend/detail/AttachmentDeleteDialog.js.map +2 -2
- package/dist/backend/detail/AttachmentMetadataDialog.js.map +1 -1
- package/dist/backend/detail/AttachmentsSection.js +1 -1
- package/dist/backend/detail/AttachmentsSection.js.map +2 -2
- package/dist/backend/detail/NotesSection.js +1 -1
- package/dist/backend/detail/NotesSection.js.map +2 -2
- package/dist/backend/detail/TagsSection.js +1 -1
- package/dist/backend/detail/TagsSection.js.map +2 -2
- package/dist/backend/filters/AdvancedFilterPanel.js +1 -1
- package/dist/backend/filters/AdvancedFilterPanel.js.map +2 -2
- package/dist/backend/forms/FormHeader.js +1 -2
- package/dist/backend/forms/FormHeader.js.map +2 -2
- package/dist/backend/icons/lucideRegistry.generated.js +1 -25
- package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
- package/dist/backend/icons/lucideRegistry.js +3 -3
- package/dist/backend/icons/lucideRegistry.js.map +2 -2
- package/dist/backend/icons/lucideRegistryRuntime.js +28 -0
- package/dist/backend/icons/lucideRegistryRuntime.js.map +7 -0
- package/dist/backend/injection/spotIds.js +23 -18
- package/dist/backend/injection/spotIds.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js.map +1 -1
- package/dist/backend/messages/EmailThreadsPanel.js +1 -1
- package/dist/backend/messages/EmailThreadsPanel.js.map +2 -2
- package/dist/backend/perspectiveState.js +34 -0
- package/dist/backend/perspectiveState.js.map +7 -0
- package/dist/backend/schedule/ScheduleAgenda.js +3 -3
- package/dist/backend/schedule/ScheduleAgenda.js.map +1 -1
- package/dist/backend/schedule/ScheduleGrid.js +3 -3
- package/dist/backend/schedule/ScheduleGrid.js.map +1 -1
- package/dist/backend/schedule/ScheduleToolbar.js +87 -70
- package/dist/backend/schedule/ScheduleToolbar.js.map +2 -2
- package/dist/backend/sidebar/SidebarCustomizationEditor.js +30 -2
- package/dist/backend/sidebar/SidebarCustomizationEditor.js.map +2 -2
- package/dist/backend/sidebar/customization-helpers.js +30 -0
- package/dist/backend/sidebar/customization-helpers.js.map +2 -2
- package/dist/backend/utils/apiCall.js +21 -1
- package/dist/backend/utils/apiCall.js.map +2 -2
- package/dist/backend/utils/customFieldDefs.js.map +2 -2
- package/dist/backend/utils/customFieldForms.js +10 -9
- package/dist/backend/utils/customFieldForms.js.map +2 -2
- package/dist/backend/utils/fetchAllPages.js +57 -0
- package/dist/backend/utils/fetchAllPages.js.map +7 -0
- package/dist/backend/utils/nav.js +5 -2
- package/dist/backend/utils/nav.js.map +2 -2
- package/dist/backend/version-history/VersionHistoryPanel.js +1 -1
- package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
- package/dist/frontend/LanguageSwitcher.js +2 -1
- package/dist/frontend/LanguageSwitcher.js.map +2 -2
- package/dist/portal/PortalContext.js +1 -1
- package/dist/portal/PortalContext.js.map +2 -2
- package/dist/portal/hooks/portalBridgeStatus.js +25 -0
- package/dist/portal/hooks/portalBridgeStatus.js.map +7 -0
- package/dist/portal/hooks/usePortalEventBridge.js +6 -0
- package/dist/portal/hooks/usePortalEventBridge.js.map +2 -2
- package/dist/portal/hooks/usePortalNotifications.js +42 -4
- package/dist/portal/hooks/usePortalNotifications.js.map +2 -2
- package/dist/portal/utils/nav.js +2 -3
- package/dist/portal/utils/nav.js.map +2 -2
- package/dist/primitives/ErrorNotice.js +1 -1
- package/dist/primitives/ErrorNotice.js.map +2 -2
- package/dist/primitives/Notice.js +1 -1
- package/dist/primitives/Notice.js.map +1 -1
- package/dist/primitives/button.js +9 -4
- package/dist/primitives/button.js.map +2 -2
- package/dist/theme/ThemeProvider.js +1 -1
- package/dist/theme/ThemeProvider.js.map +2 -2
- package/dist/theme/index.js +3 -0
- package/dist/theme/index.js.map +2 -2
- package/dist/theme/theme-init-script.js +17 -0
- package/dist/theme/theme-init-script.js.map +7 -0
- package/figma/alert.figma.tsx +35 -0
- package/figma/badge.figma.tsx +33 -0
- package/figma/button.figma.tsx +33 -0
- package/figma/checkbox.figma.tsx +28 -0
- package/figma/drawer.figma.tsx +40 -0
- package/figma/input-variants.figma.tsx +58 -0
- package/figma/radio.figma.tsx +16 -0
- package/figma/select.figma.tsx +24 -0
- package/figma/status-badge.figma.tsx +24 -0
- package/figma/switch.figma.tsx +28 -0
- package/figma/tabs.figma.tsx +22 -0
- package/figma/tag.figma.tsx +27 -0
- package/figma.config.json +6 -0
- package/jest.config.cjs +2 -1
- package/package.json +21 -18
- package/scripts/lucideRegistrySource.cjs +133 -0
- package/src/ai/AiChat.tsx +36 -7
- package/src/ai/LoopDisabledBanner.tsx +1 -1
- package/src/ai/__tests__/AiChat.test.tsx +31 -0
- package/src/ai/parts/ConfirmationCard.tsx +4 -4
- package/src/ai/parts/FieldDiffCard.tsx +2 -2
- package/src/ai/parts/MutationResultCard.tsx +3 -3
- package/src/ai/ui-parts/pending-phase3-placeholder.tsx +2 -2
- package/src/backend/AppShell.tsx +55 -4
- package/src/backend/AuthSessionGuard.tsx +29 -0
- package/src/backend/BackendChromeProvider.tsx +19 -1
- package/src/backend/CrudForm.tsx +18 -4
- package/src/backend/DataTable.tsx +36 -19
- package/src/backend/JsonBuilder.tsx +3 -3
- package/src/backend/NextStepCallout.tsx +4 -4
- package/src/backend/PerspectiveSidebar.tsx +1 -1
- package/src/backend/ProfileDropdown.tsx +1 -0
- package/src/backend/RowActions.tsx +2 -2
- package/src/backend/__tests__/AppShell.test.tsx +65 -7
- package/src/backend/__tests__/AuthSessionGuard.test.tsx +52 -0
- package/src/backend/__tests__/BackendChromeProvider.test.tsx +98 -2
- package/src/backend/__tests__/CrudForm.fieldsetBinding.test.tsx +158 -0
- package/src/backend/__tests__/CrudForm.groupInjectionColumn.test.tsx +98 -0
- package/src/backend/__tests__/DataTable.metaHiddenLateColumns.test.tsx +105 -0
- package/src/backend/__tests__/DataTable.perspectiveStorage.test.ts +62 -1
- package/src/backend/__tests__/nav-utils.test.ts +139 -0
- package/src/backend/confirm-dialog/ConfirmDialog.tsx +16 -1
- package/src/backend/confirm-dialog/__tests__/ConfirmDialog.test.tsx +68 -1
- package/src/backend/crud/__tests__/useGroupOrder.test.ts +39 -1
- package/src/backend/crud/useGroupOrder.ts +30 -25
- package/src/backend/custom-fields/FieldDefinitionsManager.tsx +1 -1
- package/src/backend/dashboard/DashboardScreen.tsx +3 -3
- package/src/backend/detail/AddressTiles.tsx +3 -6
- package/src/backend/detail/AttachmentDeleteDialog.tsx +1 -1
- package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
- package/src/backend/detail/AttachmentsSection.tsx +1 -1
- package/src/backend/detail/NotesSection.tsx +1 -1
- package/src/backend/detail/TagsSection.tsx +1 -1
- package/src/backend/detail/__tests__/AttachmentAssignmentsEditor.test.tsx +21 -0
- package/src/backend/filters/AdvancedFilterPanel.tsx +1 -1
- package/src/backend/forms/FormHeader.tsx +1 -2
- package/src/backend/icons/__tests__/lucideRegistryGenerator.test.ts +177 -0
- package/src/backend/icons/lucideRegistry.generated.tsx +3 -37
- package/src/backend/icons/lucideRegistry.ts +2 -2
- package/src/backend/icons/lucideRegistryRuntime.tsx +36 -0
- package/src/backend/injection/__tests__/spotIds.extension-points.test.ts +38 -0
- package/src/backend/injection/spotIds.ts +23 -18
- package/src/backend/inputs/ComboboxInput.tsx +1 -1
- package/src/backend/messages/EmailThreadsPanel.tsx +1 -1
- package/src/backend/perspectiveState.ts +44 -0
- package/src/backend/schedule/ScheduleAgenda.tsx +3 -3
- package/src/backend/schedule/ScheduleGrid.tsx +3 -3
- package/src/backend/schedule/ScheduleToolbar.tsx +89 -68
- package/src/backend/sidebar/SidebarCustomizationEditor.tsx +42 -3
- package/src/backend/sidebar/__tests__/group-visibility.test.ts +131 -0
- package/src/backend/sidebar/customization-helpers.ts +47 -0
- package/src/backend/utils/__tests__/apiCall.test.ts +61 -0
- package/src/backend/utils/__tests__/customFieldForms.optionsPaging.test.ts +95 -0
- package/src/backend/utils/__tests__/fetchAllPages.test.ts +103 -0
- package/src/backend/utils/apiCall.ts +29 -1
- package/src/backend/utils/customFieldDefs.ts +1 -0
- package/src/backend/utils/customFieldForms.ts +15 -11
- package/src/backend/utils/fetchAllPages.ts +93 -0
- package/src/backend/utils/nav.ts +22 -2
- package/src/backend/version-history/VersionHistoryPanel.tsx +1 -1
- package/src/frontend/LanguageSwitcher.tsx +1 -0
- package/src/portal/PortalContext.tsx +1 -1
- package/src/portal/hooks/__tests__/usePortalEventBridge.test.tsx +66 -0
- package/src/portal/hooks/__tests__/usePortalNotifications.test.tsx +155 -0
- package/src/portal/hooks/portalBridgeStatus.ts +29 -0
- package/src/portal/hooks/usePortalEventBridge.ts +6 -0
- package/src/portal/hooks/usePortalNotifications.ts +55 -12
- package/src/portal/utils/__tests__/nav.test.ts +2 -2
- package/src/portal/utils/nav.ts +3 -4
- package/src/primitives/ErrorNotice.tsx +1 -1
- package/src/primitives/Notice.tsx +5 -5
- package/src/primitives/__tests__/button.test.tsx +70 -0
- package/src/primitives/button.tsx +10 -4
- package/src/theme/ThemeProvider.tsx +1 -2
- package/src/theme/__tests__/theme-init-script.test.ts +70 -0
- package/src/theme/index.ts +1 -0
- package/src/theme/theme-init-script.ts +23 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/filters/AdvancedFilterPanel.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { Popover, PopoverAnchor, PopoverContent } from '../../primitives/popover'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { Alert, AlertDescription } from '../../primitives/alert'\nimport { Button } from '../../primitives/button'\nimport { Input } from '../../primitives/input'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '../../primitives/dialog'\nimport { Filter, Save, Trash2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n deserializeTreeFromPersist,\n isPersistedFilterTree,\n serializeTreeForPersist,\n type AdvancedFilterTree,\n type PersistedFilterTree,\n} from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport type { FilterFieldDef } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { getDefaultOperator } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { ValidationError } from './filterValidation'\nimport { AdvancedFilterBuilder } from './AdvancedFilterBuilder'\nimport { FilterEmptyState } from './FilterEmptyState'\nimport { QuickFilters, type FilterPreset } from './QuickFilters'\nimport { FilterFieldPicker } from './FilterFieldPicker'\nimport { treeReducer } from './treeReducer'\nimport { flash } from '../FlashMessages'\n\nexport type AdvancedFilterPanelProps = {\n fields: FilterFieldDef[]\n value: AdvancedFilterTree\n onChange: (t: AdvancedFilterTree) => void\n onApply: () => void\n onClear: () => void\n onFlush?: () => void\n pendingErrors: ValidationError[]\n userId: string\n presets: FilterPreset[]\n open: boolean\n onOpenChange: (open: boolean) => void\n triggerRef?: React.RefObject<HTMLElement | null>\n aiSlot?: React.ReactNode\n savedFilterStorageKey?: string\n}\n\ntype SavedAdvancedFilter = {\n id: string\n name: string\n tree: PersistedFilterTree\n createdAt: string\n updatedAt: string\n}\n\n/**\n * Versioned envelope for saved-filter records in localStorage. The `v` discriminator\n * lets future schema changes (rename fields, retention policy, sharing flags) migrate\n * existing records instead of silently dropping them. Bump `v` when adding a breaking\n * change and add a read-old migration branch in `readSavedFilters`.\n */\ntype SavedFiltersEnvelopeV1 = {\n v: 1\n filters: SavedAdvancedFilter[]\n}\n\nconst SAVED_FILTER_STORAGE_PREFIX = 'open-mercato:advanced-filters:'\nconst MAX_SAVED_FILTERS = 20\nconst CURRENT_SAVED_FILTERS_VERSION = 1 as const\n\nfunction makeSavedFilterStorageKey(key?: string): string | null {\n const trimmed = key?.trim()\n return trimmed ? `${SAVED_FILTER_STORAGE_PREFIX}${trimmed}` : null\n}\n\nfunction isSavedAdvancedFilter(value: unknown): value is SavedAdvancedFilter {\n if (!value || typeof value !== 'object') return false\n const record = value as Record<string, unknown>\n return (\n typeof record.id === 'string'\n && typeof record.name === 'string'\n && typeof record.createdAt === 'string'\n && typeof record.updatedAt === 'string'\n && isPersistedFilterTree(record.tree)\n )\n}\n\nfunction readSavedFilters(storageKey: string): SavedAdvancedFilter[] {\n if (typeof window === 'undefined') return []\n try {\n const raw = window.localStorage.getItem(storageKey)\n if (!raw) return []\n const parsed = JSON.parse(raw)\n // v1 envelope (current shape).\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const envelope = parsed as { v?: unknown; filters?: unknown }\n if (envelope.v === 1 && Array.isArray(envelope.filters)) {\n return envelope.filters.filter(isSavedAdvancedFilter)\n }\n }\n // Pre-envelope shape (bare array) \u2014 migrate forward on next write.\n if (Array.isArray(parsed)) {\n return parsed.filter(isSavedAdvancedFilter)\n }\n return []\n } catch {\n return []\n }\n}\n\nfunction writeSavedFilters(storageKey: string, filters: SavedAdvancedFilter[]) {\n if (typeof window === 'undefined') return\n const envelope: SavedFiltersEnvelopeV1 = { v: CURRENT_SAVED_FILTERS_VERSION, filters }\n window.localStorage.setItem(storageKey, JSON.stringify(envelope))\n}\n\nfunction SaveFilterDialog({\n open,\n onOpenChange,\n onSave,\n t,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n onSave: (name: string) => Promise<void>\n t: (key: string, fallback: string) => string\n}) {\n const [name, setName] = React.useState('')\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const trimmed = name.trim()\n\n React.useEffect(() => {\n if (!open) {\n setName('')\n setSaving(false)\n setError(null)\n }\n }, [open])\n\n const submit = React.useCallback(async () => {\n if (!trimmed || saving) return\n setSaving(true)\n setError(null)\n try {\n await onSave(trimmed)\n onOpenChange(false)\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : t('ui.advancedFilter.saveFilter.error', 'Failed to save filter'))\n } finally {\n setSaving(false)\n }\n }, [onSave, onOpenChange, saving, t, trimmed])\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n {/* `elevated` lifts this dialog above the popover (z-popover=45) \u2014 at default\n z-modal=40 the dialog is occluded by the popover that opens it. */}\n <DialogContent elevated className=\"sm:max-w-md\" data-advanced-filter-portal>\n <form\n className=\"contents\"\n onSubmit={(event) => {\n event.preventDefault()\n void submit()\n }}\n >\n <DialogHeader>\n <DialogTitle>{t('ui.advancedFilter.saveFilter.title', 'Save filter')}</DialogTitle>\n <DialogDescription>\n {t('ui.advancedFilter.saveFilter.description', 'Name this filter so you can reuse the current conditions later.')}\n </DialogDescription>\n </DialogHeader>\n <div className=\"space-y-2\">\n <Input\n value={name}\n onChange={(event) => setName(event.target.value)}\n placeholder={t('ui.advancedFilter.saveFilter.placeholder', 'Filter name')}\n aria-label={t('ui.advancedFilter.saveFilter.nameAriaLabel', 'Filter name')}\n autoFocus\n />\n {error ? <p className=\"text-sm text-status-error-text\">{error}</p> : null}\n </div>\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n {t('common.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={!trimmed || saving}>\n {saving ? t('ui.advancedFilter.saveFilter.saving', 'Saving...') : t('ui.advancedFilter.saveFilter.submit', 'Save filter')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction SavedFiltersSection({\n filters,\n canSaveCurrent,\n onSaveClick,\n onApply,\n onDelete,\n t,\n}: {\n filters: SavedAdvancedFilter[]\n canSaveCurrent: boolean\n onSaveClick: () => void\n onApply: (filter: SavedAdvancedFilter) => void\n onDelete: (filterId: string) => void\n t: (key: string, fallback: string) => string\n}) {\n if (!canSaveCurrent && filters.length === 0) return null\n return (\n <div className=\"flex flex-col gap-2\" data-testid=\"advanced-filter-saved-filters\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"text-overline font-medium uppercase tracking-widest text-muted-foreground\">\n {t('ui.advancedFilter.savedFilters.heading', 'Saved filters')}\n </div>\n {canSaveCurrent ? (\n <Button\n data-testid=\"advanced-filter-save-trigger\"\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-8 rounded-full gap-1.5\"\n onClick={onSaveClick}\n >\n <Save className=\"size-3.5 text-muted-foreground\" />\n {t('ui.advancedFilter.saveFilter.button', 'Save filter')}\n </Button>\n ) : null}\n </div>\n {filters.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {filters.map((filter) => (\n <div key={filter.id} className=\"inline-flex max-w-full items-center overflow-hidden rounded-full border border-border bg-background\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 min-w-0 rounded-r-none px-3 gap-1.5\"\n title={filter.name}\n onClick={() => onApply(filter)}\n >\n <Filter className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{filter.name}</span>\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 w-8 rounded-l-none px-0 text-muted-foreground hover:text-status-error-text\"\n aria-label={`${t('ui.advancedFilter.savedFilters.delete', 'Delete saved filter')}: ${filter.name}`}\n onClick={() => onDelete(filter.id)}\n >\n <Trash2 className=\"size-3.5\" />\n </Button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {t('ui.advancedFilter.savedFilters.empty', 'No saved filters yet')}\n </p>\n )}\n </div>\n )\n}\n\nexport function AdvancedFilterPanel(props: AdvancedFilterPanelProps) {\n const t = useT()\n const empty = props.value.root.children.length === 0\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const [saveDialogOpen, setSaveDialogOpen] = React.useState(false)\n const [savedFilters, setSavedFilters] = React.useState<SavedAdvancedFilter[]>([])\n const addConditionRef = React.useRef<HTMLButtonElement>(null)\n const savedFilterStorageKey = React.useMemo(\n () => makeSavedFilterStorageKey(props.savedFilterStorageKey),\n [props.savedFilterStorageKey],\n )\n\n // Flush on close \u2014 the hook's useEffect on close also flushes; this is belt-and-suspenders.\n const wasOpenRef = React.useRef(props.open)\n React.useEffect(() => {\n if (wasOpenRef.current && !props.open && props.onFlush) props.onFlush()\n wasOpenRef.current = props.open\n }, [props.open, props.onFlush])\n\n const handlePresetApply = (tree: AdvancedFilterTree) => {\n props.onChange(tree)\n props.onOpenChange(false)\n }\n\n const { onFlush, onOpenChange, open } = props\n\n React.useEffect(() => {\n if (!open || pickerOpen || saveDialogOpen) return\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return\n // Do NOT gate on `event.defaultPrevented`. Radix keeps a just-closed\n // dropdown's dismissable layer mounted while its exit animation plays, and\n // that layer still calls preventDefault() on Escape. Picking a value in the\n // operator/value Select therefore leaves a layer that swallows the next\n // Escape, so the panel never closes and the active-filter chips never render\n // (regression seen on loaded standalone CI runners where the exit-animation\n // unmount is delayed). Instead, only defer Escape to an inner dropdown that\n // is *actually* open \u2014 a Radix Select trigger flips aria-expanded to \"false\"\n // synchronously on selection, so a closing-but-animating dropdown is no\n // longer treated as open here.\n if (typeof document !== 'undefined'\n && document.querySelector('[role=\"combobox\"][aria-expanded=\"true\"]')) {\n return\n }\n onOpenChange(false)\n onFlush?.()\n }\n window.addEventListener('keydown', closeOnEscape)\n return () => window.removeEventListener('keydown', closeOnEscape)\n }, [onFlush, onOpenChange, open, pickerOpen, saveDialogOpen])\n\n React.useEffect(() => {\n if (!savedFilterStorageKey) {\n setSavedFilters([])\n return\n }\n if (props.open) {\n setSavedFilters(readSavedFilters(savedFilterStorageKey))\n }\n }, [props.open, savedFilterStorageKey])\n\n const persistSavedFilters = React.useCallback((next: SavedAdvancedFilter[]) => {\n if (!savedFilterStorageKey) return\n setSavedFilters(next)\n writeSavedFilters(savedFilterStorageKey, next)\n }, [savedFilterStorageKey])\n\n const handleSaveFilter = React.useCallback(async (name: string) => {\n if (!savedFilterStorageKey) return\n const now = new Date().toISOString()\n const normalizedName = name.trim()\n const existing = savedFilters.find((item) => item.name.trim().toLowerCase() === normalizedName.toLowerCase())\n const record: SavedAdvancedFilter = {\n id: existing?.id ?? crypto.randomUUID(),\n name: normalizedName,\n tree: serializeTreeForPersist(props.value),\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n }\n const next = [\n record,\n ...savedFilters.filter((item) => item.id !== record.id),\n ].slice(0, MAX_SAVED_FILTERS)\n persistSavedFilters(next)\n flash(t('ui.advancedFilter.saveFilter.success', 'Filter saved'), 'success')\n }, [persistSavedFilters, props.value, savedFilterStorageKey, savedFilters, t])\n\n const handleApplySavedFilter = React.useCallback((filter: SavedAdvancedFilter) => {\n const tree = deserializeTreeFromPersist(filter.tree)\n if (!tree) return\n props.onChange(tree)\n props.onOpenChange(false)\n }, [props.onChange, props.onOpenChange])\n\n const handleDeleteSavedFilter = React.useCallback((filterId: string) => {\n persistSavedFilters(savedFilters.filter((item) => item.id !== filterId))\n }, [persistSavedFilters, savedFilters])\n\n const shouldShowSavedFilters = Boolean(savedFilterStorageKey && (!empty || savedFilters.length > 0))\n const savedFiltersSection = shouldShowSavedFilters ? (\n <SavedFiltersSection\n filters={savedFilters}\n canSaveCurrent={!empty}\n onSaveClick={() => setSaveDialogOpen(true)}\n onApply={handleApplySavedFilter}\n onDelete={handleDeleteSavedFilter}\n t={t}\n />\n ) : null\n\n const emptyStateShortcuts = props.presets.length || savedFiltersSection ? (\n <div className=\"flex flex-col gap-4\">\n {props.presets.length ? <QuickFilters presets={props.presets} userId={props.userId} onApply={handlePresetApply} /> : null}\n {savedFiltersSection}\n </div>\n ) : null\n\n const builderErrors = React.useMemo(\n () => props.pendingErrors.map(e => ({ ruleId: e.ruleId, messageKey: e.messageKey, message: e.message })),\n [props.pendingErrors],\n )\n const ignoreAdvancedFilterPortalInteractions = React.useCallback((event: { target: EventTarget | null; preventDefault: () => void }) => {\n const target = event.target\n if (!(target instanceof Element)) return\n if (target.closest('[data-advanced-filter-portal]')) {\n event.preventDefault()\n }\n }, [])\n\n return (\n <Popover open={props.open} onOpenChange={props.onOpenChange}>\n {props.triggerRef ? (\n <PopoverAnchor virtualRef={props.triggerRef as React.RefObject<{ getBoundingClientRect(): DOMRect }>} />\n ) : null}\n <PopoverContent\n className=\"w-[min(calc(100vw-2rem),780px)] max-h-[min(calc(100vh-6rem),720px)] overflow-y-auto p-0 gap-0\"\n align=\"end\"\n sideOffset={8}\n data-testid=\"advanced-filter-panel\"\n onPointerDownOutside={ignoreAdvancedFilterPortalInteractions}\n onFocusOutside={ignoreAdvancedFilterPortalInteractions}\n onInteractOutside={ignoreAdvancedFilterPortalInteractions}\n >\n <VisuallyHidden>\n <h2>{t('ui.advancedFilter.dialog.title', 'Advanced filter')}</h2>\n </VisuallyHidden>\n <div className=\"flex flex-col\">\n {empty ? (\n <FilterEmptyState\n onAddCondition={() => setPickerOpen(true)}\n addConditionRef={addConditionRef}\n aiSlot={props.aiSlot}\n quickFilters={emptyStateShortcuts}\n />\n ) : (\n <>\n <AdvancedFilterBuilder\n fields={props.fields}\n value={props.value}\n onChange={props.onChange}\n onApply={props.onApply}\n onClear={props.onClear}\n pendingErrors={builderErrors}\n />\n {savedFiltersSection ? (\n <div className=\"border-t border-border p-3\">\n {savedFiltersSection}\n </div>\n ) : null}\n </>\n )}\n {props.pendingErrors.length > 0 ? (\n <div className=\"p-3 border-t border-border\" data-testid=\"filter-validation-banner\">\n <Alert variant=\"destructive\">\n <AlertDescription>\n {props.pendingErrors.length === 1\n ? t('ui.advancedFilter.banner.incomplete', '{count} filter is incomplete \u2014 filters won\\'t apply until all values are picked.', { count: 1 })\n : t('ui.advancedFilter.banner.incompletePlural', '{count} filters are incomplete \u2014 filters won\\'t apply until all values are picked.', { count: props.pendingErrors.length })}\n </AlertDescription>\n </Alert>\n </div>\n ) : null}\n </div>\n </PopoverContent>\n <FilterFieldPicker\n fields={props.fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(field) => {\n // Pass defaultOperator so the rule starts with a valid operator for the field's\n // type \u2014 otherwise emptyRule defaults to 'contains', which only matches text.\n const next = treeReducer(props.value, {\n type: 'addRule',\n groupId: props.value.root.id,\n defaultField: field.key,\n defaultOperator: getDefaultOperator(field.type),\n })\n props.onChange(next)\n }}\n triggerRef={addConditionRef}\n />\n <SaveFilterDialog\n open={saveDialogOpen}\n onOpenChange={setSaveDialogOpen}\n onSave={handleSaveFilter}\n t={t}\n />\n </Popover>\n )\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { Popover, PopoverAnchor, PopoverContent } from '../../primitives/popover'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { Alert, AlertDescription } from '../../primitives/alert'\nimport { Button } from '../../primitives/button'\nimport { Input } from '../../primitives/input'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '../../primitives/dialog'\nimport { Filter, Save, Trash2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n deserializeTreeFromPersist,\n isPersistedFilterTree,\n serializeTreeForPersist,\n type AdvancedFilterTree,\n type PersistedFilterTree,\n} from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport type { FilterFieldDef } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { getDefaultOperator } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { ValidationError } from './filterValidation'\nimport { AdvancedFilterBuilder } from './AdvancedFilterBuilder'\nimport { FilterEmptyState } from './FilterEmptyState'\nimport { QuickFilters, type FilterPreset } from './QuickFilters'\nimport { FilterFieldPicker } from './FilterFieldPicker'\nimport { treeReducer } from './treeReducer'\nimport { flash } from '../FlashMessages'\n\nexport type AdvancedFilterPanelProps = {\n fields: FilterFieldDef[]\n value: AdvancedFilterTree\n onChange: (t: AdvancedFilterTree) => void\n onApply: () => void\n onClear: () => void\n onFlush?: () => void\n pendingErrors: ValidationError[]\n userId: string\n presets: FilterPreset[]\n open: boolean\n onOpenChange: (open: boolean) => void\n triggerRef?: React.RefObject<HTMLElement | null>\n aiSlot?: React.ReactNode\n savedFilterStorageKey?: string\n}\n\ntype SavedAdvancedFilter = {\n id: string\n name: string\n tree: PersistedFilterTree\n createdAt: string\n updatedAt: string\n}\n\n/**\n * Versioned envelope for saved-filter records in localStorage. The `v` discriminator\n * lets future schema changes (rename fields, retention policy, sharing flags) migrate\n * existing records instead of silently dropping them. Bump `v` when adding a breaking\n * change and add a read-old migration branch in `readSavedFilters`.\n */\ntype SavedFiltersEnvelopeV1 = {\n v: 1\n filters: SavedAdvancedFilter[]\n}\n\nconst SAVED_FILTER_STORAGE_PREFIX = 'open-mercato:advanced-filters:'\nconst MAX_SAVED_FILTERS = 20\nconst CURRENT_SAVED_FILTERS_VERSION = 1 as const\n\nfunction makeSavedFilterStorageKey(key?: string): string | null {\n const trimmed = key?.trim()\n return trimmed ? `${SAVED_FILTER_STORAGE_PREFIX}${trimmed}` : null\n}\n\nfunction isSavedAdvancedFilter(value: unknown): value is SavedAdvancedFilter {\n if (!value || typeof value !== 'object') return false\n const record = value as Record<string, unknown>\n return (\n typeof record.id === 'string'\n && typeof record.name === 'string'\n && typeof record.createdAt === 'string'\n && typeof record.updatedAt === 'string'\n && isPersistedFilterTree(record.tree)\n )\n}\n\nfunction readSavedFilters(storageKey: string): SavedAdvancedFilter[] {\n if (typeof window === 'undefined') return []\n try {\n const raw = window.localStorage.getItem(storageKey)\n if (!raw) return []\n const parsed = JSON.parse(raw)\n // v1 envelope (current shape).\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const envelope = parsed as { v?: unknown; filters?: unknown }\n if (envelope.v === 1 && Array.isArray(envelope.filters)) {\n return envelope.filters.filter(isSavedAdvancedFilter)\n }\n }\n // Pre-envelope shape (bare array) \u2014 migrate forward on next write.\n if (Array.isArray(parsed)) {\n return parsed.filter(isSavedAdvancedFilter)\n }\n return []\n } catch {\n return []\n }\n}\n\nfunction writeSavedFilters(storageKey: string, filters: SavedAdvancedFilter[]) {\n if (typeof window === 'undefined') return\n const envelope: SavedFiltersEnvelopeV1 = { v: CURRENT_SAVED_FILTERS_VERSION, filters }\n window.localStorage.setItem(storageKey, JSON.stringify(envelope))\n}\n\nfunction SaveFilterDialog({\n open,\n onOpenChange,\n onSave,\n t,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n onSave: (name: string) => Promise<void>\n t: (key: string, fallback: string) => string\n}) {\n const [name, setName] = React.useState('')\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const trimmed = name.trim()\n\n React.useEffect(() => {\n if (!open) {\n setName('')\n setSaving(false)\n setError(null)\n }\n }, [open])\n\n const submit = React.useCallback(async () => {\n if (!trimmed || saving) return\n setSaving(true)\n setError(null)\n try {\n await onSave(trimmed)\n onOpenChange(false)\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : t('ui.advancedFilter.saveFilter.error', 'Failed to save filter'))\n } finally {\n setSaving(false)\n }\n }, [onSave, onOpenChange, saving, t, trimmed])\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n {/* `elevated` lifts this dialog above the popover (z-popover=45) \u2014 at default\n z-modal=40 the dialog is occluded by the popover that opens it. */}\n <DialogContent elevated className=\"sm:max-w-md\" data-advanced-filter-portal>\n <form\n className=\"contents\"\n onSubmit={(event) => {\n event.preventDefault()\n void submit()\n }}\n >\n <DialogHeader>\n <DialogTitle>{t('ui.advancedFilter.saveFilter.title', 'Save filter')}</DialogTitle>\n <DialogDescription>\n {t('ui.advancedFilter.saveFilter.description', 'Name this filter so you can reuse the current conditions later.')}\n </DialogDescription>\n </DialogHeader>\n <div className=\"space-y-2\">\n <Input\n value={name}\n onChange={(event) => setName(event.target.value)}\n placeholder={t('ui.advancedFilter.saveFilter.placeholder', 'Filter name')}\n aria-label={t('ui.advancedFilter.saveFilter.nameAriaLabel', 'Filter name')}\n autoFocus\n />\n {error ? <p className=\"text-sm text-status-error-text\">{error}</p> : null}\n </div>\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n {t('common.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={!trimmed || saving}>\n {saving ? t('ui.advancedFilter.saveFilter.saving', 'Saving...') : t('ui.advancedFilter.saveFilter.submit', 'Save filter')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction SavedFiltersSection({\n filters,\n canSaveCurrent,\n onSaveClick,\n onApply,\n onDelete,\n t,\n}: {\n filters: SavedAdvancedFilter[]\n canSaveCurrent: boolean\n onSaveClick: () => void\n onApply: (filter: SavedAdvancedFilter) => void\n onDelete: (filterId: string) => void\n t: (key: string, fallback: string) => string\n}) {\n if (!canSaveCurrent && filters.length === 0) return null\n return (\n <div className=\"flex flex-col gap-2\" data-testid=\"advanced-filter-saved-filters\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"text-overline font-medium uppercase tracking-widest text-muted-foreground\">\n {t('ui.advancedFilter.savedFilters.heading', 'Saved filters')}\n </div>\n {canSaveCurrent ? (\n <Button\n data-testid=\"advanced-filter-save-trigger\"\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-8 rounded-full gap-1.5\"\n onClick={onSaveClick}\n >\n <Save className=\"size-3.5 text-muted-foreground\" />\n {t('ui.advancedFilter.saveFilter.button', 'Save filter')}\n </Button>\n ) : null}\n </div>\n {filters.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {filters.map((filter) => (\n <div key={filter.id} className=\"inline-flex max-w-full items-center overflow-hidden rounded-full border border-border bg-background\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 min-w-0 rounded-r-none px-3 gap-1.5\"\n title={filter.name}\n onClick={() => onApply(filter)}\n >\n <Filter className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{filter.name}</span>\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 w-8 rounded-l-none px-0 text-muted-foreground hover:text-status-error-text\"\n aria-label={`${t('ui.advancedFilter.savedFilters.delete', 'Delete saved filter')}: ${filter.name}`}\n onClick={() => onDelete(filter.id)}\n >\n <Trash2 className=\"size-3.5\" />\n </Button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {t('ui.advancedFilter.savedFilters.empty', 'No saved filters yet')}\n </p>\n )}\n </div>\n )\n}\n\nexport function AdvancedFilterPanel(props: AdvancedFilterPanelProps) {\n const t = useT()\n const empty = props.value.root.children.length === 0\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const [saveDialogOpen, setSaveDialogOpen] = React.useState(false)\n const [savedFilters, setSavedFilters] = React.useState<SavedAdvancedFilter[]>([])\n const addConditionRef = React.useRef<HTMLButtonElement>(null)\n const savedFilterStorageKey = React.useMemo(\n () => makeSavedFilterStorageKey(props.savedFilterStorageKey),\n [props.savedFilterStorageKey],\n )\n\n // Flush on close \u2014 the hook's useEffect on close also flushes; this is belt-and-suspenders.\n const wasOpenRef = React.useRef(props.open)\n React.useEffect(() => {\n if (wasOpenRef.current && !props.open && props.onFlush) props.onFlush()\n wasOpenRef.current = props.open\n }, [props.open, props.onFlush])\n\n const handlePresetApply = (tree: AdvancedFilterTree) => {\n props.onChange(tree)\n props.onOpenChange(false)\n }\n\n const { onFlush, onOpenChange, open } = props\n\n React.useEffect(() => {\n if (!open || pickerOpen || saveDialogOpen) return\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return\n // Do NOT gate on `event.defaultPrevented`. Radix keeps a just-closed\n // dropdown's dismissable layer mounted while its exit animation plays, and\n // that layer still calls preventDefault() on Escape. Picking a value in the\n // operator/value Select therefore leaves a layer that swallows the next\n // Escape, so the panel never closes and the active-filter chips never render\n // (regression seen on loaded standalone CI runners where the exit-animation\n // unmount is delayed). Instead, only defer Escape to an inner dropdown that\n // is *actually* open \u2014 a Radix Select trigger flips aria-expanded to \"false\"\n // synchronously on selection, so a closing-but-animating dropdown is no\n // longer treated as open here.\n if (typeof document !== 'undefined'\n && document.querySelector('[role=\"combobox\"][aria-expanded=\"true\"]')) {\n return\n }\n onOpenChange(false)\n onFlush?.()\n }\n window.addEventListener('keydown', closeOnEscape)\n return () => window.removeEventListener('keydown', closeOnEscape)\n }, [onFlush, onOpenChange, open, pickerOpen, saveDialogOpen])\n\n React.useEffect(() => {\n if (!savedFilterStorageKey) {\n setSavedFilters([])\n return\n }\n if (props.open) {\n setSavedFilters(readSavedFilters(savedFilterStorageKey))\n }\n }, [props.open, savedFilterStorageKey])\n\n const persistSavedFilters = React.useCallback((next: SavedAdvancedFilter[]) => {\n if (!savedFilterStorageKey) return\n setSavedFilters(next)\n writeSavedFilters(savedFilterStorageKey, next)\n }, [savedFilterStorageKey])\n\n const handleSaveFilter = React.useCallback(async (name: string) => {\n if (!savedFilterStorageKey) return\n const now = new Date().toISOString()\n const normalizedName = name.trim()\n const existing = savedFilters.find((item) => item.name.trim().toLowerCase() === normalizedName.toLowerCase())\n const record: SavedAdvancedFilter = {\n id: existing?.id ?? crypto.randomUUID(),\n name: normalizedName,\n tree: serializeTreeForPersist(props.value),\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n }\n const next = [\n record,\n ...savedFilters.filter((item) => item.id !== record.id),\n ].slice(0, MAX_SAVED_FILTERS)\n persistSavedFilters(next)\n flash(t('ui.advancedFilter.saveFilter.success', 'Filter saved'), 'success')\n }, [persistSavedFilters, props.value, savedFilterStorageKey, savedFilters, t])\n\n const handleApplySavedFilter = React.useCallback((filter: SavedAdvancedFilter) => {\n const tree = deserializeTreeFromPersist(filter.tree)\n if (!tree) return\n props.onChange(tree)\n props.onOpenChange(false)\n }, [props.onChange, props.onOpenChange])\n\n const handleDeleteSavedFilter = React.useCallback((filterId: string) => {\n persistSavedFilters(savedFilters.filter((item) => item.id !== filterId))\n }, [persistSavedFilters, savedFilters])\n\n const shouldShowSavedFilters = Boolean(savedFilterStorageKey && (!empty || savedFilters.length > 0))\n const savedFiltersSection = shouldShowSavedFilters ? (\n <SavedFiltersSection\n filters={savedFilters}\n canSaveCurrent={!empty}\n onSaveClick={() => setSaveDialogOpen(true)}\n onApply={handleApplySavedFilter}\n onDelete={handleDeleteSavedFilter}\n t={t}\n />\n ) : null\n\n const emptyStateShortcuts = props.presets.length || savedFiltersSection ? (\n <div className=\"flex flex-col gap-4\">\n {props.presets.length ? <QuickFilters presets={props.presets} userId={props.userId} onApply={handlePresetApply} /> : null}\n {savedFiltersSection}\n </div>\n ) : null\n\n const builderErrors = React.useMemo(\n () => props.pendingErrors.map(e => ({ ruleId: e.ruleId, messageKey: e.messageKey, message: e.message })),\n [props.pendingErrors],\n )\n const ignoreAdvancedFilterPortalInteractions = React.useCallback((event: { target: EventTarget | null; preventDefault: () => void }) => {\n const target = event.target\n if (!(target instanceof Element)) return\n if (target.closest('[data-advanced-filter-portal]')) {\n event.preventDefault()\n }\n }, [])\n\n return (\n <Popover open={props.open} onOpenChange={props.onOpenChange}>\n {props.triggerRef ? (\n <PopoverAnchor virtualRef={props.triggerRef as React.RefObject<{ getBoundingClientRect(): DOMRect }>} />\n ) : null}\n <PopoverContent\n className=\"w-[min(calc(100vw-2rem),780px)] max-h-[min(calc(100vh-6rem),720px)] overflow-y-auto p-0 gap-0\"\n align=\"end\"\n sideOffset={8}\n data-testid=\"advanced-filter-panel\"\n onPointerDownOutside={ignoreAdvancedFilterPortalInteractions}\n onFocusOutside={ignoreAdvancedFilterPortalInteractions}\n onInteractOutside={ignoreAdvancedFilterPortalInteractions}\n >\n <VisuallyHidden>\n <h2>{t('ui.advancedFilter.dialog.title', 'Advanced filter')}</h2>\n </VisuallyHidden>\n <div className=\"flex flex-col\">\n {empty ? (\n <FilterEmptyState\n onAddCondition={() => setPickerOpen(true)}\n addConditionRef={addConditionRef}\n aiSlot={props.aiSlot}\n quickFilters={emptyStateShortcuts}\n />\n ) : (\n <>\n <AdvancedFilterBuilder\n fields={props.fields}\n value={props.value}\n onChange={props.onChange}\n onApply={props.onApply}\n onClear={props.onClear}\n pendingErrors={builderErrors}\n />\n {savedFiltersSection ? (\n <div className=\"border-t border-border p-3\">\n {savedFiltersSection}\n </div>\n ) : null}\n </>\n )}\n {props.pendingErrors.length > 0 ? (\n <div className=\"p-3 border-t border-border\" data-testid=\"filter-validation-banner\">\n <Alert status=\"error\">\n <AlertDescription>\n {props.pendingErrors.length === 1\n ? t('ui.advancedFilter.banner.incomplete', '{count} filter is incomplete \u2014 filters won\\'t apply until all values are picked.', { count: 1 })\n : t('ui.advancedFilter.banner.incompletePlural', '{count} filters are incomplete \u2014 filters won\\'t apply until all values are picked.', { count: props.pendingErrors.length })}\n </AlertDescription>\n </Alert>\n </div>\n ) : null}\n </div>\n </PopoverContent>\n <FilterFieldPicker\n fields={props.fields}\n open={pickerOpen}\n onOpenChange={setPickerOpen}\n onSelect={(field) => {\n // Pass defaultOperator so the rule starts with a valid operator for the field's\n // type \u2014 otherwise emptyRule defaults to 'contains', which only matches text.\n const next = treeReducer(props.value, {\n type: 'addRule',\n groupId: props.value.root.id,\n defaultField: field.key,\n defaultOperator: getDefaultOperator(field.type),\n })\n props.onChange(next)\n }}\n triggerRef={addConditionRef}\n />\n <SaveFilterDialog\n open={saveDialogOpen}\n onOpenChange={setSaveDialogOpen}\n onSave={handleSaveFilter}\n t={t}\n />\n </Popover>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AA0KU,SAkQE,UAjQA,KADF;AAzKV,YAAY,WAAW;AACvB,SAAS,SAAS,eAAe,sBAAsB;AACvD,SAAS,sBAAsB;AAC/B,SAAS,OAAO,wBAAwB;AACxC,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,QAAQ,MAAM,cAAc;AACrC,SAAS,YAAY;AACrB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP,SAAS,0BAA0B;AAEnC,SAAS,6BAA6B;AACtC,SAAS,wBAAwB;AACjC,SAAS,oBAAuC;AAChD,SAAS,yBAAyB;AAClC,SAAS,mBAAmB;AAC5B,SAAS,aAAa;AAsCtB,MAAM,8BAA8B;AACpC,MAAM,oBAAoB;AAC1B,MAAM,gCAAgC;AAEtC,SAAS,0BAA0B,KAA6B;AAC9D,QAAM,UAAU,KAAK,KAAK;AAC1B,SAAO,UAAU,GAAG,2BAA2B,GAAG,OAAO,KAAK;AAChE;AAEA,SAAS,sBAAsB,OAA8C;AAC3E,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,SAAS;AACf,SACE,OAAO,OAAO,OAAO,YAClB,OAAO,OAAO,SAAS,YACvB,OAAO,OAAO,cAAc,YAC5B,OAAO,OAAO,cAAc,YAC5B,sBAAsB,OAAO,IAAI;AAExC;AAEA,SAAS,iBAAiB,YAA2C;AACnE,MAAI,OAAO,WAAW,YAAa,QAAO,CAAC;AAC3C,MAAI;AACF,UAAM,MAAM,OAAO,aAAa,QAAQ,UAAU;AAClD,QAAI,CAAC,IAAK,QAAO,CAAC;AAClB,UAAM,SAAS,KAAK,MAAM,GAAG;AAE7B,QAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAClE,YAAM,WAAW;AACjB,UAAI,SAAS,MAAM,KAAK,MAAM,QAAQ,SAAS,OAAO,GAAG;AACvD,eAAO,SAAS,QAAQ,OAAO,qBAAqB;AAAA,MACtD;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,aAAO,OAAO,OAAO,qBAAqB;AAAA,IAC5C;AACA,WAAO,CAAC;AAAA,EACV,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,kBAAkB,YAAoB,SAAgC;AAC7E,MAAI,OAAO,WAAW,YAAa;AACnC,QAAM,WAAmC,EAAE,GAAG,+BAA+B,QAAQ;AACrF,SAAO,aAAa,QAAQ,YAAY,KAAK,UAAU,QAAQ,CAAC;AAClE;AAEA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,EAAE;AACzC,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,UAAU,KAAK,KAAK;AAE1B,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,MAAM;AACT,cAAQ,EAAE;AACV,gBAAU,KAAK;AACf,eAAS,IAAI;AAAA,IACf;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,SAAS,MAAM,YAAY,YAAY;AAC3C,QAAI,CAAC,WAAW,OAAQ;AACxB,cAAU,IAAI;AACd,aAAS,IAAI;AACb,QAAI;AACF,YAAM,OAAO,OAAO;AACpB,mBAAa,KAAK;AAAA,IACpB,SAAS,KAAc;AACrB,eAAS,eAAe,QAAQ,IAAI,UAAU,EAAE,sCAAsC,uBAAuB,CAAC;AAAA,IAChH,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,QAAQ,cAAc,QAAQ,GAAG,OAAO,CAAC;AAE7C,SACE,oBAAC,UAAO,MAAY,cAGlB,8BAAC,iBAAc,UAAQ,MAAC,WAAU,eAAc,+BAA2B,MACzE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,UAAU,CAAC,UAAU;AACnB,cAAM,eAAe;AACrB,aAAK,OAAO;AAAA,MACd;AAAA,MAEA;AAAA,6BAAC,gBACC;AAAA,8BAAC,eAAa,YAAE,sCAAsC,aAAa,GAAE;AAAA,UACrE,oBAAC,qBACE,YAAE,4CAA4C,iEAAiE,GAClH;AAAA,WACF;AAAA,QACA,qBAAC,SAAI,WAAU,aACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,KAAK;AAAA,cAC/C,aAAa,EAAE,4CAA4C,aAAa;AAAA,cACxE,cAAY,EAAE,8CAA8C,aAAa;AAAA,cACzE,WAAS;AAAA;AAAA,UACX;AAAA,UACC,QAAQ,oBAAC,OAAE,WAAU,kCAAkC,iBAAM,IAAO;AAAA,WACvE;AAAA,QACA,qBAAC,gBACC;AAAA,8BAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,SAAS,MAAM,aAAa,KAAK,GACtE,YAAE,iBAAiB,QAAQ,GAC9B;AAAA,UACA,oBAAC,UAAO,MAAK,UAAS,UAAU,CAAC,WAAW,QACzC,mBAAS,EAAE,uCAAuC,WAAW,IAAI,EAAE,uCAAuC,aAAa,GAC1H;AAAA,WACF;AAAA;AAAA;AAAA,EACF,GACF,GACF;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,MAAI,CAAC,kBAAkB,QAAQ,WAAW,EAAG,QAAO;AACpD,SACE,qBAAC,SAAI,WAAU,uBAAsB,eAAY,iCAC/C;AAAA,yBAAC,SAAI,WAAU,2CACb;AAAA,0BAAC,SAAI,WAAU,6EACZ,YAAE,0CAA0C,eAAe,GAC9D;AAAA,MACC,iBACC;AAAA,QAAC;AAAA;AAAA,UACC,eAAY;AAAA,UACZ,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS;AAAA,UAET;AAAA,gCAAC,QAAK,WAAU,kCAAiC;AAAA,YAChD,EAAE,uCAAuC,aAAa;AAAA;AAAA;AAAA,MACzD,IACE;AAAA,OACN;AAAA,IACC,QAAQ,SACP,oBAAC,SAAI,WAAU,wBACZ,kBAAQ,IAAI,CAAC,WACZ,qBAAC,SAAoB,WAAU,uGAC7B;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,WAAU;AAAA,UACV,OAAO,OAAO;AAAA,UACd,SAAS,MAAM,QAAQ,MAAM;AAAA,UAE7B;AAAA,gCAAC,UAAO,WAAU,2CAA0C;AAAA,YAC5D,oBAAC,UAAK,WAAU,YAAY,iBAAO,MAAK;AAAA;AAAA;AAAA,MAC1C;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,WAAU;AAAA,UACV,cAAY,GAAG,EAAE,yCAAyC,qBAAqB,CAAC,KAAK,OAAO,IAAI;AAAA,UAChG,SAAS,MAAM,SAAS,OAAO,EAAE;AAAA,UAEjC,8BAAC,UAAO,WAAU,YAAW;AAAA;AAAA,MAC/B;AAAA,SArBQ,OAAO,EAsBjB,CACD,GACH,IAEA,oBAAC,OAAE,WAAU,iCACV,YAAE,wCAAwC,sBAAsB,GACnE;AAAA,KAEJ;AAEJ;AAEO,SAAS,oBAAoB,OAAiC;AACnE,QAAM,IAAI,KAAK;AACf,QAAM,QAAQ,MAAM,MAAM,KAAK,SAAS,WAAW;AACnD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,KAAK;AAChE,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAgC,CAAC,CAAC;AAChF,QAAM,kBAAkB,MAAM,OAA0B,IAAI;AAC5D,QAAM,wBAAwB,MAAM;AAAA,IAClC,MAAM,0BAA0B,MAAM,qBAAqB;AAAA,IAC3D,CAAC,MAAM,qBAAqB;AAAA,EAC9B;AAGA,QAAM,aAAa,MAAM,OAAO,MAAM,IAAI;AAC1C,QAAM,UAAU,MAAM;AACpB,QAAI,WAAW,WAAW,CAAC,MAAM,QAAQ,MAAM,QAAS,OAAM,QAAQ;AACtE,eAAW,UAAU,MAAM;AAAA,EAC7B,GAAG,CAAC,MAAM,MAAM,MAAM,OAAO,CAAC;AAE9B,QAAM,oBAAoB,CAAC,SAA6B;AACtD,UAAM,SAAS,IAAI;AACnB,UAAM,aAAa,KAAK;AAAA,EAC1B;AAEA,QAAM,EAAE,SAAS,cAAc,KAAK,IAAI;AAExC,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAQ,cAAc,eAAgB;AAC3C,UAAM,gBAAgB,CAAC,UAAyB;AAC9C,UAAI,MAAM,QAAQ,SAAU;AAW5B,UAAI,OAAO,aAAa,eACnB,SAAS,cAAc,yCAAyC,GAAG;AACtE;AAAA,MACF;AACA,mBAAa,KAAK;AAClB,gBAAU;AAAA,IACZ;AACA,WAAO,iBAAiB,WAAW,aAAa;AAChD,WAAO,MAAM,OAAO,oBAAoB,WAAW,aAAa;AAAA,EAClE,GAAG,CAAC,SAAS,cAAc,MAAM,YAAY,cAAc,CAAC;AAE5D,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,uBAAuB;AAC1B,sBAAgB,CAAC,CAAC;AAClB;AAAA,IACF;AACA,QAAI,MAAM,MAAM;AACd,sBAAgB,iBAAiB,qBAAqB,CAAC;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,MAAM,MAAM,qBAAqB,CAAC;AAEtC,QAAM,sBAAsB,MAAM,YAAY,CAAC,SAAgC;AAC7E,QAAI,CAAC,sBAAuB;AAC5B,oBAAgB,IAAI;AACpB,sBAAkB,uBAAuB,IAAI;AAAA,EAC/C,GAAG,CAAC,qBAAqB,CAAC;AAE1B,QAAM,mBAAmB,MAAM,YAAY,OAAO,SAAiB;AACjE,QAAI,CAAC,sBAAuB;AAC5B,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,iBAAiB,KAAK,KAAK;AACjC,UAAM,WAAW,aAAa,KAAK,CAAC,SAAS,KAAK,KAAK,KAAK,EAAE,YAAY,MAAM,eAAe,YAAY,CAAC;AAC5G,UAAM,SAA8B;AAAA,MAClC,IAAI,UAAU,MAAM,OAAO,WAAW;AAAA,MACtC,MAAM;AAAA,MACN,MAAM,wBAAwB,MAAM,KAAK;AAAA,MACzC,WAAW,UAAU,aAAa;AAAA,MAClC,WAAW;AAAA,IACb;AACA,UAAM,OAAO;AAAA,MACX;AAAA,MACA,GAAG,aAAa,OAAO,CAAC,SAAS,KAAK,OAAO,OAAO,EAAE;AAAA,IACxD,EAAE,MAAM,GAAG,iBAAiB;AAC5B,wBAAoB,IAAI;AACxB,UAAM,EAAE,wCAAwC,cAAc,GAAG,SAAS;AAAA,EAC5E,GAAG,CAAC,qBAAqB,MAAM,OAAO,uBAAuB,cAAc,CAAC,CAAC;AAE7E,QAAM,yBAAyB,MAAM,YAAY,CAAC,WAAgC;AAChF,UAAM,OAAO,2BAA2B,OAAO,IAAI;AACnD,QAAI,CAAC,KAAM;AACX,UAAM,SAAS,IAAI;AACnB,UAAM,aAAa,KAAK;AAAA,EAC1B,GAAG,CAAC,MAAM,UAAU,MAAM,YAAY,CAAC;AAEvC,QAAM,0BAA0B,MAAM,YAAY,CAAC,aAAqB;AACtE,wBAAoB,aAAa,OAAO,CAAC,SAAS,KAAK,OAAO,QAAQ,CAAC;AAAA,EACzE,GAAG,CAAC,qBAAqB,YAAY,CAAC;AAEtC,QAAM,yBAAyB,QAAQ,0BAA0B,CAAC,SAAS,aAAa,SAAS,EAAE;AACnG,QAAM,sBAAsB,yBAC1B;AAAA,IAAC;AAAA;AAAA,MACC,SAAS;AAAA,MACT,gBAAgB,CAAC;AAAA,MACjB,aAAa,MAAM,kBAAkB,IAAI;AAAA,MACzC,SAAS;AAAA,MACT,UAAU;AAAA,MACV;AAAA;AAAA,EACF,IACE;AAEJ,QAAM,sBAAsB,MAAM,QAAQ,UAAU,sBAClD,qBAAC,SAAI,WAAU,uBACZ;AAAA,UAAM,QAAQ,SAAS,oBAAC,gBAAa,SAAS,MAAM,SAAS,QAAQ,MAAM,QAAQ,SAAS,mBAAmB,IAAK;AAAA,IACpH;AAAA,KACH,IACE;AAEJ,QAAM,gBAAgB,MAAM;AAAA,IAC1B,MAAM,MAAM,cAAc,IAAI,QAAM,EAAE,QAAQ,EAAE,QAAQ,YAAY,EAAE,YAAY,SAAS,EAAE,QAAQ,EAAE;AAAA,IACvG,CAAC,MAAM,aAAa;AAAA,EACtB;AACA,QAAM,yCAAyC,MAAM,YAAY,CAAC,UAAsE;AACtI,UAAM,SAAS,MAAM;AACrB,QAAI,EAAE,kBAAkB,SAAU;AAClC,QAAI,OAAO,QAAQ,+BAA+B,GAAG;AACnD,YAAM,eAAe;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SACE,qBAAC,WAAQ,MAAM,MAAM,MAAM,cAAc,MAAM,cAC5C;AAAA,UAAM,aACL,oBAAC,iBAAc,YAAY,MAAM,YAAqE,IACpG;AAAA,IACJ;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAM;AAAA,QACN,YAAY;AAAA,QACZ,eAAY;AAAA,QACZ,sBAAsB;AAAA,QACtB,gBAAgB;AAAA,QAChB,mBAAmB;AAAA,QAEnB;AAAA,8BAAC,kBACC,8BAAC,QAAI,YAAE,kCAAkC,iBAAiB,GAAE,GAC9D;AAAA,UACA,qBAAC,SAAI,WAAU,iBACZ;AAAA,oBACC;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAgB,MAAM,cAAc,IAAI;AAAA,gBACxC;AAAA,gBACA,QAAQ,MAAM;AAAA,gBACd,cAAc;AAAA;AAAA,YAChB,IAEA,iCACE;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,QAAQ,MAAM;AAAA,kBACd,OAAO,MAAM;AAAA,kBACb,UAAU,MAAM;AAAA,kBAChB,SAAS,MAAM;AAAA,kBACf,SAAS,MAAM;AAAA,kBACf,eAAe;AAAA;AAAA,cACjB;AAAA,cACC,sBACC,oBAAC,SAAI,WAAU,8BACZ,+BACH,IACE;AAAA,eACN;AAAA,YAED,MAAM,cAAc,SAAS,IAC5B,oBAAC,SAAI,WAAU,8BAA6B,eAAY,4BACtD,8BAAC,SAAM,QAAO,SACZ,8BAAC,oBACE,gBAAM,cAAc,WAAW,IAC5B,EAAE,uCAAuC,wFAAoF,EAAE,OAAO,EAAE,CAAC,IACzI,EAAE,6CAA6C,0FAAsF,EAAE,OAAO,MAAM,cAAc,OAAO,CAAC,GAChL,GACF,GACF,IACE;AAAA,aACN;AAAA;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,MAAM;AAAA,QACd,MAAM;AAAA,QACN,cAAc;AAAA,QACd,UAAU,CAAC,UAAU;AAGnB,gBAAM,OAAO,YAAY,MAAM,OAAO;AAAA,YACpC,MAAM;AAAA,YACN,SAAS,MAAM,MAAM,KAAK;AAAA,YAC1B,cAAc,MAAM;AAAA,YACpB,iBAAiB,mBAAmB,MAAM,IAAI;AAAA,UAChD,CAAC;AACD,gBAAM,SAAS,IAAI;AAAA,QACrB;AAAA,QACA,YAAY;AAAA;AAAA,IACd;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd,QAAQ;AAAA,QACR;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -105,11 +105,10 @@ function DetailHeader({
|
|
|
105
105
|
Button,
|
|
106
106
|
{
|
|
107
107
|
type: "button",
|
|
108
|
-
variant: "
|
|
108
|
+
variant: "destructive",
|
|
109
109
|
size: "sm",
|
|
110
110
|
onClick: onDelete,
|
|
111
111
|
disabled: isDeleting,
|
|
112
|
-
className: "text-red-600 border-red-200 hover:bg-red-50 rounded",
|
|
113
112
|
children: [
|
|
114
113
|
isDeleting ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 mr-2 animate-spin" }) : /* @__PURE__ */ jsx(Trash2, { className: "size-4 mr-2" }),
|
|
115
114
|
resolvedDeleteLabel
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/forms/FormHeader.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { usePathname, useSearchParams } from 'next/navigation'\nimport { Trash2, Loader2 } from 'lucide-react'\nimport { Button } from '../../primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { FormActionButtons, type FormActionButtonsProps } from './FormActionButtons'\nimport { ActionsDropdown, type ActionItem } from './ActionsDropdown'\nimport { InjectionSpot } from '../injection/InjectionSpot'\n\n/** Base props shared by both modes */\ntype FormHeaderBaseProps = {\n /** Back link URL */\n backHref?: string\n /** Back link label */\n backLabel?: string\n}\n\n/** Edit mode: compact header for CrudForm pages */\nexport type FormHeaderEditProps = FormHeaderBaseProps & {\n mode?: 'edit'\n /** Small title next to the back link */\n title?: string\n /** Structured action buttons (Delete/Cancel/Save) */\n actions?: FormActionButtonsProps\n /** Custom right-side content (overrides `actions`) */\n actionsContent?: React.ReactNode\n}\n\n/** Detail mode: large header for view/detail pages */\nexport type FormHeaderDetailProps = FormHeaderBaseProps & {\n mode: 'detail'\n /** Large title -- string renders as h1; ReactNode for InlineTextEditor */\n title?: React.ReactNode\n /** Small uppercase entity type label above the title */\n entityTypeLabel?: string\n /** Subtitle text below the title */\n subtitle?: string\n /** Status badge or similar element below the title */\n statusBadge?: React.ReactNode\n /** Context actions grouped into an \"Actions\" dropdown (preferred) */\n menuActions?: ActionItem[]\n /** Optional label for actions dropdown trigger */\n menuLabel?: string\n /** Trigger style for actions dropdown */\n menuTriggerMode?: 'label' | 'icon'\n /** Accessible label used when trigger is icon-only */\n menuAriaLabel?: string\n /** Optional utility actions (icon-only) displayed before menu actions */\n utilityActions?: React.ReactNode\n /** Delete action -- rendered as a standalone destructive button next to the dropdown */\n onDelete?: () => void\n /** Delete button label */\n deleteLabel?: string\n /** Whether delete is in progress */\n isDeleting?: boolean\n /** Fallback: fully custom right-side content (overrides menuActions + onDelete) */\n actionsContent?: React.ReactNode\n}\n\nexport type FormHeaderProps = FormHeaderEditProps | FormHeaderDetailProps\n\nexport function FormHeader(props: FormHeaderProps) {\n const t = useT()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const resolvedBackLabel = props.backLabel ?? t('ui.navigation.back')\n const injectionContext = React.useMemo(\n () => ({\n path: pathname ?? '',\n query: searchParams?.toString() ?? '',\n }),\n [pathname, searchParams],\n )\n\n if (props.mode === 'detail') {\n return (\n <>\n <DetailHeader {...props} resolvedBackLabel={resolvedBackLabel} />\n <InjectionSpot spotId=\"form-header:detail\" context={injectionContext} />\n </>\n )\n }\n\n return (\n <>\n <EditHeader {...props} resolvedBackLabel={resolvedBackLabel} />\n <InjectionSpot spotId=\"form-header:edit\" context={injectionContext} />\n </>\n )\n}\n\nfunction EditHeader({\n backHref,\n resolvedBackLabel,\n title,\n actions,\n actionsContent,\n}: FormHeaderEditProps & { resolvedBackLabel: string }) {\n return (\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3\">\n <div className=\"flex items-center gap-3\">\n {backHref ? (\n <Link href={backHref} className=\"text-sm text-muted-foreground hover:text-foreground\">\n ← {resolvedBackLabel}\n </Link>\n ) : null}\n {title ? <div className=\"text-base font-medium\">{title}</div> : null}\n </div>\n {actionsContent ?? (actions ? <FormActionButtons {...actions} /> : null)}\n </div>\n )\n}\n\nfunction DetailHeader({\n backHref,\n resolvedBackLabel,\n title,\n entityTypeLabel,\n subtitle,\n statusBadge,\n menuActions,\n menuLabel,\n menuTriggerMode,\n menuAriaLabel,\n utilityActions,\n onDelete,\n deleteLabel,\n isDeleting,\n actionsContent,\n}: FormHeaderDetailProps & { resolvedBackLabel: string }) {\n const t = useT()\n const resolvedDeleteLabel = deleteLabel ?? t('ui.forms.actions.delete')\n\n const hasActions = actionsContent || utilityActions || menuActions?.length || onDelete\n\n return (\n <div className=\"flex flex-col gap-2 md:gap-3 md:flex-row md:items-center md:justify-between\">\n <div className=\"flex flex-wrap items-center gap-2 md:gap-3 min-w-0\">\n {backHref ? (\n <Link\n href={backHref}\n className=\"inline-flex items-center text-sm text-muted-foreground hover:text-foreground shrink-0\"\n >\n <span aria-hidden className=\"mr-1 text-base\">←</span>\n <span className=\"sr-only\">{resolvedBackLabel}</span>\n </Link>\n ) : null}\n <div className=\"space-y-0.5 md:space-y-1 min-w-0\">\n {entityTypeLabel ? (\n <p className=\"text-xs uppercase text-muted-foreground\">{entityTypeLabel}</p>\n ) : null}\n {title ? (\n typeof title === 'string' ? (\n <h1 className=\"text-lg md:text-2xl font-semibold leading-tight truncate\">{title}</h1>\n ) : (\n <div className=\"text-lg md:text-2xl font-semibold leading-tight\">{title}</div>\n )\n ) : null}\n {statusBadge}\n {subtitle ? (\n <p className=\"text-xs md:text-sm text-muted-foreground\">{subtitle}</p>\n ) : null}\n </div>\n </div>\n {hasActions ? (\n <div className=\"flex flex-wrap items-center gap-2\">\n {actionsContent ? actionsContent : (\n <>\n {utilityActions}\n {menuActions?.length ? (\n <ActionsDropdown\n items={menuActions}\n label={menuLabel}\n triggerMode={menuTriggerMode}\n ariaLabel={menuAriaLabel}\n />\n ) : null}\n {onDelete ? (\n <Button\n type=\"button\"\n variant=\"
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { usePathname, useSearchParams } from 'next/navigation'\nimport { Trash2, Loader2 } from 'lucide-react'\nimport { Button } from '../../primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { FormActionButtons, type FormActionButtonsProps } from './FormActionButtons'\nimport { ActionsDropdown, type ActionItem } from './ActionsDropdown'\nimport { InjectionSpot } from '../injection/InjectionSpot'\n\n/** Base props shared by both modes */\ntype FormHeaderBaseProps = {\n /** Back link URL */\n backHref?: string\n /** Back link label */\n backLabel?: string\n}\n\n/** Edit mode: compact header for CrudForm pages */\nexport type FormHeaderEditProps = FormHeaderBaseProps & {\n mode?: 'edit'\n /** Small title next to the back link */\n title?: string\n /** Structured action buttons (Delete/Cancel/Save) */\n actions?: FormActionButtonsProps\n /** Custom right-side content (overrides `actions`) */\n actionsContent?: React.ReactNode\n}\n\n/** Detail mode: large header for view/detail pages */\nexport type FormHeaderDetailProps = FormHeaderBaseProps & {\n mode: 'detail'\n /** Large title -- string renders as h1; ReactNode for InlineTextEditor */\n title?: React.ReactNode\n /** Small uppercase entity type label above the title */\n entityTypeLabel?: string\n /** Subtitle text below the title */\n subtitle?: string\n /** Status badge or similar element below the title */\n statusBadge?: React.ReactNode\n /** Context actions grouped into an \"Actions\" dropdown (preferred) */\n menuActions?: ActionItem[]\n /** Optional label for actions dropdown trigger */\n menuLabel?: string\n /** Trigger style for actions dropdown */\n menuTriggerMode?: 'label' | 'icon'\n /** Accessible label used when trigger is icon-only */\n menuAriaLabel?: string\n /** Optional utility actions (icon-only) displayed before menu actions */\n utilityActions?: React.ReactNode\n /** Delete action -- rendered as a standalone destructive button next to the dropdown */\n onDelete?: () => void\n /** Delete button label */\n deleteLabel?: string\n /** Whether delete is in progress */\n isDeleting?: boolean\n /** Fallback: fully custom right-side content (overrides menuActions + onDelete) */\n actionsContent?: React.ReactNode\n}\n\nexport type FormHeaderProps = FormHeaderEditProps | FormHeaderDetailProps\n\nexport function FormHeader(props: FormHeaderProps) {\n const t = useT()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const resolvedBackLabel = props.backLabel ?? t('ui.navigation.back')\n const injectionContext = React.useMemo(\n () => ({\n path: pathname ?? '',\n query: searchParams?.toString() ?? '',\n }),\n [pathname, searchParams],\n )\n\n if (props.mode === 'detail') {\n return (\n <>\n <DetailHeader {...props} resolvedBackLabel={resolvedBackLabel} />\n <InjectionSpot spotId=\"form-header:detail\" context={injectionContext} />\n </>\n )\n }\n\n return (\n <>\n <EditHeader {...props} resolvedBackLabel={resolvedBackLabel} />\n <InjectionSpot spotId=\"form-header:edit\" context={injectionContext} />\n </>\n )\n}\n\nfunction EditHeader({\n backHref,\n resolvedBackLabel,\n title,\n actions,\n actionsContent,\n}: FormHeaderEditProps & { resolvedBackLabel: string }) {\n return (\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3\">\n <div className=\"flex items-center gap-3\">\n {backHref ? (\n <Link href={backHref} className=\"text-sm text-muted-foreground hover:text-foreground\">\n ← {resolvedBackLabel}\n </Link>\n ) : null}\n {title ? <div className=\"text-base font-medium\">{title}</div> : null}\n </div>\n {actionsContent ?? (actions ? <FormActionButtons {...actions} /> : null)}\n </div>\n )\n}\n\nfunction DetailHeader({\n backHref,\n resolvedBackLabel,\n title,\n entityTypeLabel,\n subtitle,\n statusBadge,\n menuActions,\n menuLabel,\n menuTriggerMode,\n menuAriaLabel,\n utilityActions,\n onDelete,\n deleteLabel,\n isDeleting,\n actionsContent,\n}: FormHeaderDetailProps & { resolvedBackLabel: string }) {\n const t = useT()\n const resolvedDeleteLabel = deleteLabel ?? t('ui.forms.actions.delete')\n\n const hasActions = actionsContent || utilityActions || menuActions?.length || onDelete\n\n return (\n <div className=\"flex flex-col gap-2 md:gap-3 md:flex-row md:items-center md:justify-between\">\n <div className=\"flex flex-wrap items-center gap-2 md:gap-3 min-w-0\">\n {backHref ? (\n <Link\n href={backHref}\n className=\"inline-flex items-center text-sm text-muted-foreground hover:text-foreground shrink-0\"\n >\n <span aria-hidden className=\"mr-1 text-base\">←</span>\n <span className=\"sr-only\">{resolvedBackLabel}</span>\n </Link>\n ) : null}\n <div className=\"space-y-0.5 md:space-y-1 min-w-0\">\n {entityTypeLabel ? (\n <p className=\"text-xs uppercase text-muted-foreground\">{entityTypeLabel}</p>\n ) : null}\n {title ? (\n typeof title === 'string' ? (\n <h1 className=\"text-lg md:text-2xl font-semibold leading-tight truncate\">{title}</h1>\n ) : (\n <div className=\"text-lg md:text-2xl font-semibold leading-tight\">{title}</div>\n )\n ) : null}\n {statusBadge}\n {subtitle ? (\n <p className=\"text-xs md:text-sm text-muted-foreground\">{subtitle}</p>\n ) : null}\n </div>\n </div>\n {hasActions ? (\n <div className=\"flex flex-wrap items-center gap-2\">\n {actionsContent ? actionsContent : (\n <>\n {utilityActions}\n {menuActions?.length ? (\n <ActionsDropdown\n items={menuActions}\n label={menuLabel}\n triggerMode={menuTriggerMode}\n ariaLabel={menuAriaLabel}\n />\n ) : null}\n {onDelete ? (\n <Button\n type=\"button\"\n variant=\"destructive\"\n size=\"sm\"\n onClick={onDelete}\n disabled={isDeleting}\n >\n {isDeleting ? (\n <Loader2 className=\"size-4 mr-2 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4 mr-2\" />\n )}\n {resolvedDeleteLabel}\n </Button>\n ) : null}\n </>\n )}\n </div>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AA+EM,mBACE,KADF;AA7EN,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,aAAa,uBAAuB;AAC7C,SAAS,QAAQ,eAAe;AAChC,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,yBAAsD;AAC/D,SAAS,uBAAwC;AACjD,SAAS,qBAAqB;AAsDvB,SAAS,WAAW,OAAwB;AACjD,QAAM,IAAI,KAAK;AACf,QAAM,WAAW,YAAY;AAC7B,QAAM,eAAe,gBAAgB;AACrC,QAAM,oBAAoB,MAAM,aAAa,EAAE,oBAAoB;AACnE,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OAAO;AAAA,MACL,MAAM,YAAY;AAAA,MAClB,OAAO,cAAc,SAAS,KAAK;AAAA,IACrC;AAAA,IACA,CAAC,UAAU,YAAY;AAAA,EACzB;AAEA,MAAI,MAAM,SAAS,UAAU;AAC3B,WACE,iCACE;AAAA,0BAAC,gBAAc,GAAG,OAAO,mBAAsC;AAAA,MAC/D,oBAAC,iBAAc,QAAO,sBAAqB,SAAS,kBAAkB;AAAA,OACxE;AAAA,EAEJ;AAEA,SACE,iCACE;AAAA,wBAAC,cAAY,GAAG,OAAO,mBAAsC;AAAA,IAC7D,oBAAC,iBAAc,QAAO,oBAAmB,SAAS,kBAAkB;AAAA,KACtE;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwD;AACtD,SACE,qBAAC,SAAI,WAAU,+EACb;AAAA,yBAAC,SAAI,WAAU,2BACZ;AAAA,iBACC,qBAAC,QAAK,MAAM,UAAU,WAAU,uDAAsD;AAAA;AAAA,QAC5E;AAAA,SACV,IACE;AAAA,MACH,QAAQ,oBAAC,SAAI,WAAU,yBAAyB,iBAAM,IAAS;AAAA,OAClE;AAAA,IACC,mBAAmB,UAAU,oBAAC,qBAAmB,GAAG,SAAS,IAAK;AAAA,KACrE;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0D;AACxD,QAAM,IAAI,KAAK;AACf,QAAM,sBAAsB,eAAe,EAAE,yBAAyB;AAEtE,QAAM,aAAa,kBAAkB,kBAAkB,aAAa,UAAU;AAE9E,SACE,qBAAC,SAAI,WAAU,+EACb;AAAA,yBAAC,SAAI,WAAU,sDACZ;AAAA,iBACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,WAAU;AAAA,UAEV;AAAA,gCAAC,UAAK,eAAW,MAAC,WAAU,kBAAiB,oBAAM;AAAA,YACnD,oBAAC,UAAK,WAAU,WAAW,6BAAkB;AAAA;AAAA;AAAA,MAC/C,IACE;AAAA,MACJ,qBAAC,SAAI,WAAU,oCACZ;AAAA,0BACC,oBAAC,OAAE,WAAU,2CAA2C,2BAAgB,IACtE;AAAA,QACH,QACC,OAAO,UAAU,WACf,oBAAC,QAAG,WAAU,4DAA4D,iBAAM,IAEhF,oBAAC,SAAI,WAAU,mDAAmD,iBAAM,IAExE;AAAA,QACH;AAAA,QACA,WACC,oBAAC,OAAE,WAAU,4CAA4C,oBAAS,IAChE;AAAA,SACN;AAAA,OACF;AAAA,IACC,aACC,oBAAC,SAAI,WAAU,qCACZ,2BAAiB,iBAChB,iCACG;AAAA;AAAA,MACA,aAAa,SACZ;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,OAAO;AAAA,UACP,aAAa;AAAA,UACb,WAAW;AAAA;AAAA,MACb,IACE;AAAA,MACH,WACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,SAAS;AAAA,UACT,UAAU;AAAA,UAET;AAAA,yBACC,oBAAC,WAAQ,WAAU,4BAA2B,IAE9C,oBAAC,UAAO,WAAU,eAAc;AAAA,YAEjC;AAAA;AAAA;AAAA,MACH,IACE;AAAA,OACN,GAEJ,IACE;AAAA,KACN;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
1
|
import {
|
|
3
2
|
Activity,
|
|
4
3
|
AlertCircle,
|
|
@@ -293,30 +292,7 @@ const LUCIDE_ICON_REGISTRY = {
|
|
|
293
292
|
"x": X,
|
|
294
293
|
"x-circle": XCircle
|
|
295
294
|
};
|
|
296
|
-
function normalizeKebabIconName(input) {
|
|
297
|
-
const trimmed = input.trim();
|
|
298
|
-
if (!trimmed) return "";
|
|
299
|
-
const withoutPrefix = trimmed.startsWith("lucide:") ? trimmed.slice("lucide:".length) : trimmed;
|
|
300
|
-
if (!withoutPrefix) return "";
|
|
301
|
-
if (!withoutPrefix.includes("-") && !withoutPrefix.includes("_") && !withoutPrefix.includes(" ") && /[A-Z]/.test(withoutPrefix)) {
|
|
302
|
-
return withoutPrefix.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
303
|
-
}
|
|
304
|
-
return withoutPrefix.replace(/[_\s]+/g, "-").replace(/-+/g, "-").toLowerCase();
|
|
305
|
-
}
|
|
306
|
-
function resolveRegisteredLucideIcon(name) {
|
|
307
|
-
if (!name) return null;
|
|
308
|
-
const normalized = normalizeKebabIconName(name);
|
|
309
|
-
if (!normalized) return null;
|
|
310
|
-
return LUCIDE_ICON_REGISTRY[normalized] ?? null;
|
|
311
|
-
}
|
|
312
|
-
function resolveRegisteredLucideIconNode(name, className) {
|
|
313
|
-
const Icon = resolveRegisteredLucideIcon(name);
|
|
314
|
-
if (!Icon) return null;
|
|
315
|
-
return /* @__PURE__ */ jsx(Icon, { className });
|
|
316
|
-
}
|
|
317
295
|
export {
|
|
318
|
-
LUCIDE_ICON_REGISTRY
|
|
319
|
-
resolveRegisteredLucideIcon,
|
|
320
|
-
resolveRegisteredLucideIconNode
|
|
296
|
+
LUCIDE_ICON_REGISTRY
|
|
321
297
|
};
|
|
322
298
|
//# sourceMappingURL=lucideRegistry.generated.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/icons/lucideRegistry.generated.tsx"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable */\n// AUTO-GENERATED by @open-mercato/ui build. Do not edit by hand.\n// Generated from discovered icon string usages across the repo.\n\nimport type
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/* eslint-disable */\n// AUTO-GENERATED by @open-mercato/ui build. Do not edit by hand.\n// Generated from discovered icon string usages across the repo.\n\nimport type { LucideIcon } from 'lucide-react';\nimport {\n Activity,\n AlertCircle,\n AlertOctagon,\n AlertTriangle,\n Archive,\n Award,\n BadgeCheck,\n Ban,\n Banknote,\n BarChart2,\n BarChart3,\n Bell,\n Bolt,\n Book,\n Bookmark,\n Box,\n Boxes,\n Briefcase,\n BriefcaseBusiness,\n Building,\n Building2,\n Calendar,\n CalendarCheck,\n CalendarClock,\n CalendarCog,\n CalendarMinus,\n CalendarOff,\n CalendarX,\n Check,\n CheckCircle,\n CheckCircle2,\n CheckSquare,\n Circle,\n ClipboardCheck,\n ClipboardList,\n Clock,\n Clock3,\n Coins,\n Copy,\n CreditCard,\n Crown,\n Database,\n DollarSign,\n Download,\n ExternalLink,\n Eye,\n FileMinus,\n FilePenLine,\n FileSpreadsheet,\n FileText,\n FilterX,\n Flag,\n Flame,\n FolderTree,\n Gauge,\n GitBranch,\n GitCompareArrows,\n GitPullRequestArrow,\n Globe,\n GraduationCap,\n Hand,\n Handshake,\n Headphones,\n Heart,\n HelpCircle,\n Hourglass,\n Inbox,\n Key,\n KeyRound,\n Layers,\n Lightbulb,\n LineChart,\n Link,\n List,\n Loader,\n Loader2,\n Lock,\n Mail,\n MailOpen,\n MapPin,\n MapPinned,\n MessageCircle,\n MinusCircle,\n Notebook,\n Package,\n PackageCheck,\n PackagePlus,\n PackageSearch,\n PackageX,\n PauseCircle,\n Percent,\n Phone,\n PhoneCall,\n PieChart,\n PlusSquare,\n Receipt,\n ReceiptText,\n RefreshCcw,\n RefreshCw,\n Reply,\n RotateCcw,\n Ruler,\n Send,\n Settings,\n Shapes,\n Share2,\n Shield,\n ShieldAlert,\n ShieldCheck,\n ShoppingBag,\n ShoppingCart,\n Shuffle,\n Sliders,\n Smartphone,\n Snowflake,\n Sparkles,\n Star,\n StickyNote,\n Store,\n Tag,\n Target,\n ThumbsUp,\n Ticket,\n Timer,\n Trash2,\n TrendingUp,\n TriangleAlert,\n Trophy,\n Truck,\n Undo2,\n Unlock,\n User,\n UserCheck,\n UserMinus,\n UserPlus,\n UserRound,\n Users,\n UserX,\n Wallet,\n Warehouse,\n Webhook,\n Workflow,\n Wrench,\n X,\n XCircle,\n} from 'lucide-react';\n\nexport const LUCIDE_ICON_REGISTRY: Record<string, LucideIcon> = {\n 'activity': Activity,\n 'alert-circle': AlertCircle,\n 'alert-octagon': AlertOctagon,\n 'alert-triangle': AlertTriangle,\n 'archive': Archive,\n 'award': Award,\n 'badge-check': BadgeCheck,\n 'ban': Ban,\n 'banknote': Banknote,\n 'bar-chart-2': BarChart2,\n 'bar-chart-3': BarChart3,\n 'bell': Bell,\n 'bolt': Bolt,\n 'book': Book,\n 'bookmark': Bookmark,\n 'box': Box,\n 'boxes': Boxes,\n 'briefcase': Briefcase,\n 'briefcase-business': BriefcaseBusiness,\n 'building': Building,\n 'building2': Building2,\n 'calendar': Calendar,\n 'calendar-check': CalendarCheck,\n 'calendar-clock': CalendarClock,\n 'calendar-cog': CalendarCog,\n 'calendar-minus': CalendarMinus,\n 'calendar-off': CalendarOff,\n 'calendar-x': CalendarX,\n 'check': Check,\n 'check-circle': CheckCircle,\n 'check-circle-2': CheckCircle2,\n 'check-square': CheckSquare,\n 'circle': Circle,\n 'clipboard-check': ClipboardCheck,\n 'clipboard-list': ClipboardList,\n 'clock': Clock,\n 'clock-3': Clock3,\n 'coins': Coins,\n 'copy': Copy,\n 'credit-card': CreditCard,\n 'crown': Crown,\n 'database': Database,\n 'dollar-sign': DollarSign,\n 'download': Download,\n 'external-link': ExternalLink,\n 'eye': Eye,\n 'file-minus': FileMinus,\n 'file-pen-line': FilePenLine,\n 'file-spreadsheet': FileSpreadsheet,\n 'file-text': FileText,\n 'filter-x': FilterX,\n 'flag': Flag,\n 'flame': Flame,\n 'folder-tree': FolderTree,\n 'gauge': Gauge,\n 'git-branch': GitBranch,\n 'git-compare-arrows': GitCompareArrows,\n 'git-pull-request-arrow': GitPullRequestArrow,\n 'globe': Globe,\n 'graduation-cap': GraduationCap,\n 'hand': Hand,\n 'handshake': Handshake,\n 'headphones': Headphones,\n 'heart': Heart,\n 'help-circle': HelpCircle,\n 'hourglass': Hourglass,\n 'inbox': Inbox,\n 'key': Key,\n 'key-round': KeyRound,\n 'layers': Layers,\n 'lightbulb': Lightbulb,\n 'line-chart': LineChart,\n 'link': Link,\n 'list': List,\n 'loader': Loader,\n 'loader-2': Loader2,\n 'lock': Lock,\n 'mail': Mail,\n 'mail-open': MailOpen,\n 'map-pin': MapPin,\n 'map-pinned': MapPinned,\n 'message-circle': MessageCircle,\n 'minus-circle': MinusCircle,\n 'notebook': Notebook,\n 'package': Package,\n 'package-check': PackageCheck,\n 'package-plus': PackagePlus,\n 'package-search': PackageSearch,\n 'package-x': PackageX,\n 'pause-circle': PauseCircle,\n 'percent': Percent,\n 'phone': Phone,\n 'phone-call': PhoneCall,\n 'pie-chart': PieChart,\n 'plus-square': PlusSquare,\n 'receipt': Receipt,\n 'receipt-text': ReceiptText,\n 'refresh-ccw': RefreshCcw,\n 'refresh-cw': RefreshCw,\n 'reply': Reply,\n 'rotate-ccw': RotateCcw,\n 'ruler': Ruler,\n 'send': Send,\n 'settings': Settings,\n 'shapes': Shapes,\n 'share-2': Share2,\n 'shield': Shield,\n 'shield-alert': ShieldAlert,\n 'shield-check': ShieldCheck,\n 'shopping-bag': ShoppingBag,\n 'shopping-cart': ShoppingCart,\n 'shuffle': Shuffle,\n 'sliders': Sliders,\n 'smartphone': Smartphone,\n 'snowflake': Snowflake,\n 'sparkles': Sparkles,\n 'star': Star,\n 'sticky-note': StickyNote,\n 'store': Store,\n 'tag': Tag,\n 'target': Target,\n 'thumbs-up': ThumbsUp,\n 'ticket': Ticket,\n 'timer': Timer,\n 'trash-2': Trash2,\n 'trending-up': TrendingUp,\n 'triangle-alert': TriangleAlert,\n 'trophy': Trophy,\n 'truck': Truck,\n 'undo-2': Undo2,\n 'unlock': Unlock,\n 'user': User,\n 'user-check': UserCheck,\n 'user-minus': UserMinus,\n 'user-plus': UserPlus,\n 'user-round': UserRound,\n 'user-x': UserX,\n 'users': Users,\n 'wallet': Wallet,\n 'warehouse': Warehouse,\n 'webhook': Webhook,\n 'workflow': Workflow,\n 'wrench': Wrench,\n 'x': X,\n 'x-circle': XCircle,\n};\n"],
|
|
5
|
+
"mappings": "AAKA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveRegisteredLucideIcon,
|
|
3
|
-
resolveRegisteredLucideIconNode
|
|
4
|
-
|
|
5
|
-
} from "./lucideRegistry.generated.js";
|
|
3
|
+
resolveRegisteredLucideIconNode
|
|
4
|
+
} from "./lucideRegistryRuntime.js";
|
|
5
|
+
import { LUCIDE_ICON_REGISTRY } from "./lucideRegistry.generated.js";
|
|
6
6
|
import { LUCIDE_ICON_REGISTRY as LUCIDE_ICON_REGISTRY2 } from "./lucideRegistry.generated.js";
|
|
7
7
|
function registerAdditionalIcons(icons) {
|
|
8
8
|
for (const [name, component] of Object.entries(icons)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/icons/lucideRegistry.ts"],
|
|
4
|
-
"sourcesContent": ["export {\n resolveRegisteredLucideIcon,\n resolveRegisteredLucideIconNode,\n
|
|
5
|
-
"mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,
|
|
4
|
+
"sourcesContent": ["export {\n resolveRegisteredLucideIcon,\n resolveRegisteredLucideIconNode,\n} from './lucideRegistryRuntime'\nexport { LUCIDE_ICON_REGISTRY } from './lucideRegistry.generated'\n\nimport type { LucideIcon } from 'lucide-react'\nimport { LUCIDE_ICON_REGISTRY } from './lucideRegistry.generated'\n\nexport function registerAdditionalIcons(icons: Record<string, LucideIcon>): void {\n for (const [name, component] of Object.entries(icons)) {\n LUCIDE_ICON_REGISTRY[name] = component\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,4BAA4B;AAGrC,SAAS,wBAAAA,6BAA4B;AAE9B,SAAS,wBAAwB,OAAyC;AAC/E,aAAW,CAAC,MAAM,SAAS,KAAK,OAAO,QAAQ,KAAK,GAAG;AACrD,IAAAA,sBAAqB,IAAI,IAAI;AAAA,EAC/B;AACF;",
|
|
6
6
|
"names": ["LUCIDE_ICON_REGISTRY"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { LUCIDE_ICON_REGISTRY } from "./lucideRegistry.generated.js";
|
|
3
|
+
function normalizeKebabIconName(input) {
|
|
4
|
+
const trimmed = input.trim();
|
|
5
|
+
if (!trimmed) return "";
|
|
6
|
+
const withoutPrefix = trimmed.startsWith("lucide:") ? trimmed.slice("lucide:".length) : trimmed;
|
|
7
|
+
if (!withoutPrefix) return "";
|
|
8
|
+
if (!withoutPrefix.includes("-") && !withoutPrefix.includes("_") && !withoutPrefix.includes(" ") && /[A-Z]/.test(withoutPrefix)) {
|
|
9
|
+
return withoutPrefix.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
10
|
+
}
|
|
11
|
+
return withoutPrefix.replace(/[_\s]+/g, "-").replace(/-+/g, "-").toLowerCase();
|
|
12
|
+
}
|
|
13
|
+
function resolveRegisteredLucideIcon(name) {
|
|
14
|
+
if (!name) return null;
|
|
15
|
+
const normalized = normalizeKebabIconName(name);
|
|
16
|
+
if (!normalized) return null;
|
|
17
|
+
return LUCIDE_ICON_REGISTRY[normalized] ?? null;
|
|
18
|
+
}
|
|
19
|
+
function resolveRegisteredLucideIconNode(name, className) {
|
|
20
|
+
const Icon = resolveRegisteredLucideIcon(name);
|
|
21
|
+
if (!Icon) return null;
|
|
22
|
+
return /* @__PURE__ */ jsx(Icon, { className });
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
resolveRegisteredLucideIcon,
|
|
26
|
+
resolveRegisteredLucideIconNode
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=lucideRegistryRuntime.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/backend/icons/lucideRegistryRuntime.tsx"],
|
|
4
|
+
"sourcesContent": ["import type * as React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { LUCIDE_ICON_REGISTRY } from './lucideRegistry.generated'\n\nfunction normalizeKebabIconName(input: string): string {\n const trimmed = input.trim()\n if (!trimmed) return ''\n const withoutPrefix = trimmed.startsWith('lucide:') ? trimmed.slice('lucide:'.length) : trimmed\n if (!withoutPrefix) return ''\n if (!withoutPrefix.includes('-') && !withoutPrefix.includes('_') && !withoutPrefix.includes(' ') && /[A-Z]/.test(withoutPrefix)) {\n return withoutPrefix\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')\n .toLowerCase()\n }\n return withoutPrefix\n .replace(/[_\\s]+/g, '-')\n .replace(/-+/g, '-')\n .toLowerCase()\n}\n\nexport function resolveRegisteredLucideIcon(name: string | undefined): LucideIcon | null {\n if (!name) return null\n const normalized = normalizeKebabIconName(name)\n if (!normalized) return null\n return (LUCIDE_ICON_REGISTRY as Record<string, LucideIcon | undefined>)[normalized] ?? null\n}\n\nexport function resolveRegisteredLucideIconNode(\n name: string | undefined,\n className: string\n): React.ReactNode | null {\n const Icon = resolveRegisteredLucideIcon(name)\n if (!Icon) return null\n return <Icon className={className} />\n}\n"],
|
|
5
|
+
"mappings": "AAkCS;AAhCT,SAAS,4BAA4B;AAErC,SAAS,uBAAuB,OAAuB;AACrD,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,gBAAgB,QAAQ,WAAW,SAAS,IAAI,QAAQ,MAAM,UAAU,MAAM,IAAI;AACxF,MAAI,CAAC,cAAe,QAAO;AAC3B,MAAI,CAAC,cAAc,SAAS,GAAG,KAAK,CAAC,cAAc,SAAS,GAAG,KAAK,CAAC,cAAc,SAAS,GAAG,KAAK,QAAQ,KAAK,aAAa,GAAG;AAC/H,WAAO,cACJ,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,wBAAwB,OAAO,EACvC,YAAY;AAAA,EACjB;AACA,SAAO,cACJ,QAAQ,WAAW,GAAG,EACtB,QAAQ,OAAO,GAAG,EAClB,YAAY;AACjB;AAEO,SAAS,4BAA4B,MAA6C;AACvF,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,aAAa,uBAAuB,IAAI;AAC9C,MAAI,CAAC,WAAY,QAAO;AACxB,SAAQ,qBAAgE,UAAU,KAAK;AACzF;AAEO,SAAS,gCACd,MACA,WACwB;AACxB,QAAM,OAAO,4BAA4B,IAAI;AAC7C,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,oBAAC,QAAK,WAAsB;AACrC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
crudFormExtensionSpotId,
|
|
3
|
+
dataTableExtensionSpotId
|
|
4
|
+
} from "@open-mercato/shared/modules/widgets/extension-points";
|
|
1
5
|
const BACKEND_RECORD_CURRENT_INJECTION_SPOT_ID = "backend:record:current";
|
|
2
6
|
const BACKEND_LAYOUT_TOP_INJECTION_SPOT_ID = "backend:layout:top";
|
|
3
7
|
const BACKEND_LAYOUT_FOOTER_INJECTION_SPOT_ID = "backend:layout:footer";
|
|
@@ -10,31 +14,32 @@ const BACKEND_SIDEBAR_NAV_FOOTER_INJECTION_SPOT_ID = "backend:sidebar:nav:footer
|
|
|
10
14
|
const GLOBAL_SIDEBAR_STATUS_BADGES_INJECTION_SPOT_ID = "global:sidebar:status-badges";
|
|
11
15
|
const GLOBAL_HEADER_STATUS_INDICATORS_INJECTION_SPOT_ID = "global:header:status-indicators";
|
|
12
16
|
const CrudFormInjectionSpots = {
|
|
13
|
-
base: (entityId) =>
|
|
14
|
-
beforeFields: (entityId) =>
|
|
15
|
-
afterFields: (entityId) =>
|
|
16
|
-
header: (entityId) =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
base: (entityId) => crudFormExtensionSpotId(entityId),
|
|
18
|
+
beforeFields: (entityId) => crudFormExtensionSpotId(entityId, "before-fields"),
|
|
19
|
+
afterFields: (entityId) => crudFormExtensionSpotId(entityId, "after-fields"),
|
|
20
|
+
header: (entityId) => crudFormExtensionSpotId(entityId, "header"),
|
|
21
|
+
fields: (entityId) => crudFormExtensionSpotId(entityId, "fields"),
|
|
22
|
+
footer: (entityId) => crudFormExtensionSpotId(entityId, "footer"),
|
|
23
|
+
sidebar: (entityId) => crudFormExtensionSpotId(entityId, "sidebar"),
|
|
24
|
+
group: (entityId, groupId) => crudFormExtensionSpotId(entityId, `group:${groupId}`),
|
|
25
|
+
fieldBefore: (entityId, fieldId) => crudFormExtensionSpotId(entityId, `field:${fieldId}:before`),
|
|
26
|
+
fieldAfter: (entityId, fieldId) => crudFormExtensionSpotId(entityId, `field:${fieldId}:after`)
|
|
22
27
|
};
|
|
23
28
|
const DataTableInjectionSpots = {
|
|
24
|
-
header: (tableId) =>
|
|
25
|
-
footer: (tableId) =>
|
|
26
|
-
toolbar: (tableId) =>
|
|
29
|
+
header: (tableId) => dataTableExtensionSpotId(tableId, "header"),
|
|
30
|
+
footer: (tableId) => dataTableExtensionSpotId(tableId, "footer"),
|
|
31
|
+
toolbar: (tableId) => dataTableExtensionSpotId(tableId, "toolbar"),
|
|
27
32
|
// Slot rendered immediately after the search input on the same row as the
|
|
28
33
|
// FilterBar — intended for compact, icon-sized triggers (AI assistants,
|
|
29
34
|
// saved view shortcuts, etc.). Hosts pass the resolved spot ID through to
|
|
30
35
|
// FilterBar's `searchTrailing` prop. Stays empty when the table has no
|
|
31
36
|
// search input.
|
|
32
|
-
searchTrailing: (tableId) =>
|
|
33
|
-
emptyState: (tableId) =>
|
|
34
|
-
columns: (tableId) =>
|
|
35
|
-
rowActions: (tableId) =>
|
|
36
|
-
bulkActions: (tableId) =>
|
|
37
|
-
filters: (tableId) =>
|
|
37
|
+
searchTrailing: (tableId) => dataTableExtensionSpotId(tableId, "search-trailing"),
|
|
38
|
+
emptyState: (tableId) => dataTableExtensionSpotId(tableId, "empty-state"),
|
|
39
|
+
columns: (tableId) => dataTableExtensionSpotId(tableId, "columns"),
|
|
40
|
+
rowActions: (tableId) => dataTableExtensionSpotId(tableId, "row-actions"),
|
|
41
|
+
bulkActions: (tableId) => dataTableExtensionSpotId(tableId, "bulk-actions"),
|
|
42
|
+
filters: (tableId) => dataTableExtensionSpotId(tableId, "filters")
|
|
38
43
|
};
|
|
39
44
|
const PORTAL_SIDEBAR_MAIN_INJECTION_SPOT_ID = "menu:portal:sidebar:main";
|
|
40
45
|
const PORTAL_SIDEBAR_ACCOUNT_INJECTION_SPOT_ID = "menu:portal:sidebar:account";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/injection/spotIds.ts"],
|
|
4
|
-
"sourcesContent": ["import type { InjectionSpotId } from '@open-mercato/shared/modules/widgets/injection'\n\nexport const BACKEND_RECORD_CURRENT_INJECTION_SPOT_ID: InjectionSpotId = 'backend:record:current'\nexport const BACKEND_LAYOUT_TOP_INJECTION_SPOT_ID: InjectionSpotId = 'backend:layout:top'\nexport const BACKEND_LAYOUT_FOOTER_INJECTION_SPOT_ID: InjectionSpotId = 'backend:layout:footer'\nexport const BACKEND_SIDEBAR_TOP_INJECTION_SPOT_ID: InjectionSpotId = 'backend:sidebar:top'\nexport const BACKEND_SIDEBAR_FOOTER_INJECTION_SPOT_ID: InjectionSpotId = 'backend:sidebar:footer'\n\n// Standardized backend chrome spot ids\nexport const BACKEND_TOPBAR_PROFILE_MENU_INJECTION_SPOT_ID: InjectionSpotId = 'backend:topbar:profile-menu'\nexport const BACKEND_TOPBAR_ACTIONS_INJECTION_SPOT_ID: InjectionSpotId = 'backend:topbar:actions'\nexport const BACKEND_SIDEBAR_NAV_INJECTION_SPOT_ID: InjectionSpotId = 'backend:sidebar:nav'\nexport const BACKEND_SIDEBAR_NAV_FOOTER_INJECTION_SPOT_ID: InjectionSpotId = 'backend:sidebar:nav:footer'\n\n// Standardized global status spot ids\nexport const GLOBAL_SIDEBAR_STATUS_BADGES_INJECTION_SPOT_ID: InjectionSpotId = 'global:sidebar:status-badges'\nexport const GLOBAL_HEADER_STATUS_INDICATORS_INJECTION_SPOT_ID: InjectionSpotId = 'global:header:status-indicators'\n\n// Standardized pattern helpers\nexport const CrudFormInjectionSpots = {\n base: (entityId: string): InjectionSpotId =>
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { InjectionSpotId } from '@open-mercato/shared/modules/widgets/injection'\nimport {\n crudFormExtensionSpotId,\n dataTableExtensionSpotId,\n} from '@open-mercato/shared/modules/widgets/extension-points'\n\nexport const BACKEND_RECORD_CURRENT_INJECTION_SPOT_ID: InjectionSpotId = 'backend:record:current'\nexport const BACKEND_LAYOUT_TOP_INJECTION_SPOT_ID: InjectionSpotId = 'backend:layout:top'\nexport const BACKEND_LAYOUT_FOOTER_INJECTION_SPOT_ID: InjectionSpotId = 'backend:layout:footer'\nexport const BACKEND_SIDEBAR_TOP_INJECTION_SPOT_ID: InjectionSpotId = 'backend:sidebar:top'\nexport const BACKEND_SIDEBAR_FOOTER_INJECTION_SPOT_ID: InjectionSpotId = 'backend:sidebar:footer'\n\n// Standardized backend chrome spot ids\nexport const BACKEND_TOPBAR_PROFILE_MENU_INJECTION_SPOT_ID: InjectionSpotId = 'backend:topbar:profile-menu'\nexport const BACKEND_TOPBAR_ACTIONS_INJECTION_SPOT_ID: InjectionSpotId = 'backend:topbar:actions'\nexport const BACKEND_SIDEBAR_NAV_INJECTION_SPOT_ID: InjectionSpotId = 'backend:sidebar:nav'\nexport const BACKEND_SIDEBAR_NAV_FOOTER_INJECTION_SPOT_ID: InjectionSpotId = 'backend:sidebar:nav:footer'\n\n// Standardized global status spot ids\nexport const GLOBAL_SIDEBAR_STATUS_BADGES_INJECTION_SPOT_ID: InjectionSpotId = 'global:sidebar:status-badges'\nexport const GLOBAL_HEADER_STATUS_INDICATORS_INJECTION_SPOT_ID: InjectionSpotId = 'global:header:status-indicators'\n\n// Standardized pattern helpers\nexport const CrudFormInjectionSpots = {\n base: (entityId: string): InjectionSpotId => crudFormExtensionSpotId(entityId),\n beforeFields: (entityId: string): InjectionSpotId => crudFormExtensionSpotId(entityId, 'before-fields'),\n afterFields: (entityId: string): InjectionSpotId => crudFormExtensionSpotId(entityId, 'after-fields'),\n header: (entityId: string): InjectionSpotId => crudFormExtensionSpotId(entityId, 'header'),\n fields: (entityId: string): InjectionSpotId => crudFormExtensionSpotId(entityId, 'fields'),\n footer: (entityId: string): InjectionSpotId => crudFormExtensionSpotId(entityId, 'footer'),\n sidebar: (entityId: string): InjectionSpotId => crudFormExtensionSpotId(entityId, 'sidebar'),\n group: (entityId: string, groupId: string): InjectionSpotId => crudFormExtensionSpotId(entityId, `group:${groupId}`),\n fieldBefore: (entityId: string, fieldId: string): InjectionSpotId => crudFormExtensionSpotId(entityId, `field:${fieldId}:before`),\n fieldAfter: (entityId: string, fieldId: string): InjectionSpotId => crudFormExtensionSpotId(entityId, `field:${fieldId}:after`),\n} as const\n\nexport const DataTableInjectionSpots = {\n header: (tableId: string): InjectionSpotId => dataTableExtensionSpotId(tableId, 'header'),\n footer: (tableId: string): InjectionSpotId => dataTableExtensionSpotId(tableId, 'footer'),\n toolbar: (tableId: string): InjectionSpotId => dataTableExtensionSpotId(tableId, 'toolbar'),\n // Slot rendered immediately after the search input on the same row as the\n // FilterBar \u2014 intended for compact, icon-sized triggers (AI assistants,\n // saved view shortcuts, etc.). Hosts pass the resolved spot ID through to\n // FilterBar's `searchTrailing` prop. Stays empty when the table has no\n // search input.\n searchTrailing: (tableId: string): InjectionSpotId => dataTableExtensionSpotId(tableId, 'search-trailing'),\n emptyState: (tableId: string): InjectionSpotId => dataTableExtensionSpotId(tableId, 'empty-state'),\n columns: (tableId: string): InjectionSpotId => dataTableExtensionSpotId(tableId, 'columns'),\n rowActions: (tableId: string): InjectionSpotId => dataTableExtensionSpotId(tableId, 'row-actions'),\n bulkActions: (tableId: string): InjectionSpotId => dataTableExtensionSpotId(tableId, 'bulk-actions'),\n filters: (tableId: string): InjectionSpotId => dataTableExtensionSpotId(tableId, 'filters'),\n} as const\n\n// Portal chrome spot IDs (FROZEN once shipped)\nexport const PORTAL_SIDEBAR_MAIN_INJECTION_SPOT_ID: InjectionSpotId = 'menu:portal:sidebar:main'\nexport const PORTAL_SIDEBAR_ACCOUNT_INJECTION_SPOT_ID: InjectionSpotId = 'menu:portal:sidebar:account'\nexport const PORTAL_HEADER_ACTIONS_INJECTION_SPOT_ID: InjectionSpotId = 'menu:portal:header:actions'\nexport const PORTAL_USER_DROPDOWN_INJECTION_SPOT_ID: InjectionSpotId = 'menu:portal:user-dropdown'\n\n// Portal page injection spots (FROZEN once shipped)\nexport const PortalInjectionSpots = {\n dashboardSections: 'portal:dashboard:sections' as InjectionSpotId,\n dashboardProfile: 'portal:dashboard:profile' as InjectionSpotId,\n dashboardSidebar: 'portal:dashboard:sidebar' as InjectionSpotId,\n pageBefore: (pageId: string): InjectionSpotId => `portal:${pageId}:before` as InjectionSpotId,\n pageAfter: (pageId: string): InjectionSpotId => `portal:${pageId}:after` as InjectionSpotId,\n} as const\n\nexport const DetailInjectionSpots = {\n header: (entityId: string): InjectionSpotId => `detail:${entityId}:header`,\n tabs: (entityId: string): InjectionSpotId => `detail:${entityId}:tabs`,\n sidebar: (entityId: string): InjectionSpotId => `detail:${entityId}:sidebar`,\n footer: (entityId: string): InjectionSpotId => `detail:${entityId}:footer`,\n statusBadges: (entityId: string): InjectionSpotId => `detail:${entityId}:status-badges`,\n} as const\n"],
|
|
5
|
+
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEA,MAAM,2CAA4D;AAClE,MAAM,uCAAwD;AAC9D,MAAM,0CAA2D;AACjE,MAAM,wCAAyD;AAC/D,MAAM,2CAA4D;AAGlE,MAAM,gDAAiE;AACvE,MAAM,2CAA4D;AAClE,MAAM,wCAAyD;AAC/D,MAAM,+CAAgE;AAGtE,MAAM,iDAAkE;AACxE,MAAM,oDAAqE;AAG3E,MAAM,yBAAyB;AAAA,EACpC,MAAM,CAAC,aAAsC,wBAAwB,QAAQ;AAAA,EAC7E,cAAc,CAAC,aAAsC,wBAAwB,UAAU,eAAe;AAAA,EACtG,aAAa,CAAC,aAAsC,wBAAwB,UAAU,cAAc;AAAA,EACpG,QAAQ,CAAC,aAAsC,wBAAwB,UAAU,QAAQ;AAAA,EACzF,QAAQ,CAAC,aAAsC,wBAAwB,UAAU,QAAQ;AAAA,EACzF,QAAQ,CAAC,aAAsC,wBAAwB,UAAU,QAAQ;AAAA,EACzF,SAAS,CAAC,aAAsC,wBAAwB,UAAU,SAAS;AAAA,EAC3F,OAAO,CAAC,UAAkB,YAAqC,wBAAwB,UAAU,SAAS,OAAO,EAAE;AAAA,EACnH,aAAa,CAAC,UAAkB,YAAqC,wBAAwB,UAAU,SAAS,OAAO,SAAS;AAAA,EAChI,YAAY,CAAC,UAAkB,YAAqC,wBAAwB,UAAU,SAAS,OAAO,QAAQ;AAChI;AAEO,MAAM,0BAA0B;AAAA,EACrC,QAAQ,CAAC,YAAqC,yBAAyB,SAAS,QAAQ;AAAA,EACxF,QAAQ,CAAC,YAAqC,yBAAyB,SAAS,QAAQ;AAAA,EACxF,SAAS,CAAC,YAAqC,yBAAyB,SAAS,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM1F,gBAAgB,CAAC,YAAqC,yBAAyB,SAAS,iBAAiB;AAAA,EACzG,YAAY,CAAC,YAAqC,yBAAyB,SAAS,aAAa;AAAA,EACjG,SAAS,CAAC,YAAqC,yBAAyB,SAAS,SAAS;AAAA,EAC1F,YAAY,CAAC,YAAqC,yBAAyB,SAAS,aAAa;AAAA,EACjG,aAAa,CAAC,YAAqC,yBAAyB,SAAS,cAAc;AAAA,EACnG,SAAS,CAAC,YAAqC,yBAAyB,SAAS,SAAS;AAC5F;AAGO,MAAM,wCAAyD;AAC/D,MAAM,2CAA4D;AAClE,MAAM,0CAA2D;AACjE,MAAM,yCAA0D;AAGhE,MAAM,uBAAuB;AAAA,EAClC,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,YAAY,CAAC,WAAoC,UAAU,MAAM;AAAA,EACjE,WAAW,CAAC,WAAoC,UAAU,MAAM;AAClE;AAEO,MAAM,uBAAuB;AAAA,EAClC,QAAQ,CAAC,aAAsC,UAAU,QAAQ;AAAA,EACjE,MAAM,CAAC,aAAsC,UAAU,QAAQ;AAAA,EAC/D,SAAS,CAAC,aAAsC,UAAU,QAAQ;AAAA,EAClE,QAAQ,CAAC,aAAsC,UAAU,QAAQ;AAAA,EACjE,cAAc,CAAC,aAAsC,UAAU,QAAQ;AACzE;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|