@open-mercato/ui 0.6.7-develop.6862.1.c11a64ce0a → 0.6.7-develop.6869.1.d1ce9999ea

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/ai/AiChat.js +5 -5
  2. package/dist/ai/AiChat.js.map +2 -2
  3. package/dist/ai/LoopDisabledBanner.js +1 -1
  4. package/dist/ai/LoopDisabledBanner.js.map +2 -2
  5. package/dist/ai/parts/ConfirmationCard.js +4 -4
  6. package/dist/ai/parts/ConfirmationCard.js.map +2 -2
  7. package/dist/ai/parts/FieldDiffCard.js +2 -2
  8. package/dist/ai/parts/FieldDiffCard.js.map +2 -2
  9. package/dist/ai/parts/MutationResultCard.js +3 -3
  10. package/dist/ai/parts/MutationResultCard.js.map +2 -2
  11. package/dist/ai/ui-parts/pending-phase3-placeholder.js +1 -1
  12. package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +2 -2
  13. package/dist/backend/PerspectiveSidebar.js +1 -1
  14. package/dist/backend/PerspectiveSidebar.js.map +2 -2
  15. package/dist/backend/custom-fields/FieldDefinitionsManager.js +1 -1
  16. package/dist/backend/custom-fields/FieldDefinitionsManager.js.map +2 -2
  17. package/dist/backend/dashboard/DashboardScreen.js +3 -3
  18. package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
  19. package/dist/backend/filters/AdvancedFilterPanel.js +1 -1
  20. package/dist/backend/filters/AdvancedFilterPanel.js.map +2 -2
  21. package/dist/backend/inputs/ComboboxInput.js.map +1 -1
  22. package/dist/backend/messages/EmailThreadsPanel.js +1 -1
  23. package/dist/backend/messages/EmailThreadsPanel.js.map +2 -2
  24. package/dist/backend/version-history/VersionHistoryPanel.js +1 -1
  25. package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
  26. package/dist/primitives/ErrorNotice.js +1 -1
  27. package/dist/primitives/ErrorNotice.js.map +2 -2
  28. package/dist/primitives/Notice.js +1 -1
  29. package/dist/primitives/Notice.js.map +1 -1
  30. package/package.json +3 -3
  31. package/src/ai/AiChat.tsx +6 -6
  32. package/src/ai/LoopDisabledBanner.tsx +1 -1
  33. package/src/ai/parts/ConfirmationCard.tsx +4 -4
  34. package/src/ai/parts/FieldDiffCard.tsx +2 -2
  35. package/src/ai/parts/MutationResultCard.tsx +3 -3
  36. package/src/ai/ui-parts/pending-phase3-placeholder.tsx +2 -2
  37. package/src/backend/PerspectiveSidebar.tsx +1 -1
  38. package/src/backend/custom-fields/FieldDefinitionsManager.tsx +1 -1
  39. package/src/backend/dashboard/DashboardScreen.tsx +3 -3
  40. package/src/backend/filters/AdvancedFilterPanel.tsx +1 -1
  41. package/src/backend/inputs/ComboboxInput.tsx +1 -1
  42. package/src/backend/messages/EmailThreadsPanel.tsx +1 -1
  43. package/src/backend/version-history/VersionHistoryPanel.tsx +1 -1
  44. package/src/primitives/ErrorNotice.tsx +1 -1
  45. package/src/primitives/Notice.tsx +5 -5
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/custom-fields/FieldDefinitionsManager.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { Button } from '../../primitives/button'\nimport { Spinner } from '../../primitives/spinner'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { flash } from '../FlashMessages'\nimport { apiCall, readApiResultOrThrow } from '../utils/apiCall'\nimport { raiseCrudError } from '../utils/serverErrors'\nimport { invalidateCustomFieldDefs } from '../utils/customFieldDefs'\nimport { FieldDefinitionsEditor, type FieldDefinition, type FieldDefinitionError } from './FieldDefinitionsEditor'\nimport { upsertCustomFieldDefSchema } from '@open-mercato/shared/modules/entities/validators'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype FieldsetGroup = { code: string; title?: string; hint?: string }\ntype FieldsetDefinition = { code: string; label: string; icon?: string; description?: string; groups?: FieldsetGroup[] }\ntype DefinitionsManageResponse = {\n items?: any[]\n deletedKeys?: string[]\n fieldsets?: FieldsetDefinition[]\n settings?: { singleFieldsetPerRecord?: boolean }\n}\n\ntype FieldDefinitionsManagerProps = {\n entityId: string\n initialFieldset?: string | null\n onClose: () => void\n onSaved?: () => void | Promise<void>\n fullEditorHref?: string | null\n}\n\nexport type FieldDefinitionsManagerHandle = {\n submit: () => Promise<void>\n}\n\nfunction normalizeGroupPayload(value: unknown): FieldsetGroup | null {\n if (!value) return null\n if (typeof value === 'string') {\n const code = value.trim()\n return code ? { code } : null\n }\n if (typeof value !== 'object') return null\n const entry = value as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return null\n const group: FieldsetGroup = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n}\n\nexport const FieldDefinitionsManager = React.forwardRef<FieldDefinitionsManagerHandle, FieldDefinitionsManagerProps>(\n function FieldDefinitionsManager({ entityId, initialFieldset = null, onClose, onSaved, fullEditorHref }, ref) {\n const t = useT()\n const queryClient = useQueryClient()\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [statusError, setStatusError] = React.useState<string | null>(null)\n const [defs, setDefs] = React.useState<FieldDefinition[]>([])\n const [deletedKeys, setDeletedKeys] = React.useState<string[]>([])\n const [defErrors, setDefErrors] = React.useState<Record<number, FieldDefinitionError>>({})\n const [fieldsets, setFieldsets] = React.useState<FieldsetDefinition[]>([])\n const [activeFieldset, setActiveFieldset] = React.useState<string | null>(initialFieldset)\n const [singleFieldsetPerRecord, setSingleFieldsetPerRecord] = React.useState(true)\n const [isDirty, setIsDirty] = React.useState(false)\n const [orderDirty, setOrderDirty] = React.useState(false)\n\n const loadDefinitions = React.useCallback(async () => {\n if (!entityId) return\n setLoading(true)\n setStatusError(null)\n try {\n const json = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: t('entities.customFields.errors.loadFailed', 'Failed to load field definitions'), fallback: { items: [], deletedKeys: [] } },\n )\n const loaded: FieldDefinition[] = (json.items || []).map((d: any) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson || {},\n isActive: d.isActive !== false,\n }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDeletedKeys(Array.isArray(json.deletedKeys) ? json.deletedKeys : [])\n const loadedFieldsets = Array.isArray(json.fieldsets) ? json.fieldsets : []\n setFieldsets(loadedFieldsets)\n setActiveFieldset((current) => {\n if (initialFieldset && loadedFieldsets.some((fs) => fs.code === initialFieldset)) {\n return initialFieldset\n }\n if (current && loadedFieldsets.some((fs) => fs.code === current)) return current\n return loadedFieldsets[0]?.code ?? null\n })\n setSingleFieldsetPerRecord(json.settings?.singleFieldsetPerRecord !== false)\n setDefErrors({})\n setIsDirty(false)\n setOrderDirty(false)\n } catch (err) {\n const message = err instanceof Error ? err.message : t('entities.customFields.errors.loadFailed', 'Failed to load field definitions')\n setStatusError(message)\n } finally {\n setLoading(false)\n }\n }, [entityId, initialFieldset, t])\n\n React.useEffect(() => {\n loadDefinitions().catch(() => {})\n }, [loadDefinitions])\n\n const buildFieldsetPayload = React.useCallback(() => {\n const groupMap = new Map<string, FieldsetGroup[]>()\n defs.forEach((definition) => {\n const code = typeof definition.configJson?.fieldset === 'string' ? definition.configJson.fieldset : null\n if (!code) return\n const normalized = normalizeGroupPayload(definition.configJson?.group)\n if (!normalized) return\n const list = groupMap.get(code) ?? []\n if (!list.some((entry) => entry.code === normalized.code)) {\n list.push(normalized)\n groupMap.set(code, list)\n }\n })\n return fieldsets.map((fs) => ({\n ...fs,\n groups: groupMap.get(fs.code) ?? [],\n }))\n }, [defs, fieldsets])\n\n const validateDef = React.useCallback(\n (definition: FieldDefinition): FieldDefinitionError => {\n const parsed = upsertCustomFieldDefSchema.safeParse({\n entityId,\n key: definition.key,\n kind: definition.kind,\n configJson: definition.configJson,\n isActive: definition.isActive,\n })\n if (parsed.success) return {}\n const errs: FieldDefinitionError = {}\n for (const issue of parsed.error.issues) {\n if (issue.path.includes('key')) errs.key = issue.message\n if (issue.path.includes('kind')) errs.kind = issue.message\n }\n return errs\n },\n [entityId],\n )\n\n const validateAll = React.useCallback(() => {\n const nextErrors: Record<number, FieldDefinitionError> = {}\n defs.forEach((definition, index) => {\n nextErrors[index] = validateDef(definition)\n })\n setDefErrors(nextErrors)\n return Object.values(nextErrors).every((entry) => !entry.key && !entry.kind)\n }, [defs, validateDef])\n\n const handleDefinitionChange = React.useCallback((index: number, nextDef: FieldDefinition) => {\n setDefs((arr) => arr.map((entry, idx) => (idx === index ? nextDef : entry)))\n setDefErrors((prev) => ({ ...prev, [index]: validateDef(nextDef) }))\n setIsDirty(true)\n }, [validateDef])\n\n const handleAddField = React.useCallback(() => {\n setDefs((arr) => [\n ...arr,\n {\n key: '',\n kind: 'text',\n configJson: activeFieldset ? { fieldset: activeFieldset } : {},\n isActive: true,\n },\n ])\n setIsDirty(true)\n }, [activeFieldset])\n\n const handleRemoveField = React.useCallback(async (index: number) => {\n const def = defs[index]\n if (!def) return\n if (def.key) {\n try {\n const call = await apiCall('/api/entities/definitions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key: def.key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.deleteFailed', 'Failed to delete field'))\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('entities.customFields.errors.deleteFailed', 'Failed to delete field')\n flash(message, 'error')\n return\n }\n }\n setDefs((arr) => arr.filter((_, idx) => idx !== index))\n setIsDirty(true)\n }, [defs, entityId, t])\n\n const handleRestoreField = React.useCallback(async (key: string) => {\n try {\n const call = await apiCall('/api/entities/definitions.restore', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.restoreFailed', 'Failed to restore field'))\n }\n flash(t('entities.customFields.flash.restored', 'Field restored'), 'success')\n await loadDefinitions()\n } catch (error) {\n const message = error instanceof Error ? error.message : t('entities.customFields.errors.restoreFailed', 'Failed to restore field')\n flash(message, 'error')\n }\n }, [entityId, loadDefinitions, t])\n\n const handleReorder = React.useCallback((from: number, to: number) => {\n setDefs((arr) => {\n const next = [...arr]\n const [moved] = next.splice(from, 1)\n next.splice(to, 0, moved)\n return next\n })\n setIsDirty(true)\n setOrderDirty(true)\n }, [])\n\n const handleFieldsetsChange = React.useCallback((next: FieldsetDefinition[]) => {\n setFieldsets(next)\n if (!next.some((fs) => fs.code === activeFieldset)) {\n setActiveFieldset(next[0]?.code ?? null)\n }\n setIsDirty(true)\n }, [activeFieldset])\n\n const handleFieldsetCodeChange = React.useCallback((previousCode: string, nextCode: string) => {\n if (!previousCode || !nextCode || previousCode === nextCode) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== previousCode) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n nextConfig.fieldset = nextCode\n return { ...entry, configJson: nextConfig }\n }),\n )\n setActiveFieldset((current) => (current === previousCode ? nextCode : current))\n setIsDirty(true)\n }, [])\n\n const handleFieldsetRemoved = React.useCallback((code: string) => {\n if (!code) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== code) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n delete nextConfig.fieldset\n delete nextConfig.group\n return { ...entry, configJson: nextConfig }\n }),\n )\n setIsDirty(true)\n }, [])\n\n const submit = React.useCallback(async () => {\n if (!entityId) return\n setSaving(true)\n setStatusError(null)\n try {\n if (!validateAll()) {\n flash(t('entities.customFields.errors.validation', 'Please fix validation errors'), 'error')\n throw new Error(t('entities.customFields.errors.validation', 'Validation failed'))\n }\n const payload = {\n entityId,\n definitions: defs.filter((d) => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n fieldsets: buildFieldsetPayload(),\n singleFieldsetPerRecord,\n }\n const call = await apiCall('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.saveFailed', 'Failed to save definitions'))\n }\n await invalidateCustomFieldDefs(queryClient, entityId)\n flash(t('entities.customFields.flash.saved', 'Definitions saved'), 'success')\n setIsDirty(false)\n setOrderDirty(false)\n if (onSaved) await onSaved()\n onClose()\n } catch (err) {\n const message = err instanceof Error ? err.message : t('entities.customFields.errors.saveFailed', 'Failed to save definitions')\n setStatusError(message)\n } finally {\n setSaving(false)\n }\n }, [buildFieldsetPayload, defs, entityId, onClose, onSaved, queryClient, singleFieldsetPerRecord, t, validateAll])\n\n React.useImperativeHandle(ref, () => ({ submit }), [submit])\n\n const cancelLabel = t('ui.forms.actions.cancel', 'Cancel')\n const saveLabel = saving ? t('ui.forms.status.saving', 'Saving...') : t('ui.forms.actions.save', 'Save')\n const openFullLabel = t('entities.customFields.openInNewTab', 'Open full editor')\n\n const handleOpenFullEditor = React.useCallback(() => {\n if (!fullEditorHref || typeof window === 'undefined') return\n window.open(fullEditorHref, '_blank', 'noopener,noreferrer')\n }, [fullEditorHref])\n\n const content = loading ? (\n <div className=\"flex h-64 items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : (\n <FieldDefinitionsEditor\n definitions={defs}\n errors={defErrors}\n deletedKeys={deletedKeys}\n fieldsets={fieldsets}\n activeFieldset={activeFieldset}\n onActiveFieldsetChange={setActiveFieldset}\n onFieldsetsChange={handleFieldsetsChange}\n onFieldsetCodeChange={handleFieldsetCodeChange}\n onFieldsetRemoved={handleFieldsetRemoved}\n singleFieldsetPerRecord={singleFieldsetPerRecord}\n onSingleFieldsetPerRecordChange={(value) => {\n setSingleFieldsetPerRecord(value)\n setIsDirty(true)\n }}\n onAddField={handleAddField}\n onRemoveField={(index) => { void handleRemoveField(index) }}\n onDefinitionChange={handleDefinitionChange}\n onRestoreField={(key) => { void handleRestoreField(key) }}\n onReorder={handleReorder}\n orderNotice={orderDirty ? { dirty: true, message: t('entities.customFields.orderNotice', 'Reordered \u2014 remember to save') } : undefined}\n translate={t}\n />\n )\n\n return (\n <div className=\"flex flex-col gap-3 sm:gap-4\">\n {statusError ? (\n <Alert variant=\"destructive\">\n <AlertTitle>{t('entities.customFields.errors.title', 'Something went wrong')}</AlertTitle>\n <AlertDescription>{statusError}</AlertDescription>\n </Alert>\n ) : null}\n <div className=\"rounded-lg border bg-card p-3 sm:p-4 max-h-[70vh] overflow-y-auto\">\n {content}\n </div>\n <div className=\"flex justify-end gap-2\">\n {fullEditorHref ? (\n <Button type=\"button\" variant=\"ghost\" onClick={handleOpenFullEditor}>\n {openFullLabel}\n </Button>\n ) : null}\n <Button type=\"button\" variant=\"outline\" onClick={onClose} disabled={saving}>\n {cancelLabel}\n </Button>\n <Button type=\"button\" onClick={() => { void submit() }} disabled={saving || !isDirty}>\n {saveLabel}\n </Button>\n </div>\n </div>\n )\n },\n)\n\nFieldDefinitionsManager.displayName = 'FieldDefinitionsManager'\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { Button } from '../../primitives/button'\nimport { Spinner } from '../../primitives/spinner'\nimport { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'\nimport { flash } from '../FlashMessages'\nimport { apiCall, readApiResultOrThrow } from '../utils/apiCall'\nimport { raiseCrudError } from '../utils/serverErrors'\nimport { invalidateCustomFieldDefs } from '../utils/customFieldDefs'\nimport { FieldDefinitionsEditor, type FieldDefinition, type FieldDefinitionError } from './FieldDefinitionsEditor'\nimport { upsertCustomFieldDefSchema } from '@open-mercato/shared/modules/entities/validators'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype FieldsetGroup = { code: string; title?: string; hint?: string }\ntype FieldsetDefinition = { code: string; label: string; icon?: string; description?: string; groups?: FieldsetGroup[] }\ntype DefinitionsManageResponse = {\n items?: any[]\n deletedKeys?: string[]\n fieldsets?: FieldsetDefinition[]\n settings?: { singleFieldsetPerRecord?: boolean }\n}\n\ntype FieldDefinitionsManagerProps = {\n entityId: string\n initialFieldset?: string | null\n onClose: () => void\n onSaved?: () => void | Promise<void>\n fullEditorHref?: string | null\n}\n\nexport type FieldDefinitionsManagerHandle = {\n submit: () => Promise<void>\n}\n\nfunction normalizeGroupPayload(value: unknown): FieldsetGroup | null {\n if (!value) return null\n if (typeof value === 'string') {\n const code = value.trim()\n return code ? { code } : null\n }\n if (typeof value !== 'object') return null\n const entry = value as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return null\n const group: FieldsetGroup = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n}\n\nexport const FieldDefinitionsManager = React.forwardRef<FieldDefinitionsManagerHandle, FieldDefinitionsManagerProps>(\n function FieldDefinitionsManager({ entityId, initialFieldset = null, onClose, onSaved, fullEditorHref }, ref) {\n const t = useT()\n const queryClient = useQueryClient()\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [statusError, setStatusError] = React.useState<string | null>(null)\n const [defs, setDefs] = React.useState<FieldDefinition[]>([])\n const [deletedKeys, setDeletedKeys] = React.useState<string[]>([])\n const [defErrors, setDefErrors] = React.useState<Record<number, FieldDefinitionError>>({})\n const [fieldsets, setFieldsets] = React.useState<FieldsetDefinition[]>([])\n const [activeFieldset, setActiveFieldset] = React.useState<string | null>(initialFieldset)\n const [singleFieldsetPerRecord, setSingleFieldsetPerRecord] = React.useState(true)\n const [isDirty, setIsDirty] = React.useState(false)\n const [orderDirty, setOrderDirty] = React.useState(false)\n\n const loadDefinitions = React.useCallback(async () => {\n if (!entityId) return\n setLoading(true)\n setStatusError(null)\n try {\n const json = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: t('entities.customFields.errors.loadFailed', 'Failed to load field definitions'), fallback: { items: [], deletedKeys: [] } },\n )\n const loaded: FieldDefinition[] = (json.items || []).map((d: any) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson || {},\n isActive: d.isActive !== false,\n }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDeletedKeys(Array.isArray(json.deletedKeys) ? json.deletedKeys : [])\n const loadedFieldsets = Array.isArray(json.fieldsets) ? json.fieldsets : []\n setFieldsets(loadedFieldsets)\n setActiveFieldset((current) => {\n if (initialFieldset && loadedFieldsets.some((fs) => fs.code === initialFieldset)) {\n return initialFieldset\n }\n if (current && loadedFieldsets.some((fs) => fs.code === current)) return current\n return loadedFieldsets[0]?.code ?? null\n })\n setSingleFieldsetPerRecord(json.settings?.singleFieldsetPerRecord !== false)\n setDefErrors({})\n setIsDirty(false)\n setOrderDirty(false)\n } catch (err) {\n const message = err instanceof Error ? err.message : t('entities.customFields.errors.loadFailed', 'Failed to load field definitions')\n setStatusError(message)\n } finally {\n setLoading(false)\n }\n }, [entityId, initialFieldset, t])\n\n React.useEffect(() => {\n loadDefinitions().catch(() => {})\n }, [loadDefinitions])\n\n const buildFieldsetPayload = React.useCallback(() => {\n const groupMap = new Map<string, FieldsetGroup[]>()\n defs.forEach((definition) => {\n const code = typeof definition.configJson?.fieldset === 'string' ? definition.configJson.fieldset : null\n if (!code) return\n const normalized = normalizeGroupPayload(definition.configJson?.group)\n if (!normalized) return\n const list = groupMap.get(code) ?? []\n if (!list.some((entry) => entry.code === normalized.code)) {\n list.push(normalized)\n groupMap.set(code, list)\n }\n })\n return fieldsets.map((fs) => ({\n ...fs,\n groups: groupMap.get(fs.code) ?? [],\n }))\n }, [defs, fieldsets])\n\n const validateDef = React.useCallback(\n (definition: FieldDefinition): FieldDefinitionError => {\n const parsed = upsertCustomFieldDefSchema.safeParse({\n entityId,\n key: definition.key,\n kind: definition.kind,\n configJson: definition.configJson,\n isActive: definition.isActive,\n })\n if (parsed.success) return {}\n const errs: FieldDefinitionError = {}\n for (const issue of parsed.error.issues) {\n if (issue.path.includes('key')) errs.key = issue.message\n if (issue.path.includes('kind')) errs.kind = issue.message\n }\n return errs\n },\n [entityId],\n )\n\n const validateAll = React.useCallback(() => {\n const nextErrors: Record<number, FieldDefinitionError> = {}\n defs.forEach((definition, index) => {\n nextErrors[index] = validateDef(definition)\n })\n setDefErrors(nextErrors)\n return Object.values(nextErrors).every((entry) => !entry.key && !entry.kind)\n }, [defs, validateDef])\n\n const handleDefinitionChange = React.useCallback((index: number, nextDef: FieldDefinition) => {\n setDefs((arr) => arr.map((entry, idx) => (idx === index ? nextDef : entry)))\n setDefErrors((prev) => ({ ...prev, [index]: validateDef(nextDef) }))\n setIsDirty(true)\n }, [validateDef])\n\n const handleAddField = React.useCallback(() => {\n setDefs((arr) => [\n ...arr,\n {\n key: '',\n kind: 'text',\n configJson: activeFieldset ? { fieldset: activeFieldset } : {},\n isActive: true,\n },\n ])\n setIsDirty(true)\n }, [activeFieldset])\n\n const handleRemoveField = React.useCallback(async (index: number) => {\n const def = defs[index]\n if (!def) return\n if (def.key) {\n try {\n const call = await apiCall('/api/entities/definitions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key: def.key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.deleteFailed', 'Failed to delete field'))\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('entities.customFields.errors.deleteFailed', 'Failed to delete field')\n flash(message, 'error')\n return\n }\n }\n setDefs((arr) => arr.filter((_, idx) => idx !== index))\n setIsDirty(true)\n }, [defs, entityId, t])\n\n const handleRestoreField = React.useCallback(async (key: string) => {\n try {\n const call = await apiCall('/api/entities/definitions.restore', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.restoreFailed', 'Failed to restore field'))\n }\n flash(t('entities.customFields.flash.restored', 'Field restored'), 'success')\n await loadDefinitions()\n } catch (error) {\n const message = error instanceof Error ? error.message : t('entities.customFields.errors.restoreFailed', 'Failed to restore field')\n flash(message, 'error')\n }\n }, [entityId, loadDefinitions, t])\n\n const handleReorder = React.useCallback((from: number, to: number) => {\n setDefs((arr) => {\n const next = [...arr]\n const [moved] = next.splice(from, 1)\n next.splice(to, 0, moved)\n return next\n })\n setIsDirty(true)\n setOrderDirty(true)\n }, [])\n\n const handleFieldsetsChange = React.useCallback((next: FieldsetDefinition[]) => {\n setFieldsets(next)\n if (!next.some((fs) => fs.code === activeFieldset)) {\n setActiveFieldset(next[0]?.code ?? null)\n }\n setIsDirty(true)\n }, [activeFieldset])\n\n const handleFieldsetCodeChange = React.useCallback((previousCode: string, nextCode: string) => {\n if (!previousCode || !nextCode || previousCode === nextCode) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== previousCode) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n nextConfig.fieldset = nextCode\n return { ...entry, configJson: nextConfig }\n }),\n )\n setActiveFieldset((current) => (current === previousCode ? nextCode : current))\n setIsDirty(true)\n }, [])\n\n const handleFieldsetRemoved = React.useCallback((code: string) => {\n if (!code) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== code) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n delete nextConfig.fieldset\n delete nextConfig.group\n return { ...entry, configJson: nextConfig }\n }),\n )\n setIsDirty(true)\n }, [])\n\n const submit = React.useCallback(async () => {\n if (!entityId) return\n setSaving(true)\n setStatusError(null)\n try {\n if (!validateAll()) {\n flash(t('entities.customFields.errors.validation', 'Please fix validation errors'), 'error')\n throw new Error(t('entities.customFields.errors.validation', 'Validation failed'))\n }\n const payload = {\n entityId,\n definitions: defs.filter((d) => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n fieldsets: buildFieldsetPayload(),\n singleFieldsetPerRecord,\n }\n const call = await apiCall('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.customFields.errors.saveFailed', 'Failed to save definitions'))\n }\n await invalidateCustomFieldDefs(queryClient, entityId)\n flash(t('entities.customFields.flash.saved', 'Definitions saved'), 'success')\n setIsDirty(false)\n setOrderDirty(false)\n if (onSaved) await onSaved()\n onClose()\n } catch (err) {\n const message = err instanceof Error ? err.message : t('entities.customFields.errors.saveFailed', 'Failed to save definitions')\n setStatusError(message)\n } finally {\n setSaving(false)\n }\n }, [buildFieldsetPayload, defs, entityId, onClose, onSaved, queryClient, singleFieldsetPerRecord, t, validateAll])\n\n React.useImperativeHandle(ref, () => ({ submit }), [submit])\n\n const cancelLabel = t('ui.forms.actions.cancel', 'Cancel')\n const saveLabel = saving ? t('ui.forms.status.saving', 'Saving...') : t('ui.forms.actions.save', 'Save')\n const openFullLabel = t('entities.customFields.openInNewTab', 'Open full editor')\n\n const handleOpenFullEditor = React.useCallback(() => {\n if (!fullEditorHref || typeof window === 'undefined') return\n window.open(fullEditorHref, '_blank', 'noopener,noreferrer')\n }, [fullEditorHref])\n\n const content = loading ? (\n <div className=\"flex h-64 items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : (\n <FieldDefinitionsEditor\n definitions={defs}\n errors={defErrors}\n deletedKeys={deletedKeys}\n fieldsets={fieldsets}\n activeFieldset={activeFieldset}\n onActiveFieldsetChange={setActiveFieldset}\n onFieldsetsChange={handleFieldsetsChange}\n onFieldsetCodeChange={handleFieldsetCodeChange}\n onFieldsetRemoved={handleFieldsetRemoved}\n singleFieldsetPerRecord={singleFieldsetPerRecord}\n onSingleFieldsetPerRecordChange={(value) => {\n setSingleFieldsetPerRecord(value)\n setIsDirty(true)\n }}\n onAddField={handleAddField}\n onRemoveField={(index) => { void handleRemoveField(index) }}\n onDefinitionChange={handleDefinitionChange}\n onRestoreField={(key) => { void handleRestoreField(key) }}\n onReorder={handleReorder}\n orderNotice={orderDirty ? { dirty: true, message: t('entities.customFields.orderNotice', 'Reordered \u2014 remember to save') } : undefined}\n translate={t}\n />\n )\n\n return (\n <div className=\"flex flex-col gap-3 sm:gap-4\">\n {statusError ? (\n <Alert status=\"error\">\n <AlertTitle>{t('entities.customFields.errors.title', 'Something went wrong')}</AlertTitle>\n <AlertDescription>{statusError}</AlertDescription>\n </Alert>\n ) : null}\n <div className=\"rounded-lg border bg-card p-3 sm:p-4 max-h-[70vh] overflow-y-auto\">\n {content}\n </div>\n <div className=\"flex justify-end gap-2\">\n {fullEditorHref ? (\n <Button type=\"button\" variant=\"ghost\" onClick={handleOpenFullEditor}>\n {openFullLabel}\n </Button>\n ) : null}\n <Button type=\"button\" variant=\"outline\" onClick={onClose} disabled={saving}>\n {cancelLabel}\n </Button>\n <Button type=\"button\" onClick={() => { void submit() }} disabled={saving || !isDirty}>\n {saveLabel}\n </Button>\n </div>\n </div>\n )\n },\n)\n\nFieldDefinitionsManager.displayName = 'FieldDefinitionsManager'\n"],
5
+ "mappings": "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",
6
6
  "names": ["code", "FieldDefinitionsManager"]
7
7
  }
@@ -280,14 +280,14 @@ function DashboardScreen() {
280
280
  return /* @__PURE__ */ jsx("div", { className: "flex min-h-[320px] items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, { size: "lg" }) });
281
281
  }
282
282
  if (error && layout.length === 0) {
283
- return /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
283
+ return /* @__PURE__ */ jsxs(Alert, { status: "error", children: [
284
284
  /* @__PURE__ */ jsx(AlertTitle, { children: t("dashboard.unavailable") }),
285
285
  /* @__PURE__ */ jsx(AlertDescription, { children: error }),
286
286
  /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleRefresh, children: t("dashboard.retry") }) })
287
287
  ] });
288
288
  }
289
289
  if (!hasRegisteredWidgets && layout.length === 0) {
290
- return /* @__PURE__ */ jsxs(Alert, { variant: "info", children: [
290
+ return /* @__PURE__ */ jsxs(Alert, { status: "information", children: [
291
291
  /* @__PURE__ */ jsx(AlertTitle, { children: t("dashboard.empty.noWidgets.title", "No dashboard widgets yet") }),
292
292
  /* @__PURE__ */ jsx(AlertDescription, { children: t(
293
293
  "dashboard.empty.noWidgets.description",
@@ -312,7 +312,7 @@ function DashboardScreen() {
312
312
  ] })
313
313
  ] })
314
314
  ] }),
315
- error && layout.length > 0 && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
315
+ error && layout.length > 0 && /* @__PURE__ */ jsxs(Alert, { status: "error", children: [
316
316
  /* @__PURE__ */ jsx(AlertTitle, { children: t("dashboard.error.partial") }),
317
317
  /* @__PURE__ */ jsx(AlertDescription, { children: error }),
318
318
  /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: handleRefresh, children: t("dashboard.error.reload") }) })
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/dashboard/DashboardScreen.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { getDashboardWidgets, loadDashboardWidgetModule } from './widgetRegistry'\nimport type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { GripVertical, Plus, RefreshCw, Settings2, Trash2, X, Loader2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { InjectionSpot } from '../injection/InjectionSpot'\nimport { WidgetDataBatchProvider } from './widgetData'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui').child({ component: 'DashboardScreen' })\n\ntype DashboardWidgetSize = 'sm' | 'md' | 'lg'\n\ntype LayoutItem = {\n id: string\n widgetId: string\n order: number\n priority?: number\n size?: DashboardWidgetSize\n settings?: unknown\n}\n\ntype WidgetMeta = {\n id: string\n title: string\n description: string | null\n defaultSize: DashboardWidgetSize\n defaultEnabled: boolean\n defaultSettings: unknown\n features: string[]\n moduleId: string\n icon: string | null\n loaderKey: string\n supportsRefresh: boolean\n}\n\ntype LayoutContext = {\n userId: string\n tenantId: string | null\n organizationId: string | null\n userName: string | null\n userEmail: string | null\n userLabel: string | null\n}\n\ntype LayoutResponse = {\n layout: { items: LayoutItem[] }\n widgets: WidgetMeta[]\n allowedWidgetIds: string[]\n canConfigure: boolean\n context: LayoutContext\n}\n\ntype WidgetModule = DashboardWidgetModule<any>\n\nfunction sizeClass(size: DashboardWidgetSize | undefined) {\n switch (size) {\n case 'lg':\n return 'md:col-span-2'\n case 'md':\n return 'md:col-span-1'\n case 'sm':\n default:\n return 'md:col-span-1'\n }\n}\n\nfunction sortLayout(items: LayoutItem[]): LayoutItem[] {\n return [...items]\n .sort((a, b) => {\n const aOrder = a.order ?? a.priority ?? 0\n const bOrder = b.order ?? b.priority ?? 0\n return aOrder - bOrder\n })\n .map((item, index) => ({ ...item, order: index, priority: index }))\n}\n\nconst DEFAULT_SIZE: DashboardWidgetSize = 'md'\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n // Fallback: timestamp + random for better uniqueness\n return Date.now().toString(36) + Math.random().toString(36).slice(2)\n}\n\nexport function DashboardScreen() {\n const t = useT()\n const organizationScopeVersion = useOrganizationScopeVersion()\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [hasRegisteredWidgets, setHasRegisteredWidgets] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [layout, setLayout] = React.useState<LayoutItem[]>([])\n const [widgetCatalog, setWidgetCatalog] = React.useState<WidgetMeta[]>([])\n const [allowedWidgetIds, setAllowedWidgetIds] = React.useState<string[]>([])\n const [canConfigure, setCanConfigure] = React.useState(false)\n const [context, setContext] = React.useState<LayoutContext | null>(null)\n const [editing, setEditing] = React.useState(false)\n const [settingsId, setSettingsId] = React.useState<string | null>(null)\n const pendingOpsRef = React.useRef(0)\n const saveQueueRef = React.useRef(Promise.resolve())\n const draggingIdRef = React.useRef<string | null>(null)\n\n const adjustSaving = React.useCallback((delta: number) => {\n pendingOpsRef.current = Math.max(0, pendingOpsRef.current + delta)\n setSaving(pendingOpsRef.current > 0)\n }, [])\n\n const load = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const call = await apiCall<LayoutResponse>('/api/dashboards/layout')\n if (!call.ok || !call.result) {\n throw new Error(`Failed with status ${call.status}`)\n }\n const data = call.result\n const registeredWidgetCount = getDashboardWidgets().length\n const normalizedLayout = sortLayout(data.layout?.items ?? [])\n setLayout(normalizedLayout)\n setWidgetCatalog(data.widgets ?? [])\n setHasRegisteredWidgets(registeredWidgetCount > 0 || (data.widgets ?? []).length > 0)\n setAllowedWidgetIds(data.allowedWidgetIds ?? [])\n setCanConfigure(!!data.canConfigure)\n if (data.context) {\n setContext({\n userId: data.context.userId,\n tenantId: data.context.tenantId ?? null,\n organizationId: data.context.organizationId ?? null,\n userName: data.context.userName ?? null,\n userEmail: data.context.userEmail ?? null,\n userLabel: data.context.userLabel ?? null,\n })\n } else {\n setContext(null)\n }\n if (!data.canConfigure) {\n setEditing(false)\n setSettingsId(null)\n }\n } catch (err) {\n logger.error('Failed to load dashboard layout', { err })\n if (getDashboardWidgets().length === 0) {\n setHasRegisteredWidgets(false)\n setLayout([])\n setWidgetCatalog([])\n setAllowedWidgetIds([])\n setCanConfigure(false)\n setContext(null)\n setEditing(false)\n setSettingsId(null)\n return\n }\n setError(t('dashboard.loadError'))\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n load()\n }, [load, organizationScopeVersion])\n\n const metaById = React.useMemo(() => {\n const map = new Map<string, WidgetMeta>()\n for (const meta of widgetCatalog) map.set(meta.id, meta)\n return map\n }, [widgetCatalog])\n\n const availableWidgets = React.useMemo(() => {\n const currentIds = new Set(layout.map((item) => item.widgetId))\n return widgetCatalog.filter((meta) => !currentIds.has(meta.id))\n }, [layout, widgetCatalog])\n\n const resolveWidgetTitle = React.useCallback((meta: WidgetMeta): string => {\n const keys = [\n `${meta.id}.title`,\n `dashboard.widgets.${meta.id}.title`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.title`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.title\n }, [t])\n\n const resolveWidgetDescription = React.useCallback((meta: WidgetMeta): string | null => {\n if (!meta.description) return null\n const keys = [\n `${meta.id}.description`,\n `dashboard.widgets.${meta.id}.description`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.description`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.description\n }, [t])\n\n const queueLayoutSave = React.useCallback((items: LayoutItem[]) => {\n saveQueueRef.current = saveQueueRef.current.then(async () => {\n adjustSaving(1)\n try {\n const payload = {\n items: items.map((item, index) => ({\n id: item.id,\n widgetId: item.widgetId,\n order: index,\n priority: index,\n size: item.size ?? DEFAULT_SIZE,\n settings: item.settings ?? null,\n })),\n }\n const call = await apiCall('/api/dashboards/layout', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n logger.error('Failed to save layout', { err })\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n })\n }, [adjustSaving, t])\n\n const patchWidgetSettings = React.useCallback(async (itemId: string, nextSettings: unknown) => {\n adjustSaving(1)\n try {\n const call = await apiCall(`/api/dashboards/layout/${encodeURIComponent(itemId)}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ settings: nextSettings }),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n logger.error('Failed to update widget settings', { err })\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n }, [adjustSaving, t])\n\n const handleAddWidget = React.useCallback((widgetId: string) => {\n const meta = metaById.get(widgetId)\n if (!meta) return\n setLayout((prev) => {\n const next: LayoutItem[] = sortLayout([\n ...prev,\n {\n id: generateId(),\n widgetId: meta.id,\n order: prev.length,\n priority: prev.length,\n size: meta.defaultSize ?? DEFAULT_SIZE,\n settings: meta.defaultSettings ?? null,\n },\n ])\n queueLayoutSave(next)\n return next\n })\n setSettingsId(null)\n }, [metaById, queueLayoutSave])\n\n const handleRemoveWidget = React.useCallback((itemId: string) => {\n setLayout((prev) => {\n const next = sortLayout(prev.filter((item) => item.id !== itemId))\n queueLayoutSave(next)\n return next\n })\n if (settingsId === itemId) setSettingsId(null)\n }, [queueLayoutSave, settingsId])\n\n const handleReorder = React.useCallback((dragId: string | null, targetId: string) => {\n if (!dragId || dragId === targetId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((item) => item.id === dragId)\n const to = items.findIndex((item) => item.id === targetId)\n if (from === -1 || to === -1) return prev\n const [moved] = items.splice(from, 1)\n items.splice(to, 0, moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n }, [queueLayoutSave])\n\n const handleSettingsChange = React.useCallback((itemId: string, nextSettings: unknown) => {\n setLayout((prev) => prev.map((item) => (item.id === itemId ? { ...item, settings: nextSettings } : item)))\n void patchWidgetSettings(itemId, nextSettings)\n }, [patchWidgetSettings])\n\n const toggleEditing = React.useCallback(() => {\n if (!canConfigure) return\n setEditing((prev) => {\n const next = !prev\n if (!next) setSettingsId(null)\n return next\n })\n }, [canConfigure])\n\n const handleRefresh = React.useCallback(() => {\n load()\n }, [load])\n\n const injectionContext = React.useMemo(\n () => ({\n layout,\n widgetCatalog,\n allowedWidgetIds,\n canConfigure,\n editing,\n userContext: context,\n }),\n [allowedWidgetIds, canConfigure, context, editing, layout, widgetCatalog],\n )\n const dashboardBeforeSpotId = 'dashboard:before'\n const dashboardAfterSpotId = 'dashboard:after'\n\n if (loading) {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n )\n }\n\n if (error && layout.length === 0) {\n return (\n <Alert variant=\"destructive\">\n <AlertTitle>{t('dashboard.unavailable')}</AlertTitle>\n <AlertDescription>{error}</AlertDescription>\n <div className=\"mt-2\"><Button variant=\"outline\" onClick={handleRefresh}>{t('dashboard.retry')}</Button></div>\n </Alert>\n )\n }\n\n if (!hasRegisteredWidgets && layout.length === 0) {\n return (\n <Alert variant=\"info\">\n <AlertTitle>{t('dashboard.empty.noWidgets.title', 'No dashboard widgets yet')}</AlertTitle>\n <AlertDescription>\n {t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">{t('dashboard.title')}</h1>\n <p className=\"text-sm text-muted-foreground\">{t('dashboard.subtitle')}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n {saving && (\n <div className=\"flex items-center gap-1 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{t('dashboard.saving')}</span>\n </div>\n )}\n {canConfigure && (\n <Button variant={editing ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleEditing}>\n <Settings2 className=\"h-4 w-4\" />\n <span>{editing ? t('dashboard.action.done') : t('dashboard.action.customize')}</span>\n </Button>\n )}\n </div>\n </div>\n\n {error && layout.length > 0 && (\n <Alert variant=\"destructive\">\n <AlertTitle>{t('dashboard.error.partial')}</AlertTitle>\n <AlertDescription>{error}</AlertDescription>\n <div className=\"mt-2\"><Button variant=\"ghost\" onClick={handleRefresh}>{t('dashboard.error.reload')}</Button></div>\n </Alert>\n )}\n\n <InjectionSpot spotId={dashboardBeforeSpotId} context={injectionContext} />\n\n {editing && availableWidgets.length > 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/50 p-4\">\n <div className=\"mb-2 text-sm font-medium text-muted-foreground\">{t('dashboard.addWidget')}</div>\n <div className=\"flex flex-wrap gap-2\">\n {availableWidgets.map((meta) => (\n <Button\n key={meta.id}\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleAddWidget(meta.id)}\n >\n <Plus className=\"h-4 w-4\" />\n {resolveWidgetTitle(meta)}\n </Button>\n ))}\n </div>\n </div>\n )}\n\n <WidgetDataBatchProvider>\n <div className={cn(\n 'grid gap-3 sm:gap-4 md:gap-6',\n 'grid-cols-1',\n 'md:grid-cols-2',\n 'xl:grid-cols-3'\n )}\n onDragOver={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n }}\n onDrop={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n if (!dragId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((entry) => entry.id === dragId)\n if (from === -1) return prev\n const [moved] = items.splice(from, 1)\n items.push(moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n draggingIdRef.current = null\n }}>\n {layout.map((item) => {\n const meta = metaById.get(item.widgetId)\n if (!meta) return null\n const title = resolveWidgetTitle(meta)\n const description = resolveWidgetDescription(meta)\n return (\n <DashboardWidgetCard\n key={item.id}\n item={item}\n meta={meta}\n title={title}\n description={description}\n context={context}\n editing={editing && canConfigure}\n activeSettings={settingsId === item.id}\n onToggleSettings={() => setSettingsId((current) => (current === item.id ? null : item.id))}\n onRemove={() => handleRemoveWidget(item.id)}\n onSettingsChange={(settings) => handleSettingsChange(item.id, settings)}\n onDragStart={() => { draggingIdRef.current = item.id }}\n onDragEnd={() => { draggingIdRef.current = null }}\n onDrop={(event) => {\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n handleReorder(dragId, item.id)\n draggingIdRef.current = null\n }}\n onDragEnter={() => {}}\n onDragLeave={() => {}}\n sizeClass={sizeClass(item.size)}\n />\n )\n })}\n </div>\n </WidgetDataBatchProvider>\n\n {layout.length === 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/30 p-10 text-center text-sm text-muted-foreground\">\n {!hasRegisteredWidgets\n ? t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )\n : canConfigure ? t('dashboard.empty.configurable') : t('dashboard.empty.readonly')}\n </div>\n )}\n\n <InjectionSpot spotId={dashboardAfterSpotId} context={injectionContext} />\n </div>\n )\n}\n\ntype DashboardWidgetCardProps = {\n item: LayoutItem\n meta: WidgetMeta\n title: string\n description: string | null\n context: LayoutContext | null\n editing: boolean\n activeSettings: boolean\n onToggleSettings: () => void\n onRemove: () => void\n onSettingsChange: (next: unknown) => void\n onDragStart: () => void\n onDragEnd: () => void\n onDrop: (event: React.DragEvent<HTMLDivElement>) => void\n onDragEnter: () => void\n onDragLeave: () => void\n sizeClass: string\n}\n\nfunction DashboardWidgetCard({\n item,\n meta,\n title,\n description,\n context,\n editing,\n activeSettings,\n onToggleSettings,\n onRemove,\n onSettingsChange,\n onDragStart,\n onDragEnd,\n onDrop,\n onDragEnter,\n onDragLeave,\n sizeClass,\n}: DashboardWidgetCardProps) {\n const t = useT()\n const [module, setModule] = React.useState<WidgetModule | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [isDragOver, setIsDragOver] = React.useState(false)\n const [refreshToken, setRefreshToken] = React.useState(0)\n const [refreshing, setRefreshing] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n setLoading(true)\n setLoadError(null)\n loadDashboardWidgetModule(meta.loaderKey)\n .then((loaded) => {\n if (cancelled) return\n if (!loaded) {\n setModule(null)\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n return\n }\n setModule(loaded)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n logger.error('Failed to load widget module', { err })\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n })\n return () => { cancelled = true }\n }, [meta.loaderKey, t])\n\n React.useEffect(() => {\n if (!meta.supportsRefresh) {\n setRefreshing(false)\n }\n }, [meta.supportsRefresh])\n\n React.useEffect(() => {\n if (activeSettings) {\n setRefreshing(false)\n }\n }, [activeSettings])\n\n React.useEffect(() => {\n if (loadError) {\n setRefreshing(false)\n }\n }, [loadError])\n\n const handleRefreshStateChange = React.useCallback((next: boolean) => {\n setRefreshing(next)\n }, [])\n\n const triggerRefresh = React.useCallback(() => {\n if (loading || !!loadError) return\n setRefreshing(true)\n setRefreshToken((current) => current + 1)\n }, [loadError, loading])\n\n const hydratedSettings = React.useMemo(() => {\n const raw = item.settings ?? meta.defaultSettings ?? null\n if (module?.hydrateSettings) {\n try {\n return module.hydrateSettings(raw)\n } catch (err) {\n logger.warn('Failed to hydrate widget settings', { err })\n return raw\n }\n }\n return raw\n }, [item.settings, meta.defaultSettings, module])\n\n const handleSettingsChange = React.useCallback((next: unknown) => {\n let raw = next\n if (module?.dehydrateSettings) {\n try {\n raw = module.dehydrateSettings(next as never)\n } catch (err) {\n logger.warn('Failed to dehydrate widget settings', { err })\n }\n }\n onSettingsChange(raw)\n }, [module, onSettingsChange])\n\n const WidgetComponent = module?.Widget\n const mode = activeSettings ? 'settings' : 'view'\n\n return (\n <div\n className={cn(\n 'group relative flex h-full flex-col rounded-lg border bg-background shadow-sm transition',\n isDragOver ? 'border-primary ring-2 ring-primary/20' : 'hover:border-primary/40',\n editing ? 'cursor-grab' : 'cursor-default',\n sizeClass\n )}\n draggable={editing}\n onDragStart={(event) => {\n if (!editing) return\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', item.id)\n onDragStart()\n }}\n onDragEnd={() => {\n if (!editing) return\n onDragEnd()\n }}\n onDragOver={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n event.dataTransfer.dropEffect = 'move'\n if (!isDragOver) {\n setIsDragOver(true)\n onDragEnter()\n }\n }}\n onDrop={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n onDrop(event)\n setIsDragOver(false)\n onDragLeave()\n }}\n onDragLeave={(event) => {\n if (!editing) return\n event.stopPropagation()\n if (event.currentTarget.contains(event.relatedTarget as Node)) return\n setIsDragOver(false)\n onDragLeave()\n }}\n >\n <div className=\"flex items-center justify-between gap-2 border-b px-3 py-2\">\n <div className=\"flex items-center gap-2\">\n {editing && <GripVertical className=\"h-4 w-4 text-muted-foreground\" />}\n <div>\n <div className=\"text-sm font-medium leading-none\">{title}</div>\n {description ? <div className=\"mt-1 text-xs text-muted-foreground\">{description}</div> : null}\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {!editing && meta.supportsRefresh && (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n disabled={refreshing || loading || !!loadError}\n onClick={triggerRefresh}\n aria-label={t('dashboard.widget.refresh')}\n >\n {refreshing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n </IconButton>\n )}\n {editing && (\n <>\n <IconButton\n variant={activeSettings ? 'outline' : 'ghost'}\n size=\"sm\"\n onClick={onToggleSettings}\n aria-label={activeSettings ? t('dashboard.widget.closeSettings') : t('dashboard.widget.editSettings')}\n >\n {activeSettings ? <X className=\"h-4 w-4\" /> : <Settings2 className=\"h-4 w-4\" />}\n </IconButton>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={onRemove}\n aria-label={t('dashboard.widget.remove')}\n >\n <Trash2 className=\"h-4 w-4\" />\n </IconButton>\n </>\n )}\n </div>\n </div>\n <div className=\"flex-1 p-4\">\n {loading && (\n <div className=\"flex h-full min-h-[120px] items-center justify-center\">\n <Spinner />\n </div>\n )}\n {loadError && !loading && (\n <div className=\"text-sm text-muted-foreground\">{loadError}</div>\n )}\n {!loading && !loadError && WidgetComponent && (\n <WidgetComponent\n mode={mode as 'view' | 'settings'}\n layout={item}\n context={context ?? { userId: '', tenantId: null, organizationId: null, userName: null, userEmail: null, userLabel: null }}\n settings={hydratedSettings}\n onSettingsChange={handleSettingsChange}\n refreshToken={refreshToken}\n onRefreshStateChange={handleRefreshStateChange}\n />\n )}\n </div>\n </div>\n )\n}\n"],
5
- "mappings": ";AAgWQ,SAoWI,UApWJ,KAOF,YAPE;AA9VR,YAAY,WAAW;AACvB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,eAAe;AACxB,SAAS,qBAAqB,iCAAiC;AAE/D,SAAS,UAAU;AACnB,SAAS,cAAc,MAAM,WAAW,WAAW,QAAQ,GAAG,eAAe;AAC7E,SAAS,YAAY;AACrB,SAAS,mCAAmC;AAC5C,SAAS,qBAAqB;AAC9B,SAAS,+BAA+B;AACxC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,IAAI,EAAE,MAAM,EAAE,WAAW,kBAAkB,CAAC;AA8CxE,SAAS,UAAU,MAAuC;AACxD,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,WAAW,OAAmC;AACrD,SAAO,CAAC,GAAG,KAAK,EACb,KAAK,CAAC,GAAG,MAAM;AACd,UAAM,SAAS,EAAE,SAAS,EAAE,YAAY;AACxC,UAAM,SAAS,EAAE,SAAS,EAAE,YAAY;AACxC,WAAO,SAAS;AAAA,EAClB,CAAC,EACA,IAAI,CAAC,MAAM,WAAW,EAAE,GAAG,MAAM,OAAO,OAAO,UAAU,MAAM,EAAE;AACtE;AAEA,MAAM,eAAoC;AAE1C,SAAS,aAAqB;AAC5B,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO,KAAK,IAAI,EAAE,SAAS,EAAE,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC;AACrE;AAEO,SAAS,kBAAkB;AAChC,QAAM,IAAI,KAAK;AACf,QAAM,2BAA2B,4BAA4B;AAC7D,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,sBAAsB,uBAAuB,IAAI,MAAM,SAAS,IAAI;AAC3E,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAuB,CAAC,CAAC;AAC3D,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAuB,CAAC,CAAC;AACzE,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAmB,CAAC,CAAC;AAC3E,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,KAAK;AAC5D,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAA+B,IAAI;AACvE,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAwB,IAAI;AACtE,QAAM,gBAAgB,MAAM,OAAO,CAAC;AACpC,QAAM,eAAe,MAAM,OAAO,QAAQ,QAAQ,CAAC;AACnD,QAAM,gBAAgB,MAAM,OAAsB,IAAI;AAEtD,QAAM,eAAe,MAAM,YAAY,CAAC,UAAkB;AACxD,kBAAc,UAAU,KAAK,IAAI,GAAG,cAAc,UAAU,KAAK;AACjE,cAAU,cAAc,UAAU,CAAC;AAAA,EACrC,GAAG,CAAC,CAAC;AAEL,QAAM,OAAO,MAAM,YAAY,YAAY;AACzC,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AACF,YAAM,OAAO,MAAM,QAAwB,wBAAwB;AACnE,UAAI,CAAC,KAAK,MAAM,CAAC,KAAK,QAAQ;AAC5B,cAAM,IAAI,MAAM,sBAAsB,KAAK,MAAM,EAAE;AAAA,MACrD;AACA,YAAM,OAAO,KAAK;AAClB,YAAM,wBAAwB,oBAAoB,EAAE;AACpD,YAAM,mBAAmB,WAAW,KAAK,QAAQ,SAAS,CAAC,CAAC;AAC5D,gBAAU,gBAAgB;AAC1B,uBAAiB,KAAK,WAAW,CAAC,CAAC;AACnC,8BAAwB,wBAAwB,MAAM,KAAK,WAAW,CAAC,GAAG,SAAS,CAAC;AACpF,0BAAoB,KAAK,oBAAoB,CAAC,CAAC;AAC/C,sBAAgB,CAAC,CAAC,KAAK,YAAY;AACnC,UAAI,KAAK,SAAS;AAChB,mBAAW;AAAA,UACT,QAAQ,KAAK,QAAQ;AAAA,UACrB,UAAU,KAAK,QAAQ,YAAY;AAAA,UACnC,gBAAgB,KAAK,QAAQ,kBAAkB;AAAA,UAC/C,UAAU,KAAK,QAAQ,YAAY;AAAA,UACnC,WAAW,KAAK,QAAQ,aAAa;AAAA,UACrC,WAAW,KAAK,QAAQ,aAAa;AAAA,QACvC,CAAC;AAAA,MACH,OAAO;AACL,mBAAW,IAAI;AAAA,MACjB;AACA,UAAI,CAAC,KAAK,cAAc;AACtB,mBAAW,KAAK;AAChB,sBAAc,IAAI;AAAA,MACpB;AAAA,IACF,SAAS,KAAK;AACZ,aAAO,MAAM,mCAAmC,EAAE,IAAI,CAAC;AACvD,UAAI,oBAAoB,EAAE,WAAW,GAAG;AACtC,gCAAwB,KAAK;AAC7B,kBAAU,CAAC,CAAC;AACZ,yBAAiB,CAAC,CAAC;AACnB,4BAAoB,CAAC,CAAC;AACtB,wBAAgB,KAAK;AACrB,mBAAW,IAAI;AACf,mBAAW,KAAK;AAChB,sBAAc,IAAI;AAClB;AAAA,MACF;AACA,eAAS,EAAE,qBAAqB,CAAC;AAAA,IACnC,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,UAAU,MAAM;AACpB,SAAK;AAAA,EACP,GAAG,CAAC,MAAM,wBAAwB,CAAC;AAEnC,QAAM,WAAW,MAAM,QAAQ,MAAM;AACnC,UAAM,MAAM,oBAAI,IAAwB;AACxC,eAAW,QAAQ,cAAe,KAAI,IAAI,KAAK,IAAI,IAAI;AACvD,WAAO;AAAA,EACT,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,mBAAmB,MAAM,QAAQ,MAAM;AAC3C,UAAM,aAAa,IAAI,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC;AAC9D,WAAO,cAAc,OAAO,CAAC,SAAS,CAAC,WAAW,IAAI,KAAK,EAAE,CAAC;AAAA,EAChE,GAAG,CAAC,QAAQ,aAAa,CAAC;AAE1B,QAAM,qBAAqB,MAAM,YAAY,CAAC,SAA6B;AACzE,UAAM,OAAO;AAAA,MACX,GAAG,KAAK,EAAE;AAAA,MACV,qBAAqB,KAAK,EAAE;AAAA,IAC9B;AACA,QAAI,KAAK,GAAG,SAAS,GAAG,GAAG;AACzB,YAAM,QAAQ,KAAK,GAAG,MAAM,GAAG;AAC/B,YAAM,WAAW,MAAM,IAAI;AAC3B,WAAK,QAAQ,GAAG,MAAM,KAAK,GAAG,CAAC,YAAY,QAAQ,QAAQ;AAAA,IAC7D;AACA,eAAW,OAAO,MAAM;AACtB,YAAM,aAAa,EAAE,GAAG;AACxB,UAAI,eAAe,IAAK,QAAO;AAAA,IACjC;AACA,WAAO,KAAK;AAAA,EACd,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,2BAA2B,MAAM,YAAY,CAAC,SAAoC;AACtF,QAAI,CAAC,KAAK,YAAa,QAAO;AAC9B,UAAM,OAAO;AAAA,MACX,GAAG,KAAK,EAAE;AAAA,MACV,qBAAqB,KAAK,EAAE;AAAA,IAC9B;AACA,QAAI,KAAK,GAAG,SAAS,GAAG,GAAG;AACzB,YAAM,QAAQ,KAAK,GAAG,MAAM,GAAG;AAC/B,YAAM,WAAW,MAAM,IAAI;AAC3B,WAAK,QAAQ,GAAG,MAAM,KAAK,GAAG,CAAC,YAAY,QAAQ,cAAc;AAAA,IACnE;AACA,eAAW,OAAO,MAAM;AACtB,YAAM,aAAa,EAAE,GAAG;AACxB,UAAI,eAAe,IAAK,QAAO;AAAA,IACjC;AACA,WAAO,KAAK;AAAA,EACd,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,kBAAkB,MAAM,YAAY,CAAC,UAAwB;AACjE,iBAAa,UAAU,aAAa,QAAQ,KAAK,YAAY;AAC3D,mBAAa,CAAC;AACd,UAAI;AACF,cAAM,UAAU;AAAA,UACd,OAAO,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,YACjC,IAAI,KAAK;AAAA,YACT,UAAU,KAAK;AAAA,YACf,OAAO;AAAA,YACP,UAAU;AAAA,YACV,MAAM,KAAK,QAAQ;AAAA,YACnB,UAAU,KAAK,YAAY;AAAA,UAC7B,EAAE;AAAA,QACJ;AACA,cAAM,OAAO,MAAM,QAAQ,0BAA0B;AAAA,UACnD,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC9B,CAAC;AACD,YAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,sBAAsB,KAAK,MAAM,EAAE;AACjE,iBAAS,IAAI;AAAA,MACf,SAAS,KAAK;AACZ,eAAO,MAAM,yBAAyB,EAAE,IAAI,CAAC;AAC7C,iBAAS,EAAE,qBAAqB,CAAC;AAAA,MACnC,UAAE;AACA,qBAAa,EAAE;AAAA,MACjB;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,cAAc,CAAC,CAAC;AAEpB,QAAM,sBAAsB,MAAM,YAAY,OAAO,QAAgB,iBAA0B;AAC7F,iBAAa,CAAC;AACd,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,0BAA0B,mBAAmB,MAAM,CAAC,IAAI;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,aAAa,CAAC;AAAA,MACjD,CAAC;AACD,UAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,sBAAsB,KAAK,MAAM,EAAE;AACjE,eAAS,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,aAAO,MAAM,oCAAoC,EAAE,IAAI,CAAC;AACxD,eAAS,EAAE,qBAAqB,CAAC;AAAA,IACnC,UAAE;AACA,mBAAa,EAAE;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,cAAc,CAAC,CAAC;AAEpB,QAAM,kBAAkB,MAAM,YAAY,CAAC,aAAqB;AAC9D,UAAM,OAAO,SAAS,IAAI,QAAQ;AAClC,QAAI,CAAC,KAAM;AACX,cAAU,CAAC,SAAS;AAClB,YAAM,OAAqB,WAAW;AAAA,QACpC,GAAG;AAAA,QACH;AAAA,UACE,IAAI,WAAW;AAAA,UACf,UAAU,KAAK;AAAA,UACf,OAAO,KAAK;AAAA,UACZ,UAAU,KAAK;AAAA,UACf,MAAM,KAAK,eAAe;AAAA,UAC1B,UAAU,KAAK,mBAAmB;AAAA,QACpC;AAAA,MACF,CAAC;AACD,sBAAgB,IAAI;AACpB,aAAO;AAAA,IACT,CAAC;AACD,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,UAAU,eAAe,CAAC;AAE9B,QAAM,qBAAqB,MAAM,YAAY,CAAC,WAAmB;AAC/D,cAAU,CAAC,SAAS;AAClB,YAAM,OAAO,WAAW,KAAK,OAAO,CAAC,SAAS,KAAK,OAAO,MAAM,CAAC;AACjE,sBAAgB,IAAI;AACpB,aAAO;AAAA,IACT,CAAC;AACD,QAAI,eAAe,OAAQ,eAAc,IAAI;AAAA,EAC/C,GAAG,CAAC,iBAAiB,UAAU,CAAC;AAEhC,QAAM,gBAAgB,MAAM,YAAY,CAAC,QAAuB,aAAqB;AACnF,QAAI,CAAC,UAAU,WAAW,SAAU;AACpC,cAAU,CAAC,SAAS;AAClB,YAAM,QAAQ,CAAC,GAAG,IAAI;AACtB,YAAM,OAAO,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,MAAM;AACzD,YAAM,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,QAAQ;AACzD,UAAI,SAAS,MAAM,OAAO,GAAI,QAAO;AACrC,YAAM,CAAC,KAAK,IAAI,MAAM,OAAO,MAAM,CAAC;AACpC,YAAM,OAAO,IAAI,GAAG,KAAK;AACzB,YAAM,OAAO,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,QACvC,GAAG;AAAA,QACH,OAAO;AAAA,QACP,UAAU;AAAA,MACZ,EAAE;AACF,sBAAgB,IAAI;AACpB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,uBAAuB,MAAM,YAAY,CAAC,QAAgB,iBAA0B;AACxF,cAAU,CAAC,SAAS,KAAK,IAAI,CAAC,SAAU,KAAK,OAAO,SAAS,EAAE,GAAG,MAAM,UAAU,aAAa,IAAI,IAAK,CAAC;AACzG,SAAK,oBAAoB,QAAQ,YAAY;AAAA,EAC/C,GAAG,CAAC,mBAAmB,CAAC;AAExB,QAAM,gBAAgB,MAAM,YAAY,MAAM;AAC5C,QAAI,CAAC,aAAc;AACnB,eAAW,CAAC,SAAS;AACnB,YAAM,OAAO,CAAC;AACd,UAAI,CAAC,KAAM,eAAc,IAAI;AAC7B,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,gBAAgB,MAAM,YAAY,MAAM;AAC5C,SAAK;AAAA,EACP,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,IACf;AAAA,IACA,CAAC,kBAAkB,cAAc,SAAS,SAAS,QAAQ,aAAa;AAAA,EAC1E;AACA,QAAM,wBAAwB;AAC9B,QAAM,uBAAuB;AAE7B,MAAI,SAAS;AACX,WACE,oBAAC,SAAI,WAAU,kDACb,8BAAC,WAAQ,MAAK,MAAK,GACrB;AAAA,EAEJ;AAEA,MAAI,SAAS,OAAO,WAAW,GAAG;AAChC,WACE,qBAAC,SAAM,SAAQ,eACb;AAAA,0BAAC,cAAY,YAAE,uBAAuB,GAAE;AAAA,MACxC,oBAAC,oBAAkB,iBAAM;AAAA,MACzB,oBAAC,SAAI,WAAU,QAAO,8BAAC,UAAO,SAAQ,WAAU,SAAS,eAAgB,YAAE,iBAAiB,GAAE,GAAS;AAAA,OACzG;AAAA,EAEJ;AAEA,MAAI,CAAC,wBAAwB,OAAO,WAAW,GAAG;AAChD,WACE,qBAAC,SAAM,SAAQ,QACb;AAAA,0BAAC,cAAY,YAAE,mCAAmC,0BAA0B,GAAE;AAAA,MAC9E,oBAAC,oBACE;AAAA,QACC;AAAA,QACA;AAAA,MACF,GACF;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,qDACb;AAAA,2BAAC,SACC;AAAA,4BAAC,QAAG,WAAU,yCAAyC,YAAE,iBAAiB,GAAE;AAAA,QAC5E,oBAAC,OAAE,WAAU,iCAAiC,YAAE,oBAAoB,GAAE;AAAA,SACxE;AAAA,MACA,qBAAC,SAAI,WAAU,2BACZ;AAAA,kBACC,qBAAC,SAAI,WAAU,yDACb;AAAA,8BAAC,WAAQ,WAAU,wBAAuB;AAAA,UAC1C,oBAAC,UAAM,YAAE,kBAAkB,GAAE;AAAA,WAC/B;AAAA,QAED,gBACC,qBAAC,UAAO,SAAS,UAAU,cAAc,WAAW,MAAK,MAAK,SAAS,eACrE;AAAA,8BAAC,aAAU,WAAU,WAAU;AAAA,UAC/B,oBAAC,UAAM,oBAAU,EAAE,uBAAuB,IAAI,EAAE,4BAA4B,GAAE;AAAA,WAChF;AAAA,SAEJ;AAAA,OACF;AAAA,IAEC,SAAS,OAAO,SAAS,KACxB,qBAAC,SAAM,SAAQ,eACb;AAAA,0BAAC,cAAY,YAAE,yBAAyB,GAAE;AAAA,MAC1C,oBAAC,oBAAkB,iBAAM;AAAA,MACzB,oBAAC,SAAI,WAAU,QAAO,8BAAC,UAAO,SAAQ,SAAQ,SAAS,eAAgB,YAAE,wBAAwB,GAAE,GAAS;AAAA,OAC9G;AAAA,IAGF,oBAAC,iBAAc,QAAQ,uBAAuB,SAAS,kBAAkB;AAAA,IAExE,WAAW,iBAAiB,SAAS,KACpC,qBAAC,SAAI,WAAU,mDACb;AAAA,0BAAC,SAAI,WAAU,kDAAkD,YAAE,qBAAqB,GAAE;AAAA,MAC1F,oBAAC,SAAI,WAAU,wBACZ,2BAAiB,IAAI,CAAC,SACrB;AAAA,QAAC;AAAA;AAAA,UAEC,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,SAAS,MAAM,gBAAgB,KAAK,EAAE;AAAA,UAEtC;AAAA,gCAAC,QAAK,WAAU,WAAU;AAAA,YACzB,mBAAmB,IAAI;AAAA;AAAA;AAAA,QANnB,KAAK;AAAA,MAOZ,CACD,GACH;AAAA,OACF;AAAA,IAGF,oBAAC,2BACD;AAAA,MAAC;AAAA;AAAA,QAAI,WAAW;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,YAAY,CAAC,UAAU;AACrB,cAAI,CAAC,WAAW,CAAC,aAAc;AAC/B,gBAAM,eAAe;AACrB,gBAAM,aAAa,aAAa;AAAA,QAClC;AAAA,QACA,QAAQ,CAAC,UAAU;AACjB,cAAI,CAAC,WAAW,CAAC,aAAc;AAC/B,gBAAM,eAAe;AACrB,gBAAM,SAAS,MAAM,aAAa,QAAQ,YAAY,KAAK,cAAc;AACzE,cAAI,CAAC,OAAQ;AACb,oBAAU,CAAC,SAAS;AAClB,kBAAM,QAAQ,CAAC,GAAG,IAAI;AACtB,kBAAM,OAAO,MAAM,UAAU,CAAC,UAAU,MAAM,OAAO,MAAM;AAC3D,gBAAI,SAAS,GAAI,QAAO;AACxB,kBAAM,CAAC,KAAK,IAAI,MAAM,OAAO,MAAM,CAAC;AACpC,kBAAM,KAAK,KAAK;AAChB,kBAAM,OAAO,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,cACvC,GAAG;AAAA,cACH,OAAO;AAAA,cACP,UAAU;AAAA,YACZ,EAAE;AACF,4BAAgB,IAAI;AACpB,mBAAO;AAAA,UACT,CAAC;AACD,wBAAc,UAAU;AAAA,QAC1B;AAAA,QACG,iBAAO,IAAI,CAAC,SAAS;AACpB,gBAAM,OAAO,SAAS,IAAI,KAAK,QAAQ;AACvC,cAAI,CAAC,KAAM,QAAO;AAClB,gBAAM,QAAQ,mBAAmB,IAAI;AACrC,gBAAM,cAAc,yBAAyB,IAAI;AACjD,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,SAAS,WAAW;AAAA,cACpB,gBAAgB,eAAe,KAAK;AAAA,cACpC,kBAAkB,MAAM,cAAc,CAAC,YAAa,YAAY,KAAK,KAAK,OAAO,KAAK,EAAG;AAAA,cACzF,UAAU,MAAM,mBAAmB,KAAK,EAAE;AAAA,cAC1C,kBAAkB,CAAC,aAAa,qBAAqB,KAAK,IAAI,QAAQ;AAAA,cACtE,aAAa,MAAM;AAAE,8BAAc,UAAU,KAAK;AAAA,cAAG;AAAA,cACrD,WAAW,MAAM;AAAE,8BAAc,UAAU;AAAA,cAAK;AAAA,cAChD,QAAQ,CAAC,UAAU;AACjB,sBAAM,SAAS,MAAM,aAAa,QAAQ,YAAY,KAAK,cAAc;AACzE,8BAAc,QAAQ,KAAK,EAAE;AAC7B,8BAAc,UAAU;AAAA,cAC1B;AAAA,cACA,aAAa,MAAM;AAAA,cAAC;AAAA,cACpB,aAAa,MAAM;AAAA,cAAC;AAAA,cACpB,WAAW,UAAU,KAAK,IAAI;AAAA;AAAA,YApBzB,KAAK;AAAA,UAqBZ;AAAA,QAEJ,CAAC;AAAA;AAAA,IACH,GACA;AAAA,IAEC,OAAO,WAAW,KACjB,oBAAC,SAAI,WAAU,8FACZ,WAAC,uBACE;AAAA,MACA;AAAA,MACA;AAAA,IACF,IACE,eAAe,EAAE,8BAA8B,IAAI,EAAE,0BAA0B,GACrF;AAAA,IAGF,oBAAC,iBAAc,QAAQ,sBAAsB,SAAS,kBAAkB;AAAA,KAC1E;AAEJ;AAqBA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAAA;AACF,GAA6B;AAC3B,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA8B,IAAI;AACpE,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAwB,IAAI;AACpE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,CAAC;AACxD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AAExD,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY;AAChB,eAAW,IAAI;AACf,iBAAa,IAAI;AACjB,8BAA0B,KAAK,SAAS,EACrC,KAAK,CAAC,WAAW;AAChB,UAAI,UAAW;AACf,UAAI,CAAC,QAAQ;AACX,kBAAU,IAAI;AACd,qBAAa,EAAE,4BAA4B,CAAC;AAC5C,mBAAW,KAAK;AAChB;AAAA,MACF;AACA,gBAAU,MAAM;AAChB,iBAAW,KAAK;AAAA,IAClB,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,UAAI,UAAW;AACf,aAAO,MAAM,gCAAgC,EAAE,IAAI,CAAC;AACpD,mBAAa,EAAE,4BAA4B,CAAC;AAC5C,iBAAW,KAAK;AAAA,IAClB,CAAC;AACH,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAClC,GAAG,CAAC,KAAK,WAAW,CAAC,CAAC;AAEtB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAK,iBAAiB;AACzB,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,KAAK,eAAe,CAAC;AAEzB,QAAM,UAAU,MAAM;AACpB,QAAI,gBAAgB;AAClB,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,UAAU,MAAM;AACpB,QAAI,WAAW;AACb,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,2BAA2B,MAAM,YAAY,CAAC,SAAkB;AACpE,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAiB,MAAM,YAAY,MAAM;AAC7C,QAAI,WAAW,CAAC,CAAC,UAAW;AAC5B,kBAAc,IAAI;AAClB,oBAAgB,CAAC,YAAY,UAAU,CAAC;AAAA,EAC1C,GAAG,CAAC,WAAW,OAAO,CAAC;AAEvB,QAAM,mBAAmB,MAAM,QAAQ,MAAM;AAC3C,UAAM,MAAM,KAAK,YAAY,KAAK,mBAAmB;AACrD,QAAI,QAAQ,iBAAiB;AAC3B,UAAI;AACF,eAAO,OAAO,gBAAgB,GAAG;AAAA,MACnC,SAAS,KAAK;AACZ,eAAO,KAAK,qCAAqC,EAAE,IAAI,CAAC;AACxD,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,UAAU,KAAK,iBAAiB,MAAM,CAAC;AAEhD,QAAM,uBAAuB,MAAM,YAAY,CAAC,SAAkB;AAChE,QAAI,MAAM;AACV,QAAI,QAAQ,mBAAmB;AAC7B,UAAI;AACF,cAAM,OAAO,kBAAkB,IAAa;AAAA,MAC9C,SAAS,KAAK;AACZ,eAAO,KAAK,uCAAuC,EAAE,IAAI,CAAC;AAAA,MAC5D;AAAA,IACF;AACA,qBAAiB,GAAG;AAAA,EACtB,GAAG,CAAC,QAAQ,gBAAgB,CAAC;AAE7B,QAAM,kBAAkB,QAAQ;AAChC,QAAM,OAAO,iBAAiB,aAAa;AAE3C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,aAAa,0CAA0C;AAAA,QACvD,UAAU,gBAAgB;AAAA,QAC1BA;AAAA,MACF;AAAA,MACA,WAAW;AAAA,MACX,aAAa,CAAC,UAAU;AACtB,YAAI,CAAC,QAAS;AACd,cAAM,aAAa,gBAAgB;AACnC,cAAM,aAAa,QAAQ,cAAc,KAAK,EAAE;AAChD,oBAAY;AAAA,MACd;AAAA,MACA,WAAW,MAAM;AACf,YAAI,CAAC,QAAS;AACd,kBAAU;AAAA,MACZ;AAAA,MACA,YAAY,CAAC,UAAU;AACrB,YAAI,CAAC,QAAS;AACd,cAAM,eAAe;AACrB,cAAM,gBAAgB;AACtB,cAAM,aAAa,aAAa;AAChC,YAAI,CAAC,YAAY;AACf,wBAAc,IAAI;AAClB,sBAAY;AAAA,QACd;AAAA,MACF;AAAA,MACA,QAAQ,CAAC,UAAU;AACjB,YAAI,CAAC,QAAS;AACd,cAAM,eAAe;AACrB,cAAM,gBAAgB;AACtB,eAAO,KAAK;AACZ,sBAAc,KAAK;AACnB,oBAAY;AAAA,MACd;AAAA,MACA,aAAa,CAAC,UAAU;AACtB,YAAI,CAAC,QAAS;AACd,cAAM,gBAAgB;AACtB,YAAI,MAAM,cAAc,SAAS,MAAM,aAAqB,EAAG;AAC/D,sBAAc,KAAK;AACnB,oBAAY;AAAA,MACd;AAAA,MAEA;AAAA,6BAAC,SAAI,WAAU,8DACb;AAAA,+BAAC,SAAI,WAAU,2BACZ;AAAA,uBAAW,oBAAC,gBAAa,WAAU,iCAAgC;AAAA,YACpE,qBAAC,SACC;AAAA,kCAAC,SAAI,WAAU,oCAAoC,iBAAM;AAAA,cACxD,cAAc,oBAAC,SAAI,WAAU,sCAAsC,uBAAY,IAAS;AAAA,eAC3F;AAAA,aACF;AAAA,UACA,qBAAC,SAAI,WAAU,2BACZ;AAAA,aAAC,WAAW,KAAK,mBAChB;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,UAAU,cAAc,WAAW,CAAC,CAAC;AAAA,gBACrC,SAAS;AAAA,gBACT,cAAY,EAAE,0BAA0B;AAAA,gBAEvC,uBAAa,oBAAC,WAAQ,WAAU,wBAAuB,IAAK,oBAAC,aAAU,WAAU,WAAU;AAAA;AAAA,YAC9F;AAAA,YAED,WACC,iCACE;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,iBAAiB,YAAY;AAAA,kBACtC,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,cAAY,iBAAiB,EAAE,gCAAgC,IAAI,EAAE,+BAA+B;AAAA,kBAEnG,2BAAiB,oBAAC,KAAE,WAAU,WAAU,IAAK,oBAAC,aAAU,WAAU,WAAU;AAAA;AAAA,cAC/E;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,cAAY,EAAE,yBAAyB;AAAA,kBAEvC,8BAAC,UAAO,WAAU,WAAU;AAAA;AAAA,cAC9B;AAAA,eACF;AAAA,aAEJ;AAAA,WACF;AAAA,QACA,qBAAC,SAAI,WAAU,cACZ;AAAA,qBACC,oBAAC,SAAI,WAAU,yDACb,8BAAC,WAAQ,GACX;AAAA,UAED,aAAa,CAAC,WACb,oBAAC,SAAI,WAAU,iCAAiC,qBAAU;AAAA,UAE3D,CAAC,WAAW,CAAC,aAAa,mBACzB;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,QAAQ;AAAA,cACR,SAAS,WAAW,EAAE,QAAQ,IAAI,UAAU,MAAM,gBAAgB,MAAM,UAAU,MAAM,WAAW,MAAM,WAAW,KAAK;AAAA,cACzH,UAAU;AAAA,cACV,kBAAkB;AAAA,cAClB;AAAA,cACA,sBAAsB;AAAA;AAAA,UACxB;AAAA,WAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { getDashboardWidgets, loadDashboardWidgetModule } from './widgetRegistry'\nimport type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { GripVertical, Plus, RefreshCw, Settings2, Trash2, X, Loader2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { InjectionSpot } from '../injection/InjectionSpot'\nimport { WidgetDataBatchProvider } from './widgetData'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui').child({ component: 'DashboardScreen' })\n\ntype DashboardWidgetSize = 'sm' | 'md' | 'lg'\n\ntype LayoutItem = {\n id: string\n widgetId: string\n order: number\n priority?: number\n size?: DashboardWidgetSize\n settings?: unknown\n}\n\ntype WidgetMeta = {\n id: string\n title: string\n description: string | null\n defaultSize: DashboardWidgetSize\n defaultEnabled: boolean\n defaultSettings: unknown\n features: string[]\n moduleId: string\n icon: string | null\n loaderKey: string\n supportsRefresh: boolean\n}\n\ntype LayoutContext = {\n userId: string\n tenantId: string | null\n organizationId: string | null\n userName: string | null\n userEmail: string | null\n userLabel: string | null\n}\n\ntype LayoutResponse = {\n layout: { items: LayoutItem[] }\n widgets: WidgetMeta[]\n allowedWidgetIds: string[]\n canConfigure: boolean\n context: LayoutContext\n}\n\ntype WidgetModule = DashboardWidgetModule<any>\n\nfunction sizeClass(size: DashboardWidgetSize | undefined) {\n switch (size) {\n case 'lg':\n return 'md:col-span-2'\n case 'md':\n return 'md:col-span-1'\n case 'sm':\n default:\n return 'md:col-span-1'\n }\n}\n\nfunction sortLayout(items: LayoutItem[]): LayoutItem[] {\n return [...items]\n .sort((a, b) => {\n const aOrder = a.order ?? a.priority ?? 0\n const bOrder = b.order ?? b.priority ?? 0\n return aOrder - bOrder\n })\n .map((item, index) => ({ ...item, order: index, priority: index }))\n}\n\nconst DEFAULT_SIZE: DashboardWidgetSize = 'md'\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n // Fallback: timestamp + random for better uniqueness\n return Date.now().toString(36) + Math.random().toString(36).slice(2)\n}\n\nexport function DashboardScreen() {\n const t = useT()\n const organizationScopeVersion = useOrganizationScopeVersion()\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [hasRegisteredWidgets, setHasRegisteredWidgets] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [layout, setLayout] = React.useState<LayoutItem[]>([])\n const [widgetCatalog, setWidgetCatalog] = React.useState<WidgetMeta[]>([])\n const [allowedWidgetIds, setAllowedWidgetIds] = React.useState<string[]>([])\n const [canConfigure, setCanConfigure] = React.useState(false)\n const [context, setContext] = React.useState<LayoutContext | null>(null)\n const [editing, setEditing] = React.useState(false)\n const [settingsId, setSettingsId] = React.useState<string | null>(null)\n const pendingOpsRef = React.useRef(0)\n const saveQueueRef = React.useRef(Promise.resolve())\n const draggingIdRef = React.useRef<string | null>(null)\n\n const adjustSaving = React.useCallback((delta: number) => {\n pendingOpsRef.current = Math.max(0, pendingOpsRef.current + delta)\n setSaving(pendingOpsRef.current > 0)\n }, [])\n\n const load = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const call = await apiCall<LayoutResponse>('/api/dashboards/layout')\n if (!call.ok || !call.result) {\n throw new Error(`Failed with status ${call.status}`)\n }\n const data = call.result\n const registeredWidgetCount = getDashboardWidgets().length\n const normalizedLayout = sortLayout(data.layout?.items ?? [])\n setLayout(normalizedLayout)\n setWidgetCatalog(data.widgets ?? [])\n setHasRegisteredWidgets(registeredWidgetCount > 0 || (data.widgets ?? []).length > 0)\n setAllowedWidgetIds(data.allowedWidgetIds ?? [])\n setCanConfigure(!!data.canConfigure)\n if (data.context) {\n setContext({\n userId: data.context.userId,\n tenantId: data.context.tenantId ?? null,\n organizationId: data.context.organizationId ?? null,\n userName: data.context.userName ?? null,\n userEmail: data.context.userEmail ?? null,\n userLabel: data.context.userLabel ?? null,\n })\n } else {\n setContext(null)\n }\n if (!data.canConfigure) {\n setEditing(false)\n setSettingsId(null)\n }\n } catch (err) {\n logger.error('Failed to load dashboard layout', { err })\n if (getDashboardWidgets().length === 0) {\n setHasRegisteredWidgets(false)\n setLayout([])\n setWidgetCatalog([])\n setAllowedWidgetIds([])\n setCanConfigure(false)\n setContext(null)\n setEditing(false)\n setSettingsId(null)\n return\n }\n setError(t('dashboard.loadError'))\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n load()\n }, [load, organizationScopeVersion])\n\n const metaById = React.useMemo(() => {\n const map = new Map<string, WidgetMeta>()\n for (const meta of widgetCatalog) map.set(meta.id, meta)\n return map\n }, [widgetCatalog])\n\n const availableWidgets = React.useMemo(() => {\n const currentIds = new Set(layout.map((item) => item.widgetId))\n return widgetCatalog.filter((meta) => !currentIds.has(meta.id))\n }, [layout, widgetCatalog])\n\n const resolveWidgetTitle = React.useCallback((meta: WidgetMeta): string => {\n const keys = [\n `${meta.id}.title`,\n `dashboard.widgets.${meta.id}.title`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.title`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.title\n }, [t])\n\n const resolveWidgetDescription = React.useCallback((meta: WidgetMeta): string | null => {\n if (!meta.description) return null\n const keys = [\n `${meta.id}.description`,\n `dashboard.widgets.${meta.id}.description`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.description`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.description\n }, [t])\n\n const queueLayoutSave = React.useCallback((items: LayoutItem[]) => {\n saveQueueRef.current = saveQueueRef.current.then(async () => {\n adjustSaving(1)\n try {\n const payload = {\n items: items.map((item, index) => ({\n id: item.id,\n widgetId: item.widgetId,\n order: index,\n priority: index,\n size: item.size ?? DEFAULT_SIZE,\n settings: item.settings ?? null,\n })),\n }\n const call = await apiCall('/api/dashboards/layout', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n logger.error('Failed to save layout', { err })\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n })\n }, [adjustSaving, t])\n\n const patchWidgetSettings = React.useCallback(async (itemId: string, nextSettings: unknown) => {\n adjustSaving(1)\n try {\n const call = await apiCall(`/api/dashboards/layout/${encodeURIComponent(itemId)}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ settings: nextSettings }),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n logger.error('Failed to update widget settings', { err })\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n }, [adjustSaving, t])\n\n const handleAddWidget = React.useCallback((widgetId: string) => {\n const meta = metaById.get(widgetId)\n if (!meta) return\n setLayout((prev) => {\n const next: LayoutItem[] = sortLayout([\n ...prev,\n {\n id: generateId(),\n widgetId: meta.id,\n order: prev.length,\n priority: prev.length,\n size: meta.defaultSize ?? DEFAULT_SIZE,\n settings: meta.defaultSettings ?? null,\n },\n ])\n queueLayoutSave(next)\n return next\n })\n setSettingsId(null)\n }, [metaById, queueLayoutSave])\n\n const handleRemoveWidget = React.useCallback((itemId: string) => {\n setLayout((prev) => {\n const next = sortLayout(prev.filter((item) => item.id !== itemId))\n queueLayoutSave(next)\n return next\n })\n if (settingsId === itemId) setSettingsId(null)\n }, [queueLayoutSave, settingsId])\n\n const handleReorder = React.useCallback((dragId: string | null, targetId: string) => {\n if (!dragId || dragId === targetId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((item) => item.id === dragId)\n const to = items.findIndex((item) => item.id === targetId)\n if (from === -1 || to === -1) return prev\n const [moved] = items.splice(from, 1)\n items.splice(to, 0, moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n }, [queueLayoutSave])\n\n const handleSettingsChange = React.useCallback((itemId: string, nextSettings: unknown) => {\n setLayout((prev) => prev.map((item) => (item.id === itemId ? { ...item, settings: nextSettings } : item)))\n void patchWidgetSettings(itemId, nextSettings)\n }, [patchWidgetSettings])\n\n const toggleEditing = React.useCallback(() => {\n if (!canConfigure) return\n setEditing((prev) => {\n const next = !prev\n if (!next) setSettingsId(null)\n return next\n })\n }, [canConfigure])\n\n const handleRefresh = React.useCallback(() => {\n load()\n }, [load])\n\n const injectionContext = React.useMemo(\n () => ({\n layout,\n widgetCatalog,\n allowedWidgetIds,\n canConfigure,\n editing,\n userContext: context,\n }),\n [allowedWidgetIds, canConfigure, context, editing, layout, widgetCatalog],\n )\n const dashboardBeforeSpotId = 'dashboard:before'\n const dashboardAfterSpotId = 'dashboard:after'\n\n if (loading) {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n )\n }\n\n if (error && layout.length === 0) {\n return (\n <Alert status=\"error\">\n <AlertTitle>{t('dashboard.unavailable')}</AlertTitle>\n <AlertDescription>{error}</AlertDescription>\n <div className=\"mt-2\"><Button variant=\"outline\" onClick={handleRefresh}>{t('dashboard.retry')}</Button></div>\n </Alert>\n )\n }\n\n if (!hasRegisteredWidgets && layout.length === 0) {\n return (\n <Alert status=\"information\">\n <AlertTitle>{t('dashboard.empty.noWidgets.title', 'No dashboard widgets yet')}</AlertTitle>\n <AlertDescription>\n {t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">{t('dashboard.title')}</h1>\n <p className=\"text-sm text-muted-foreground\">{t('dashboard.subtitle')}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n {saving && (\n <div className=\"flex items-center gap-1 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{t('dashboard.saving')}</span>\n </div>\n )}\n {canConfigure && (\n <Button variant={editing ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleEditing}>\n <Settings2 className=\"h-4 w-4\" />\n <span>{editing ? t('dashboard.action.done') : t('dashboard.action.customize')}</span>\n </Button>\n )}\n </div>\n </div>\n\n {error && layout.length > 0 && (\n <Alert status=\"error\">\n <AlertTitle>{t('dashboard.error.partial')}</AlertTitle>\n <AlertDescription>{error}</AlertDescription>\n <div className=\"mt-2\"><Button variant=\"ghost\" onClick={handleRefresh}>{t('dashboard.error.reload')}</Button></div>\n </Alert>\n )}\n\n <InjectionSpot spotId={dashboardBeforeSpotId} context={injectionContext} />\n\n {editing && availableWidgets.length > 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/50 p-4\">\n <div className=\"mb-2 text-sm font-medium text-muted-foreground\">{t('dashboard.addWidget')}</div>\n <div className=\"flex flex-wrap gap-2\">\n {availableWidgets.map((meta) => (\n <Button\n key={meta.id}\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleAddWidget(meta.id)}\n >\n <Plus className=\"h-4 w-4\" />\n {resolveWidgetTitle(meta)}\n </Button>\n ))}\n </div>\n </div>\n )}\n\n <WidgetDataBatchProvider>\n <div className={cn(\n 'grid gap-3 sm:gap-4 md:gap-6',\n 'grid-cols-1',\n 'md:grid-cols-2',\n 'xl:grid-cols-3'\n )}\n onDragOver={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n }}\n onDrop={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n if (!dragId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((entry) => entry.id === dragId)\n if (from === -1) return prev\n const [moved] = items.splice(from, 1)\n items.push(moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n draggingIdRef.current = null\n }}>\n {layout.map((item) => {\n const meta = metaById.get(item.widgetId)\n if (!meta) return null\n const title = resolveWidgetTitle(meta)\n const description = resolveWidgetDescription(meta)\n return (\n <DashboardWidgetCard\n key={item.id}\n item={item}\n meta={meta}\n title={title}\n description={description}\n context={context}\n editing={editing && canConfigure}\n activeSettings={settingsId === item.id}\n onToggleSettings={() => setSettingsId((current) => (current === item.id ? null : item.id))}\n onRemove={() => handleRemoveWidget(item.id)}\n onSettingsChange={(settings) => handleSettingsChange(item.id, settings)}\n onDragStart={() => { draggingIdRef.current = item.id }}\n onDragEnd={() => { draggingIdRef.current = null }}\n onDrop={(event) => {\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n handleReorder(dragId, item.id)\n draggingIdRef.current = null\n }}\n onDragEnter={() => {}}\n onDragLeave={() => {}}\n sizeClass={sizeClass(item.size)}\n />\n )\n })}\n </div>\n </WidgetDataBatchProvider>\n\n {layout.length === 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/30 p-10 text-center text-sm text-muted-foreground\">\n {!hasRegisteredWidgets\n ? t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )\n : canConfigure ? t('dashboard.empty.configurable') : t('dashboard.empty.readonly')}\n </div>\n )}\n\n <InjectionSpot spotId={dashboardAfterSpotId} context={injectionContext} />\n </div>\n )\n}\n\ntype DashboardWidgetCardProps = {\n item: LayoutItem\n meta: WidgetMeta\n title: string\n description: string | null\n context: LayoutContext | null\n editing: boolean\n activeSettings: boolean\n onToggleSettings: () => void\n onRemove: () => void\n onSettingsChange: (next: unknown) => void\n onDragStart: () => void\n onDragEnd: () => void\n onDrop: (event: React.DragEvent<HTMLDivElement>) => void\n onDragEnter: () => void\n onDragLeave: () => void\n sizeClass: string\n}\n\nfunction DashboardWidgetCard({\n item,\n meta,\n title,\n description,\n context,\n editing,\n activeSettings,\n onToggleSettings,\n onRemove,\n onSettingsChange,\n onDragStart,\n onDragEnd,\n onDrop,\n onDragEnter,\n onDragLeave,\n sizeClass,\n}: DashboardWidgetCardProps) {\n const t = useT()\n const [module, setModule] = React.useState<WidgetModule | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [isDragOver, setIsDragOver] = React.useState(false)\n const [refreshToken, setRefreshToken] = React.useState(0)\n const [refreshing, setRefreshing] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n setLoading(true)\n setLoadError(null)\n loadDashboardWidgetModule(meta.loaderKey)\n .then((loaded) => {\n if (cancelled) return\n if (!loaded) {\n setModule(null)\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n return\n }\n setModule(loaded)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n logger.error('Failed to load widget module', { err })\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n })\n return () => { cancelled = true }\n }, [meta.loaderKey, t])\n\n React.useEffect(() => {\n if (!meta.supportsRefresh) {\n setRefreshing(false)\n }\n }, [meta.supportsRefresh])\n\n React.useEffect(() => {\n if (activeSettings) {\n setRefreshing(false)\n }\n }, [activeSettings])\n\n React.useEffect(() => {\n if (loadError) {\n setRefreshing(false)\n }\n }, [loadError])\n\n const handleRefreshStateChange = React.useCallback((next: boolean) => {\n setRefreshing(next)\n }, [])\n\n const triggerRefresh = React.useCallback(() => {\n if (loading || !!loadError) return\n setRefreshing(true)\n setRefreshToken((current) => current + 1)\n }, [loadError, loading])\n\n const hydratedSettings = React.useMemo(() => {\n const raw = item.settings ?? meta.defaultSettings ?? null\n if (module?.hydrateSettings) {\n try {\n return module.hydrateSettings(raw)\n } catch (err) {\n logger.warn('Failed to hydrate widget settings', { err })\n return raw\n }\n }\n return raw\n }, [item.settings, meta.defaultSettings, module])\n\n const handleSettingsChange = React.useCallback((next: unknown) => {\n let raw = next\n if (module?.dehydrateSettings) {\n try {\n raw = module.dehydrateSettings(next as never)\n } catch (err) {\n logger.warn('Failed to dehydrate widget settings', { err })\n }\n }\n onSettingsChange(raw)\n }, [module, onSettingsChange])\n\n const WidgetComponent = module?.Widget\n const mode = activeSettings ? 'settings' : 'view'\n\n return (\n <div\n className={cn(\n 'group relative flex h-full flex-col rounded-lg border bg-background shadow-sm transition',\n isDragOver ? 'border-primary ring-2 ring-primary/20' : 'hover:border-primary/40',\n editing ? 'cursor-grab' : 'cursor-default',\n sizeClass\n )}\n draggable={editing}\n onDragStart={(event) => {\n if (!editing) return\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', item.id)\n onDragStart()\n }}\n onDragEnd={() => {\n if (!editing) return\n onDragEnd()\n }}\n onDragOver={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n event.dataTransfer.dropEffect = 'move'\n if (!isDragOver) {\n setIsDragOver(true)\n onDragEnter()\n }\n }}\n onDrop={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n onDrop(event)\n setIsDragOver(false)\n onDragLeave()\n }}\n onDragLeave={(event) => {\n if (!editing) return\n event.stopPropagation()\n if (event.currentTarget.contains(event.relatedTarget as Node)) return\n setIsDragOver(false)\n onDragLeave()\n }}\n >\n <div className=\"flex items-center justify-between gap-2 border-b px-3 py-2\">\n <div className=\"flex items-center gap-2\">\n {editing && <GripVertical className=\"h-4 w-4 text-muted-foreground\" />}\n <div>\n <div className=\"text-sm font-medium leading-none\">{title}</div>\n {description ? <div className=\"mt-1 text-xs text-muted-foreground\">{description}</div> : null}\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {!editing && meta.supportsRefresh && (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n disabled={refreshing || loading || !!loadError}\n onClick={triggerRefresh}\n aria-label={t('dashboard.widget.refresh')}\n >\n {refreshing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n </IconButton>\n )}\n {editing && (\n <>\n <IconButton\n variant={activeSettings ? 'outline' : 'ghost'}\n size=\"sm\"\n onClick={onToggleSettings}\n aria-label={activeSettings ? t('dashboard.widget.closeSettings') : t('dashboard.widget.editSettings')}\n >\n {activeSettings ? <X className=\"h-4 w-4\" /> : <Settings2 className=\"h-4 w-4\" />}\n </IconButton>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={onRemove}\n aria-label={t('dashboard.widget.remove')}\n >\n <Trash2 className=\"h-4 w-4\" />\n </IconButton>\n </>\n )}\n </div>\n </div>\n <div className=\"flex-1 p-4\">\n {loading && (\n <div className=\"flex h-full min-h-[120px] items-center justify-center\">\n <Spinner />\n </div>\n )}\n {loadError && !loading && (\n <div className=\"text-sm text-muted-foreground\">{loadError}</div>\n )}\n {!loading && !loadError && WidgetComponent && (\n <WidgetComponent\n mode={mode as 'view' | 'settings'}\n layout={item}\n context={context ?? { userId: '', tenantId: null, organizationId: null, userName: null, userEmail: null, userLabel: null }}\n settings={hydratedSettings}\n onSettingsChange={handleSettingsChange}\n refreshToken={refreshToken}\n onRefreshStateChange={handleRefreshStateChange}\n />\n )}\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["sizeClass"]
7
7
  }
@@ -328,7 +328,7 @@ function AdvancedFilterPanel(props) {
328
328
  ),
329
329
  savedFiltersSection ? /* @__PURE__ */ jsx("div", { className: "border-t border-border p-3", children: savedFiltersSection }) : null
330
330
  ] }),
331
- props.pendingErrors.length > 0 ? /* @__PURE__ */ jsx("div", { className: "p-3 border-t border-border", "data-testid": "filter-validation-banner", children: /* @__PURE__ */ jsx(Alert, { variant: "destructive", children: /* @__PURE__ */ jsx(AlertDescription, { children: props.pendingErrors.length === 1 ? t("ui.advancedFilter.banner.incomplete", "{count} filter is incomplete \u2014 filters won't apply until all values are picked.", { count: 1 }) : t("ui.advancedFilter.banner.incompletePlural", "{count} filters are incomplete \u2014 filters won't apply until all values are picked.", { count: props.pendingErrors.length }) }) }) }) : null
331
+ props.pendingErrors.length > 0 ? /* @__PURE__ */ jsx("div", { className: "p-3 border-t border-border", "data-testid": "filter-validation-banner", children: /* @__PURE__ */ jsx(Alert, { status: "error", children: /* @__PURE__ */ jsx(AlertDescription, { children: props.pendingErrors.length === 1 ? t("ui.advancedFilter.banner.incomplete", "{count} filter is incomplete \u2014 filters won't apply until all values are picked.", { count: 1 }) : t("ui.advancedFilter.banner.incompletePlural", "{count} filters are incomplete \u2014 filters won't apply until all values are picked.", { count: props.pendingErrors.length }) }) }) }) : null
332
332
  ] })
333
333
  ]
334
334
  }
@@ -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": ";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,SAAQ,eACb,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;",
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": "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",
6
6
  "names": []
7
7
  }