@open-mercato/ui 0.6.6-develop.6465.1.019f0fb26f → 0.6.6-develop.6471.1.9ab5bdd79a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/AiChat.js +3 -3
- package/dist/ai/AiChat.js.map +2 -2
- package/dist/backend/CrudForm.js +19 -17
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/DataTable.js +5 -5
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/JsonDisplay.js +3 -1
- package/dist/backend/JsonDisplay.js.map +2 -2
- package/dist/backend/confirm-dialog/useConfirmDialog.js +3 -3
- package/dist/backend/confirm-dialog/useConfirmDialog.js.map +2 -2
- package/dist/backend/dashboard/DashboardScreen.js +8 -6
- package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
- package/dist/backend/dashboard/widgetRegistry.js +3 -1
- package/dist/backend/dashboard/widgetRegistry.js.map +2 -2
- package/dist/backend/detail/CustomDataSection.js +4 -2
- package/dist/backend/detail/CustomDataSection.js.map +2 -2
- package/dist/backend/detail/InlineEditors.js +3 -1
- package/dist/backend/detail/InlineEditors.js.map +2 -2
- package/dist/backend/detail/TagsSection.js +3 -1
- package/dist/backend/detail/TagsSection.js.map +2 -2
- package/dist/backend/injection/InjectionSpot.js +10 -8
- package/dist/backend/injection/InjectionSpot.js.map +2 -2
- package/dist/backend/injection/eventBridge.js +3 -1
- package/dist/backend/injection/eventBridge.js.map +2 -2
- package/dist/backend/injection/useGuardedMutation.js +3 -1
- package/dist/backend/injection/useGuardedMutation.js.map +2 -2
- package/dist/backend/injection/useInjectionDataWidgets.js +3 -1
- package/dist/backend/injection/useInjectionDataWidgets.js.map +2 -2
- package/dist/backend/injection/useRegisteredComponent.js +6 -9
- package/dist/backend/injection/useRegisteredComponent.js.map +2 -2
- package/dist/backend/injection/widgetRegistry.js +3 -1
- package/dist/backend/injection/widgetRegistry.js.map +2 -2
- package/dist/backend/inputs/LookupSelect.js +3 -1
- package/dist/backend/inputs/LookupSelect.js.map +2 -2
- package/dist/backend/notifications/NotificationDispatcher.js +4 -2
- package/dist/backend/notifications/NotificationDispatcher.js.map +2 -2
- package/dist/backend/sidebar/SidebarCustomizationEditor.js +7 -5
- package/dist/backend/sidebar/SidebarCustomizationEditor.js.map +2 -2
- package/dist/backend/utils/api.js +3 -1
- package/dist/backend/utils/api.js.map +2 -2
- package/dist/backend/utils/bulkDelete.js +4 -2
- package/dist/backend/utils/bulkDelete.js.map +2 -2
- package/dist/backend/version-history/VersionHistoryPanel.js +4 -2
- package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
- package/dist/portal/hooks/usePortalDashboardWidgets.js +3 -1
- package/dist/portal/hooks/usePortalDashboardWidgets.js.map +2 -2
- package/dist/portal/hooks/usePortalEventBridge.js +3 -1
- package/dist/portal/hooks/usePortalEventBridge.js.map +2 -2
- package/dist/primitives/Notice.js +3 -3
- package/dist/primitives/Notice.js.map +2 -2
- package/dist/theme/ThemeProvider.js +4 -2
- package/dist/theme/ThemeProvider.js.map +2 -2
- package/package.json +3 -3
- package/src/ai/AiChat.tsx +4 -4
- package/src/backend/CrudForm.tsx +20 -17
- package/src/backend/DataTable.tsx +6 -9
- package/src/backend/JsonDisplay.tsx +4 -1
- package/src/backend/__tests__/component-replacement.test.tsx +1 -1
- package/src/backend/confirm-dialog/useConfirmDialog.tsx +4 -3
- package/src/backend/dashboard/DashboardScreen.tsx +9 -6
- package/src/backend/dashboard/widgetRegistry.ts +4 -1
- package/src/backend/detail/CustomDataSection.tsx +5 -2
- package/src/backend/detail/InlineEditors.tsx +4 -1
- package/src/backend/detail/TagsSection.tsx +4 -1
- package/src/backend/injection/InjectionSpot.tsx +11 -8
- package/src/backend/injection/eventBridge.ts +4 -1
- package/src/backend/injection/useGuardedMutation.ts +4 -1
- package/src/backend/injection/useInjectionDataWidgets.ts +4 -1
- package/src/backend/injection/useRegisteredComponent.tsx +7 -9
- package/src/backend/injection/widgetRegistry.ts +4 -1
- package/src/backend/inputs/LookupSelect.tsx +4 -1
- package/src/backend/notifications/NotificationDispatcher.ts +5 -2
- package/src/backend/sidebar/SidebarCustomizationEditor.tsx +8 -5
- package/src/backend/utils/__tests__/bulkDelete.test.ts +29 -23
- package/src/backend/utils/api.ts +4 -2
- package/src/backend/utils/bulkDelete.ts +5 -2
- package/src/backend/version-history/VersionHistoryPanel.tsx +5 -2
- package/src/portal/hooks/usePortalDashboardWidgets.ts +4 -1
- package/src/portal/hooks/usePortalEventBridge.ts +4 -1
- package/src/primitives/Notice.tsx +4 -4
- package/src/theme/ThemeProvider.tsx +5 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/detail/CustomDataSection.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { PluggableList } from 'unified'\nimport { Pencil, X } from 'lucide-react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { DataLoader } from '@open-mercato/ui/primitives/DataLoader'\nimport type { CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { fetchCustomFieldFormFieldsWithDefinitions } from '@open-mercato/ui/backend/utils/customFieldForms'\nimport type { CustomFieldDefDto } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport {\n DictionaryValue,\n type DictionaryMap,\n} from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { ensureDictionaryEntries } from '@open-mercato/core/modules/dictionaries/components/hooks/useDictionaryEntries'\nimport {\n type ResolvedValueDisplay,\n collectRelationValueIds,\n extractOptionLookupKey,\n extractInlineOptionLabel,\n parseRelationOptionsMetadata,\n getRelationHrefContextFields,\n buildRelationHref,\n fetchRelationRecordDisplays,\n} from '@open-mercato/ui/backend/utils/customFieldRelationDisplay'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { MarkdownPreview } from '../markdown'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nlet markdownPluginsPromise: Promise<PluggableList> | null = null\n\nasync function loadMarkdownPlugins(): Promise<PluggableList> {\n if (isTestEnv) return []\n if (!markdownPluginsPromise) {\n markdownPluginsPromise = import('remark-gfm')\n .then((mod) => [mod.default ?? mod] as PluggableList)\n .catch(() => [])\n }\n return markdownPluginsPromise\n}\n\nconst MARKDOWN_FIELD_TYPES = new Set<CrudField['type']>(['text', 'textarea', 'richtext'])\nconst MARKDOWN_CLASSNAME =\n 'text-sm text-foreground break-words [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs'\n\nfunction renderMarkdownValue(content: string, remarkPlugins: PluggableList) {\n return (\n <MarkdownPreview remarkPlugins={remarkPlugins} className={MARKDOWN_CLASSNAME}>\n {content}\n </MarkdownPreview>\n )\n}\n\nfunction extractDictionaryValue(entry: unknown): string | null {\n if (typeof entry === 'string') {\n const trimmed = entry.trim()\n return trimmed.length ? trimmed : null\n }\n if (!entry || typeof entry !== 'object') return null\n const record = entry as Record<string, unknown>\n const candidate = record.value ?? record.name ?? record.id ?? record.key ?? record.label\n if (typeof candidate === 'string') {\n const trimmed = candidate.trim()\n return trimmed.length ? trimmed : null\n }\n return null\n}\n\nfunction readCustomDataFieldValue(values: Record<string, unknown> | undefined, fieldId: string): unknown {\n if (!values || !fieldId) return undefined\n const candidates = fieldId.startsWith('cf_')\n ? [fieldId, `cf:${fieldId.slice(3)}`, fieldId.slice(3)]\n : [fieldId, `cf_${fieldId}`, `cf:${fieldId}`]\n\n for (const candidate of candidates) {\n if (Object.prototype.hasOwnProperty.call(values, candidate)) {\n return values[candidate]\n }\n }\n return undefined\n}\n\nexport type CustomDataLabels = {\n loading: string\n emptyValue: string\n noFields: string\n defineFields?: string\n saveShortcut: string\n edit?: string\n cancel?: string\n}\n\nexport type CustomDataSectionProps = {\n entityId?: string\n entityIds?: string[]\n values: Record<string, unknown>\n onSubmit: (values: Record<string, unknown>) => Promise<void>\n title: string\n scopeVersion?: string | number | null\n loadFields?: (\n entityIds: string[],\n ) => Promise<{ fields: CrudField[]; definitions: CustomFieldDefDto[] }>\n labels: CustomDataLabels\n definitionHref?: string\n}\n\nfunction formatFieldValue(\n field: CrudField,\n value: unknown,\n emptyLabel: string,\n dictionaryMap?: DictionaryMap,\n remarkPlugins: PluggableList = [],\n resolvedDisplays?: Record<string, ResolvedValueDisplay>,\n): React.ReactNode {\n if (dictionaryMap) {\n if (value === undefined || value === null || value === '') {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n\n if (Array.isArray(value)) {\n const normalizedValues = value\n .map((entry) => extractDictionaryValue(entry))\n .filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)\n\n if (!normalizedValues.length) {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {normalizedValues.map((entry, index) => (\n <DictionaryValue\n key={`${field.id}-${entry}-${index}`}\n value={entry}\n map={dictionaryMap}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-card px-2 py-1 text-xs\"\n iconWrapperClassName=\"inline-flex h-4 w-4 items-center justify-center rounded-full border border-border bg-background\"\n iconClassName=\"h-3 w-3\"\n colorClassName=\"h-2.5 w-2.5 rounded-full\"\n />\n ))}\n </div>\n )\n }\n\n const resolved = extractDictionaryValue(value)\n if (!resolved) {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n\n return (\n <DictionaryValue\n value={resolved}\n map={dictionaryMap}\n className=\"inline-flex items-center gap-2 text-sm\"\n fallback={<span className=\"text-muted-foreground\">{emptyLabel}</span>}\n iconWrapperClassName=\"inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-card\"\n iconClassName=\"h-4 w-4\"\n colorClassName=\"h-3 w-3 rounded-full\"\n />\n )\n }\n\n const optionMap =\n 'options' in field && Array.isArray(field.options)\n ? field.options.reduce<Map<string, string>>((acc, option) => {\n acc.set(option.value, option.label)\n return acc\n }, new Map())\n : null\n\n const resolveOptionDisplay = (entry: unknown): ResolvedValueDisplay | null => {\n const lookupKey = extractOptionLookupKey(entry)\n if (lookupKey && resolvedDisplays?.[lookupKey]) {\n return resolvedDisplays[lookupKey]\n }\n const inlineLabel = extractInlineOptionLabel(entry)\n if (lookupKey) {\n return {\n label: inlineLabel ?? optionMap?.get(lookupKey) ?? lookupKey,\n }\n }\n if (inlineLabel) {\n return { label: inlineLabel }\n }\n return null\n }\n\n const renderResolvedDisplay = (display: ResolvedValueDisplay) => {\n if (!display.href) return display.label\n return (\n <Link\n href={display.href}\n className=\"font-medium text-primary underline-offset-2 hover:underline focus-visible:underline\"\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => event.stopPropagation()}\n >\n {display.label}\n </Link>\n )\n }\n\n if (value === undefined || value === null || value === '') {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n\n if (Array.isArray(value)) {\n if (!value.length) return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n return value.map((entry, index) => (\n <span\n key={`${field.id}-${index}`}\n className=\"mr-1 inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-xs\"\n >\n {(() => {\n const display = resolveOptionDisplay(entry)\n if (!display) return emptyLabel\n return renderResolvedDisplay(display)\n })()}\n </span>\n ))\n }\n\n if (typeof value === 'boolean') {\n return value ? 'Yes' : 'No'\n }\n\n if (resolvedDisplays && Object.keys(resolvedDisplays).length > 0) {\n const resolvedDisplay = resolveOptionDisplay(value)\n if (resolvedDisplay) {\n return renderResolvedDisplay(resolvedDisplay)\n }\n }\n\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n }\n\n const resolved = optionMap?.get(String(value)) ?? String(value)\n if (typeof value === 'string' && MARKDOWN_FIELD_TYPES.has(field.type)) {\n if (!resolved.trim().length) {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n return renderMarkdownValue(value, remarkPlugins)\n }\n if (!resolved.length) return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n return resolved\n}\n\nfunction CustomDataSectionImpl({\n entityId,\n entityIds,\n values,\n onSubmit,\n title,\n scopeVersion: scopeVersionProp,\n loadFields,\n labels,\n definitionHref: explicitDefinitionHref,\n}: CustomDataSectionProps) {\n const queryClient = useQueryClient()\n const defaultScopeVersion = useOrganizationScopeVersion()\n const scopeVersion = scopeVersionProp ?? defaultScopeVersion\n const resolvedScopeVersion = React.useMemo(\n () => (typeof scopeVersion === 'number' ? scopeVersion : Number(scopeVersion) || 0),\n [scopeVersion],\n )\n const [dictionaryMapsByField, setDictionaryMapsByField] = React.useState<Record<string, DictionaryMap>>({})\n const [resolvedDisplaysByField, setResolvedDisplaysByField] = React.useState<Record<string, Record<string, ResolvedValueDisplay>>>({})\n const [editing, setEditing] = React.useState(false)\n const sectionRef = React.useRef<HTMLDivElement | null>(null)\n const [markdownPlugins, setMarkdownPlugins] = React.useState<PluggableList>([])\n React.useEffect(() => {\n if (isTestEnv) return\n let mounted = true\n void loadMarkdownPlugins().then((plugins) => {\n if (!mounted) return\n setMarkdownPlugins(plugins)\n })\n return () => {\n mounted = false\n }\n }, [])\n const resolvedEntityIds = React.useMemo(() => {\n if (Array.isArray(entityIds) && entityIds.length) {\n const dedup = new Set<string>()\n const list: string[] = []\n entityIds.forEach((id) => {\n const trimmed = typeof id === 'string' ? id.trim() : ''\n if (!trimmed || dedup.has(trimmed)) return\n dedup.add(trimmed)\n list.push(trimmed)\n })\n return list\n }\n if (typeof entityId === 'string' && entityId.trim().length > 0) {\n return [entityId.trim()]\n }\n return []\n }, [entityId, entityIds])\n const primaryEntityId = resolvedEntityIds.length ? resolvedEntityIds[0] : undefined\n const customFieldFormsQuery = useQuery({\n queryKey: ['customFieldForms', resolvedScopeVersion, ...resolvedEntityIds],\n enabled: resolvedEntityIds.length > 0,\n staleTime: 5 * 60 * 1000,\n gcTime: 30 * 60 * 1000,\n queryFn: async () => {\n const loader = loadFields ?? fetchCustomFieldFormFieldsWithDefinitions\n return loader(resolvedEntityIds)\n },\n })\n const fields = React.useMemo(() => customFieldFormsQuery.data?.fields ?? [], [customFieldFormsQuery.data])\n const definitions = React.useMemo(\n () => customFieldFormsQuery.data?.definitions ?? [],\n [customFieldFormsQuery.data],\n )\n const formInitialValues = React.useMemo(() => {\n const next: Record<string, unknown> = { ...(values ?? {}) }\n fields.forEach((field) => {\n if (!field.id || Object.prototype.hasOwnProperty.call(next, field.id)) return\n const resolved = readCustomDataFieldValue(values, field.id)\n if (resolved !== undefined) next[field.id] = resolved\n })\n return next\n }, [fields, values])\n const [dictionaryLoading, setDictionaryLoading] = React.useState(false)\n const [relationLoading, setRelationLoading] = React.useState(false)\n const loading = customFieldFormsQuery.isLoading || dictionaryLoading || relationLoading\n const hasFields = fields.length > 0\n const definitionHref = explicitDefinitionHref ?? (primaryEntityId\n ? `/backend/entities/system/${encodeURIComponent(primaryEntityId)}`\n : undefined)\n\n React.useEffect(() => {\n if (!hasFields && editing) {\n setEditing(false)\n }\n }, [editing, hasFields])\n\n const submitActiveForm = React.useCallback(() => {\n const node = sectionRef.current?.querySelector('form')\n if (!node) return\n const form = node as HTMLFormElement\n if (typeof form.requestSubmit === 'function') {\n form.requestSubmit()\n return\n }\n form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }))\n }, [])\n\n const handleEditingKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (!editing) return\n if (event.key === 'Escape') {\n event.preventDefault()\n setEditing(false)\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n submitActiveForm()\n }\n },\n [editing, submitActiveForm],\n )\n\n const handleActivate = React.useCallback(() => {\n if (loading || editing || !hasFields) return\n setEditing(true)\n }, [editing, hasFields, loading])\n\n const handleReadOnlyKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (loading || editing || !hasFields) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setEditing(true)\n }\n },\n [editing, hasFields, loading],\n )\n\n React.useEffect(() => {\n if (!resolvedEntityIds.length || !definitions.length) {\n setDictionaryLoading((prev) => (prev ? false : prev))\n setDictionaryMapsByField((prev) => (Object.keys(prev).length ? {} : prev))\n return\n }\n\n let cancelled = false\n const load = async () => {\n setDictionaryLoading(true)\n try {\n const dictionaryDefs = definitions\n .map((def) => {\n const rawId = typeof def.dictionaryId === 'string' ? def.dictionaryId.trim() : ''\n if (!rawId) return null\n return { keyLower: def.key.toLowerCase(), dictionaryId: rawId }\n })\n .filter((entry): entry is { keyLower: string; dictionaryId: string } => !!entry)\n\n if (!dictionaryDefs.length) {\n if (!cancelled) {\n setDictionaryMapsByField((prev) => (Object.keys(prev).length ? {} : prev))\n }\n return\n }\n\n const uniqueDictionaryIds = Array.from(new Set(dictionaryDefs.map((entry) => entry.dictionaryId)))\n const mapsByDictionaryId: Record<string, DictionaryMap> = {}\n\n await Promise.all(\n uniqueDictionaryIds.map(async (dictionaryId) => {\n try {\n const data = await ensureDictionaryEntries(queryClient, dictionaryId, resolvedScopeVersion)\n mapsByDictionaryId[dictionaryId] = data.map\n } catch {\n mapsByDictionaryId[dictionaryId] = {}\n }\n }),\n )\n\n const dictionaryByKey = dictionaryDefs.reduce<Map<string, string>>((acc, entry) => {\n acc.set(entry.keyLower, entry.dictionaryId)\n return acc\n }, new Map())\n\n const nextMaps: Record<string, DictionaryMap> = {}\n fields.forEach((field) => {\n const id = typeof field.id === 'string' ? field.id : ''\n if (!id) return\n const normalizedKey = id.startsWith('cf_') ? id.slice(3) : id\n const keyLower = normalizedKey.toLowerCase()\n if (!keyLower) return\n const dictionaryId = dictionaryByKey.get(keyLower)\n if (!dictionaryId) return\n nextMaps[id] = mapsByDictionaryId[dictionaryId] ?? {}\n })\n\n if (!cancelled) {\n setDictionaryMapsByField((prev) => {\n const prevKeys = Object.keys(prev)\n const nextKeys = Object.keys(nextMaps)\n if (\n prevKeys.length === nextKeys.length &&\n prevKeys.every((key) => prev[key] === nextMaps[key])\n ) {\n return prev\n }\n return nextMaps\n })\n }\n } catch {\n if (!cancelled) {\n setDictionaryMapsByField((prev) => (Object.keys(prev).length ? {} : prev))\n }\n } finally {\n if (!cancelled) {\n setDictionaryLoading(false)\n }\n }\n }\n\n load().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [definitions, fields, queryClient, resolvedEntityIds, resolvedScopeVersion])\n\n React.useEffect(() => {\n if (!definitions.length || !fields.length) {\n setRelationLoading((prev) => (prev ? false : prev))\n setResolvedDisplaysByField((prev) => (Object.keys(prev).length ? {} : prev))\n return\n }\n\n const definitionsByKey = definitions.reduce<Map<string, CustomFieldDefDto>>((acc, definition) => {\n acc.set(definition.key.toLowerCase(), definition)\n return acc\n }, new Map())\n\n const relationFields = fields\n .map((field) => {\n const normalizedKey = field.id.startsWith('cf_') ? field.id.slice(3) : field.id\n const definition = definitionsByKey.get(normalizedKey.toLowerCase())\n if (!definition || definition.kind !== 'relation') return null\n const relationIds = collectRelationValueIds(readCustomDataFieldValue(values, field.id))\n if (!relationIds.length) return null\n return { field, definition, relationIds }\n })\n .filter((entry): entry is { field: CrudField; definition: CustomFieldDefDto; relationIds: string[] } => !!entry)\n\n if (!relationFields.length) {\n setRelationLoading((prev) => (prev ? false : prev))\n setResolvedDisplaysByField((prev) => (Object.keys(prev).length ? {} : prev))\n return\n }\n\n const abortController = new AbortController()\n\n const load = async () => {\n setRelationLoading(true)\n try {\n const nextDisplays: Record<string, Record<string, ResolvedValueDisplay>> = {}\n\n await Promise.all(\n relationFields.map(async ({ field, definition, relationIds }) => {\n const displays: Record<string, ResolvedValueDisplay> = {}\n\n if ('options' in field && Array.isArray(field.options)) {\n field.options.forEach((option) => {\n displays[option.value] = { label: option.label }\n })\n }\n\n if ('loadOptions' in field && typeof field.loadOptions === 'function') {\n try {\n const remoteOptions = await field.loadOptions()\n remoteOptions.forEach((option) => {\n const href = (() => {\n const relation = parseRelationOptionsMetadata(definition.optionsUrl)\n return relation ? buildRelationHref(relation.entityId, option.value) : undefined\n })()\n displays[option.value] = { label: option.label, href }\n })\n } catch (error) {\n console.debug('[CustomDataSection] Failed to load remote options for field', field.id, error)\n }\n }\n\n const relation = parseRelationOptionsMetadata(definition.optionsUrl)\n const needsRouteContext = relation ? getRelationHrefContextFields(relation.entityId).length > 0 : false\n const unresolvedIds = relationIds.filter((relationId) => {\n const display = displays[relationId]\n if (!display) return true\n return needsRouteContext && !display.href\n })\n if (relation && unresolvedIds.length) {\n try {\n const fetchedDisplays = await fetchRelationRecordDisplays(definition.optionsUrl!, relation, unresolvedIds, abortController.signal)\n Object.assign(displays, fetchedDisplays)\n } catch (error) {\n console.debug('[CustomDataSection] Failed to fetch relation record displays for field', field.id, error)\n unresolvedIds.forEach((relationId) => {\n if (!displays[relationId]) {\n displays[relationId] = {\n label: relationId,\n href: buildRelationHref(relation.entityId, relationId),\n }\n }\n })\n }\n }\n\n if (Object.keys(displays).length > 0) {\n nextDisplays[field.id] = displays\n }\n }),\n )\n\n if (!abortController.signal.aborted) {\n setResolvedDisplaysByField((prev) => {\n const previousKeys = Object.keys(prev)\n const nextKeys = Object.keys(nextDisplays)\n if (\n previousKeys.length === nextKeys.length &&\n previousKeys.every((key) => JSON.stringify(prev[key]) === JSON.stringify(nextDisplays[key]))\n ) {\n return prev\n }\n return nextDisplays\n })\n }\n } finally {\n if (!abortController.signal.aborted) {\n setRelationLoading(false)\n }\n }\n }\n\n void load()\n return () => {\n abortController.abort()\n }\n }, [definitions, fields, values])\n\n const handleSubmit = React.useCallback(\n async (input: Record<string, unknown>) => {\n await onSubmit(input)\n setEditing(false)\n },\n [onSubmit],\n )\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between group\">\n <h2 className=\"text-sm font-semibold\">{title}</h2>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => {\n if (!hasFields || loading) return\n setEditing((prev) => !prev)\n }}\n disabled={loading || !hasFields}\n className={\n editing\n ? 'opacity-100 transition-opacity duration-150'\n : 'opacity-100 md:opacity-0 transition-opacity duration-150 md:group-hover:opacity-100 focus-visible:opacity-100'\n }\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n <span className=\"sr-only\">{editing ? labels.cancel ?? 'Cancel' : labels.edit ?? 'Edit'}</span>\n </Button>\n </div>\n <DataLoader\n isLoading={loading}\n loadingMessage={labels.loading}\n spinnerSize=\"md\"\n className=\"min-h-[120px]\"\n >\n {editing ? (\n <div\n ref={sectionRef}\n className=\"rounded-lg border bg-card p-3 sm:p-4\"\n onKeyDown={handleEditingKeyDown}\n >\n <CrudForm<Record<string, unknown>>\n embedded\n entityId={primaryEntityId}\n entityIds={resolvedEntityIds}\n fields={fields}\n initialValues={formInitialValues}\n onSubmit={handleSubmit}\n submitLabel={labels.saveShortcut}\n isLoading={loading}\n />\n </div>\n ) : (\n <div\n className={cn(\n 'rounded-lg border bg-muted/30 p-3 sm:p-4 space-y-2 sm:space-y-3 transition hover:border-border/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n hasFields && !loading ? 'cursor-pointer' : 'cursor-default',\n )}\n role={hasFields && !loading ? 'button' : undefined}\n tabIndex={hasFields && !loading ? 0 : -1}\n onClick={hasFields && !loading ? handleActivate : undefined}\n onKeyDown={hasFields && !loading ? handleReadOnlyKeyDown : undefined}\n >\n {!hasFields ? (\n <p className=\"text-sm text-muted-foreground\">\n {labels.noFields}{' '}\n {definitionHref && labels.defineFields ? (\n <Link\n href={definitionHref}\n className=\"font-medium text-primary underline-offset-2 hover:underline focus-visible:underline\"\n >\n {labels.defineFields}\n </Link>\n ) : null}\n </p>\n ) : (\n fields.map((field) => (\n <div key={field.id} className=\"space-y-1\">\n <p className=\"text-xs uppercase tracking-wide text-muted-foreground\">\n {field.label}\n </p>\n <div className=\"text-sm break-words\">\n {formatFieldValue(\n field,\n readCustomDataFieldValue(values, field.id),\n labels.emptyValue,\n dictionaryMapsByField[field.id],\n markdownPlugins,\n resolvedDisplaysByField[field.id],\n )}\n </div>\n </div>\n ))\n )}\n </div>\n )}\n </DataLoader>\n </div>\n )\n}\n\nexport function CustomDataSection(props: CustomDataSectionProps) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'CustomDataSection')\n const Resolved = useRegisteredComponent<CustomDataSectionProps>(\n handle,\n CustomDataSectionImpl as React.ComponentType<CustomDataSectionProps>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n\nexport default CustomDataSection\n"],
|
|
5
|
-
"mappings": ";AAwDI,cA0iBI,YA1iBJ;AAtDJ,YAAY,WAAW;AACvB,OAAO,UAAU;AAEjB,SAAS,QAAQ,SAAS;AAC1B,SAAS,UAAU,sBAAsB;AACzC,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAE3B,SAAS,gBAAgB;AACzB,SAAS,iDAAiD;AAE1D;AAAA,EACE;AAAA,OAEK;AACP,SAAS,+BAA+B;AACxC;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mCAAmC;AAC5C,SAAS,UAAU;AACnB,SAAS,mCAAmC;AAC5C,SAAS,uBAAuB;AAChC,SAAS,8BAA8B;AAEvC,MAAM,YACJ,OAAO,YAAY,gBAClB,QAAQ,IAAI,aAAa,UAAU,OAAO,QAAQ,IAAI,mBAAmB;AAE5E,IAAI,yBAAwD;AAE5D,eAAe,sBAA8C;AAC3D,MAAI,UAAW,QAAO,CAAC;AACvB,MAAI,CAAC,wBAAwB;AAC3B,6BAAyB,OAAO,YAAY,EACzC,KAAK,CAAC,QAAQ,CAAC,IAAI,WAAW,GAAG,CAAkB,EACnD,MAAM,MAAM,CAAC,CAAC;AAAA,EACnB;AACA,SAAO;AACT;AAEA,MAAM,uBAAuB,oBAAI,IAAuB,CAAC,QAAQ,YAAY,UAAU,CAAC;AACxF,MAAM,qBACJ;AAEF,SAAS,oBAAoB,SAAiB,eAA8B;AAC1E,SACE,oBAAC,mBAAgB,eAA8B,WAAW,oBACvD,mBACH;AAEJ;AAEA,SAAS,uBAAuB,OAA+B;AAC7D,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,UAAU,MAAM,KAAK;AAC3B,WAAO,QAAQ,SAAS,UAAU;AAAA,EACpC;AACA,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,SAAS;AACf,QAAM,YAAY,OAAO,SAAS,OAAO,QAAQ,OAAO,MAAM,OAAO,OAAO,OAAO;AACnF,MAAI,OAAO,cAAc,UAAU;AACjC,UAAM,UAAU,UAAU,KAAK;AAC/B,WAAO,QAAQ,SAAS,UAAU;AAAA,EACpC;AACA,SAAO;AACT;AAEA,SAAS,yBAAyB,QAA6C,SAA0B;AACvG,MAAI,CAAC,UAAU,CAAC,QAAS,QAAO;AAChC,QAAM,aAAa,QAAQ,WAAW,KAAK,IACvC,CAAC,SAAS,MAAM,QAAQ,MAAM,CAAC,CAAC,IAAI,QAAQ,MAAM,CAAC,CAAC,IACpD,CAAC,SAAS,MAAM,OAAO,IAAI,MAAM,OAAO,EAAE;AAE9C,aAAW,aAAa,YAAY;AAClC,QAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,SAAS,GAAG;AAC3D,aAAO,OAAO,SAAS;AAAA,IACzB;AAAA,EACF;AACA,SAAO;AACT;AA0BA,SAAS,iBACP,OACA,OACA,YACA,eACA,gBAA+B,CAAC,GAChC,kBACiB;AACjB,MAAI,eAAe;AACjB,QAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD,aAAO,oBAAC,UAAK,WAAU,yBAAyB,sBAAW;AAAA,IAC7D;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,YAAM,mBAAmB,MACtB,IAAI,CAAC,UAAU,uBAAuB,KAAK,CAAC,EAC5C,OAAO,CAAC,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AAEnF,UAAI,CAAC,iBAAiB,QAAQ;AAC5B,eAAO,oBAAC,UAAK,WAAU,yBAAyB,sBAAW;AAAA,MAC7D;AAEA,aACE,oBAAC,SAAI,WAAU,0BACZ,2BAAiB,IAAI,CAAC,OAAO,UAC5B;AAAA,QAAC;AAAA;AAAA,UAEC,OAAO;AAAA,UACP,KAAK;AAAA,UACL,WAAU;AAAA,UACV,sBAAqB;AAAA,UACrB,eAAc;AAAA,UACd,gBAAe;AAAA;AAAA,QANV,GAAG,MAAM,EAAE,IAAI,KAAK,IAAI,KAAK;AAAA,MAOpC,CACD,GACH;AAAA,IAEJ;AAEA,UAAMA,YAAW,uBAAuB,KAAK;AAC7C,QAAI,CAACA,WAAU;AACb,aAAO,oBAAC,UAAK,WAAU,yBAAyB,sBAAW;AAAA,IAC7D;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAOA;AAAA,QACP,KAAK;AAAA,QACL,WAAU;AAAA,QACV,UAAU,oBAAC,UAAK,WAAU,yBAAyB,sBAAW;AAAA,QAC9D,sBAAqB;AAAA,QACrB,eAAc;AAAA,QACd,gBAAe;AAAA;AAAA,IACjB;AAAA,EAEJ;AAEA,QAAM,YACJ,aAAa,SAAS,MAAM,QAAQ,MAAM,OAAO,IAC7C,MAAM,QAAQ,OAA4B,CAAC,KAAK,WAAW;AACzD,QAAI,IAAI,OAAO,OAAO,OAAO,KAAK;AAClC,WAAO;AAAA,EACT,GAAG,oBAAI,IAAI,CAAC,IACZ;AAEN,QAAM,uBAAuB,CAAC,UAAgD;AAC5E,UAAM,YAAY,uBAAuB,KAAK;AAC9C,QAAI,aAAa,mBAAmB,SAAS,GAAG;AAC9C,aAAO,iBAAiB,SAAS;AAAA,IACnC;AACA,UAAM,cAAc,yBAAyB,KAAK;AAClD,QAAI,WAAW;AACb,aAAO;AAAA,QACL,OAAO,eAAe,WAAW,IAAI,SAAS,KAAK;AAAA,MACrD;AAAA,IACF;AACA,QAAI,aAAa;AACf,aAAO,EAAE,OAAO,YAAY;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AAEA,QAAM,wBAAwB,CAAC,YAAkC;AAC/D,QAAI,CAAC,QAAQ,KAAM,QAAO,QAAQ;AAClC,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,QAAQ;AAAA,QACd,WAAU;AAAA,QACV,SAAS,CAAC,UAAU,MAAM,gBAAgB;AAAA,QAC1C,WAAW,CAAC,UAAU,MAAM,gBAAgB;AAAA,QAE3C,kBAAQ;AAAA;AAAA,IACX;AAAA,EAEJ;AAEA,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD,WAAO,oBAAC,UAAK,WAAU,yBAAyB,sBAAW;AAAA,EAC7D;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,QAAI,CAAC,MAAM,OAAQ,QAAO,oBAAC,UAAK,WAAU,yBAAyB,sBAAW;AAC9E,WAAO,MAAM,IAAI,CAAC,OAAO,UACvB;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QAER,iBAAM;AACN,gBAAM,UAAU,qBAAqB,KAAK;AAC1C,cAAI,CAAC,QAAS,QAAO;AACrB,iBAAO,sBAAsB,OAAO;AAAA,QACtC,GAAG;AAAA;AAAA,MAPE,GAAG,MAAM,EAAE,IAAI,KAAK;AAAA,IAQ3B,CACD;AAAA,EACH;AAEA,MAAI,OAAO,UAAU,WAAW;AAC9B,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAEA,MAAI,oBAAoB,OAAO,KAAK,gBAAgB,EAAE,SAAS,GAAG;AAChE,UAAM,kBAAkB,qBAAqB,KAAK;AAClD,QAAI,iBAAiB;AACnB,aAAO,sBAAsB,eAAe;AAAA,IAC9C;AAAA,EACF;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI;AACF,aAAO,KAAK,UAAU,KAAK;AAAA,IAC7B,QAAQ;AACN,aAAO,OAAO,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,WAAW,WAAW,IAAI,OAAO,KAAK,CAAC,KAAK,OAAO,KAAK;AAC9D,MAAI,OAAO,UAAU,YAAY,qBAAqB,IAAI,MAAM,IAAI,GAAG;AACrE,QAAI,CAAC,SAAS,KAAK,EAAE,QAAQ;AAC3B,aAAO,oBAAC,UAAK,WAAU,yBAAyB,sBAAW;AAAA,IAC7D;AACA,WAAO,oBAAoB,OAAO,aAAa;AAAA,EACjD;AACA,MAAI,CAAC,SAAS,OAAQ,QAAO,oBAAC,UAAK,WAAU,yBAAyB,sBAAW;AACjF,SAAO;AACT;AAEA,SAAS,sBAAsB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,gBAAgB;AAClB,GAA2B;AACzB,QAAM,cAAc,eAAe;AACnC,QAAM,sBAAsB,4BAA4B;AACxD,QAAM,eAAe,oBAAoB;AACzC,QAAM,uBAAuB,MAAM;AAAA,IACjC,MAAO,OAAO,iBAAiB,WAAW,eAAe,OAAO,YAAY,KAAK;AAAA,IACjF,CAAC,YAAY;AAAA,EACf;AACA,QAAM,CAAC,uBAAuB,wBAAwB,IAAI,MAAM,SAAwC,CAAC,CAAC;AAC1G,QAAM,CAAC,yBAAyB,0BAA0B,IAAI,MAAM,SAA+D,CAAC,CAAC;AACrI,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,aAAa,MAAM,OAA8B,IAAI;AAC3D,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAwB,CAAC,CAAC;AAC9E,QAAM,UAAU,MAAM;AACpB,QAAI,UAAW;AACf,QAAI,UAAU;AACd,SAAK,oBAAoB,EAAE,KAAK,CAAC,YAAY;AAC3C,UAAI,CAAC,QAAS;AACd,yBAAmB,OAAO;AAAA,IAC5B,CAAC;AACD,WAAO,MAAM;AACX,gBAAU;AAAA,IACZ;AAAA,EACF,GAAG,CAAC,CAAC;AACL,QAAM,oBAAoB,MAAM,QAAQ,MAAM;AAC5C,QAAI,MAAM,QAAQ,SAAS,KAAK,UAAU,QAAQ;AAChD,YAAM,QAAQ,oBAAI,IAAY;AAC9B,YAAM,OAAiB,CAAC;AACxB,gBAAU,QAAQ,CAAC,OAAO;AACxB,cAAM,UAAU,OAAO,OAAO,WAAW,GAAG,KAAK,IAAI;AACrD,YAAI,CAAC,WAAW,MAAM,IAAI,OAAO,EAAG;AACpC,cAAM,IAAI,OAAO;AACjB,aAAK,KAAK,OAAO;AAAA,MACnB,CAAC;AACD,aAAO;AAAA,IACT;AACA,QAAI,OAAO,aAAa,YAAY,SAAS,KAAK,EAAE,SAAS,GAAG;AAC9D,aAAO,CAAC,SAAS,KAAK,CAAC;AAAA,IACzB;AACA,WAAO,CAAC;AAAA,EACV,GAAG,CAAC,UAAU,SAAS,CAAC;AACxB,QAAM,kBAAkB,kBAAkB,SAAS,kBAAkB,CAAC,IAAI;AAC1E,QAAM,wBAAwB,SAAS;AAAA,IACrC,UAAU,CAAC,oBAAoB,sBAAsB,GAAG,iBAAiB;AAAA,IACzE,SAAS,kBAAkB,SAAS;AAAA,IACpC,WAAW,IAAI,KAAK;AAAA,IACpB,QAAQ,KAAK,KAAK;AAAA,IAClB,SAAS,YAAY;AACnB,YAAM,SAAS,cAAc;AAC7B,aAAO,OAAO,iBAAiB;AAAA,IACjC;AAAA,EACF,CAAC;AACD,QAAM,SAAS,MAAM,QAAQ,MAAM,sBAAsB,MAAM,UAAU,CAAC,GAAG,CAAC,sBAAsB,IAAI,CAAC;AACzG,QAAM,cAAc,MAAM;AAAA,IACxB,MAAM,sBAAsB,MAAM,eAAe,CAAC;AAAA,IAClD,CAAC,sBAAsB,IAAI;AAAA,EAC7B;AACA,QAAM,oBAAoB,MAAM,QAAQ,MAAM;AAC5C,UAAM,OAAgC,EAAE,GAAI,UAAU,CAAC,EAAG;AAC1D,WAAO,QAAQ,CAAC,UAAU;AACxB,UAAI,CAAC,MAAM,MAAM,OAAO,UAAU,eAAe,KAAK,MAAM,MAAM,EAAE,EAAG;AACvE,YAAM,WAAW,yBAAyB,QAAQ,MAAM,EAAE;AAC1D,UAAI,aAAa,OAAW,MAAK,MAAM,EAAE,IAAI;AAAA,IAC/C,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,MAAM,CAAC;AACnB,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAS,KAAK;AACtE,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,KAAK;AAClE,QAAM,UAAU,sBAAsB,aAAa,qBAAqB;AACxE,QAAM,YAAY,OAAO,SAAS;AAClC,QAAM,iBAAiB,2BAA2B,kBAC9C,4BAA4B,mBAAmB,eAAe,CAAC,KAC/D;AAEJ,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,aAAa,SAAS;AACzB,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,SAAS,SAAS,CAAC;AAEvB,QAAM,mBAAmB,MAAM,YAAY,MAAM;AAC/C,UAAM,OAAO,WAAW,SAAS,cAAc,MAAM;AACrD,QAAI,CAAC,KAAM;AACX,UAAM,OAAO;AACb,QAAI,OAAO,KAAK,kBAAkB,YAAY;AAC5C,WAAK,cAAc;AACnB;AAAA,IACF;AACA,SAAK,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,MAAM,YAAY,KAAK,CAAC,CAAC;AAAA,EAC7E,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAuB,MAAM;AAAA,IACjC,CAAC,UAA+C;AAC9C,UAAI,CAAC,QAAS;AACd,UAAI,MAAM,QAAQ,UAAU;AAC1B,cAAM,eAAe;AACrB,mBAAW,KAAK;AAChB;AAAA,MACF;AACA,UAAI,MAAM,QAAQ,YAAY,MAAM,WAAW,MAAM,UAAU;AAC7D,cAAM,eAAe;AACrB,yBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,IACA,CAAC,SAAS,gBAAgB;AAAA,EAC5B;AAEA,QAAM,iBAAiB,MAAM,YAAY,MAAM;AAC7C,QAAI,WAAW,WAAW,CAAC,UAAW;AACtC,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,SAAS,WAAW,OAAO,CAAC;AAEhC,QAAM,wBAAwB,MAAM;AAAA,IAClC,CAAC,UAA+C;AAC9C,UAAI,WAAW,WAAW,CAAC,UAAW;AACtC,UAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,cAAM,eAAe;AACrB,mBAAW,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,IACA,CAAC,SAAS,WAAW,OAAO;AAAA,EAC9B;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,kBAAkB,UAAU,CAAC,YAAY,QAAQ;AACpD,2BAAqB,CAAC,SAAU,OAAO,QAAQ,IAAK;AACpD,+BAAyB,CAAC,SAAU,OAAO,KAAK,IAAI,EAAE,SAAS,CAAC,IAAI,IAAK;AACzE;AAAA,IACF;AAEA,QAAI,YAAY;AAChB,UAAM,OAAO,YAAY;AACvB,2BAAqB,IAAI;AACzB,UAAI;AACF,cAAM,iBAAiB,YACpB,IAAI,CAAC,QAAQ;AACZ,gBAAM,QAAQ,OAAO,IAAI,iBAAiB,WAAW,IAAI,aAAa,KAAK,IAAI;AAC/E,cAAI,CAAC,MAAO,QAAO;AACnB,iBAAO,EAAE,UAAU,IAAI,IAAI,YAAY,GAAG,cAAc,MAAM;AAAA,QAChE,CAAC,EACA,OAAO,CAAC,UAA+D,CAAC,CAAC,KAAK;AAEjF,YAAI,CAAC,eAAe,QAAQ;AAC1B,cAAI,CAAC,WAAW;AACd,qCAAyB,CAAC,SAAU,OAAO,KAAK,IAAI,EAAE,SAAS,CAAC,IAAI,IAAK;AAAA,UAC3E;AACA;AAAA,QACF;AAEA,cAAM,sBAAsB,MAAM,KAAK,IAAI,IAAI,eAAe,IAAI,CAAC,UAAU,MAAM,YAAY,CAAC,CAAC;AACjG,cAAM,qBAAoD,CAAC;AAE3D,cAAM,QAAQ;AAAA,UACZ,oBAAoB,IAAI,OAAO,iBAAiB;AAC9C,gBAAI;AACF,oBAAM,OAAO,MAAM,wBAAwB,aAAa,cAAc,oBAAoB;AAC1F,iCAAmB,YAAY,IAAI,KAAK;AAAA,YAC1C,QAAQ;AACN,iCAAmB,YAAY,IAAI,CAAC;AAAA,YACtC;AAAA,UACF,CAAC;AAAA,QACH;AAEA,cAAM,kBAAkB,eAAe,OAA4B,CAAC,KAAK,UAAU;AACjF,cAAI,IAAI,MAAM,UAAU,MAAM,YAAY;AAC1C,iBAAO;AAAA,QACT,GAAG,oBAAI,IAAI,CAAC;AAEZ,cAAM,WAA0C,CAAC;AACjD,eAAO,QAAQ,CAAC,UAAU;AACxB,gBAAM,KAAK,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK;AACrD,cAAI,CAAC,GAAI;AACT,gBAAM,gBAAgB,GAAG,WAAW,KAAK,IAAI,GAAG,MAAM,CAAC,IAAI;AAC3D,gBAAM,WAAW,cAAc,YAAY;AAC3C,cAAI,CAAC,SAAU;AACf,gBAAM,eAAe,gBAAgB,IAAI,QAAQ;AACjD,cAAI,CAAC,aAAc;AACnB,mBAAS,EAAE,IAAI,mBAAmB,YAAY,KAAK,CAAC;AAAA,QACtD,CAAC;AAED,YAAI,CAAC,WAAW;AACd,mCAAyB,CAAC,SAAS;AACjC,kBAAM,WAAW,OAAO,KAAK,IAAI;AACjC,kBAAM,WAAW,OAAO,KAAK,QAAQ;AACrC,gBACE,SAAS,WAAW,SAAS,UAC7B,SAAS,MAAM,CAAC,QAAQ,KAAK,GAAG,MAAM,SAAS,GAAG,CAAC,GACnD;AACA,qBAAO;AAAA,YACT;AACA,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AACN,YAAI,CAAC,WAAW;AACd,mCAAyB,CAAC,SAAU,OAAO,KAAK,IAAI,EAAE,SAAS,CAAC,IAAI,IAAK;AAAA,QAC3E;AAAA,MACF,UAAE;AACA,YAAI,CAAC,WAAW;AACd,+BAAqB,KAAK;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAEA,SAAK,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACrB,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,aAAa,QAAQ,aAAa,mBAAmB,oBAAoB,CAAC;AAE9E,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,YAAY,UAAU,CAAC,OAAO,QAAQ;AACzC,yBAAmB,CAAC,SAAU,OAAO,QAAQ,IAAK;AAClD,iCAA2B,CAAC,SAAU,OAAO,KAAK,IAAI,EAAE,SAAS,CAAC,IAAI,IAAK;AAC3E;AAAA,IACF;AAEA,UAAM,mBAAmB,YAAY,OAAuC,CAAC,KAAK,eAAe;AAC/F,UAAI,IAAI,WAAW,IAAI,YAAY,GAAG,UAAU;AAChD,aAAO;AAAA,IACT,GAAG,oBAAI,IAAI,CAAC;AAEZ,UAAM,iBAAiB,OACpB,IAAI,CAAC,UAAU;AACd,YAAM,gBAAgB,MAAM,GAAG,WAAW,KAAK,IAAI,MAAM,GAAG,MAAM,CAAC,IAAI,MAAM;AAC7E,YAAM,aAAa,iBAAiB,IAAI,cAAc,YAAY,CAAC;AACnE,UAAI,CAAC,cAAc,WAAW,SAAS,WAAY,QAAO;AAC1D,YAAM,cAAc,wBAAwB,yBAAyB,QAAQ,MAAM,EAAE,CAAC;AACtF,UAAI,CAAC,YAAY,OAAQ,QAAO;AAChC,aAAO,EAAE,OAAO,YAAY,YAAY;AAAA,IAC1C,CAAC,EACA,OAAO,CAAC,UAA+F,CAAC,CAAC,KAAK;AAEjH,QAAI,CAAC,eAAe,QAAQ;AAC1B,yBAAmB,CAAC,SAAU,OAAO,QAAQ,IAAK;AAClD,iCAA2B,CAAC,SAAU,OAAO,KAAK,IAAI,EAAE,SAAS,CAAC,IAAI,IAAK;AAC3E;AAAA,IACF;AAEA,UAAM,kBAAkB,IAAI,gBAAgB;AAE5C,UAAM,OAAO,YAAY;AACvB,yBAAmB,IAAI;AACvB,UAAI;AACF,cAAM,eAAqE,CAAC;AAE5E,cAAM,QAAQ;AAAA,UACZ,eAAe,IAAI,OAAO,EAAE,OAAO,YAAY,YAAY,MAAM;AAC/D,kBAAM,WAAiD,CAAC;AAExD,gBAAI,aAAa,SAAS,MAAM,QAAQ,MAAM,OAAO,GAAG;AACtD,oBAAM,QAAQ,QAAQ,CAAC,WAAW;AAChC,yBAAS,OAAO,KAAK,IAAI,EAAE,OAAO,OAAO,MAAM;AAAA,cACjD,CAAC;AAAA,YACH;AAEA,gBAAI,iBAAiB,SAAS,OAAO,MAAM,gBAAgB,YAAY;AACrE,kBAAI;AACF,sBAAM,gBAAgB,MAAM,MAAM,YAAY;AAC9C,8BAAc,QAAQ,CAAC,WAAW;AAChC,wBAAM,QAAQ,MAAM;AAClB,0BAAMC,YAAW,6BAA6B,WAAW,UAAU;AACnE,2BAAOA,YAAW,kBAAkBA,UAAS,UAAU,OAAO,KAAK,IAAI;AAAA,kBACzE,GAAG;AACH,2BAAS,OAAO,KAAK,IAAI,EAAE,OAAO,OAAO,OAAO,KAAK;AAAA,gBACvD,CAAC;AAAA,cACH,SAAS,OAAO;AACd,wBAAQ,MAAM,+DAA+D,MAAM,IAAI,KAAK;AAAA,cAC9F;AAAA,YACF;AAEA,kBAAM,WAAW,6BAA6B,WAAW,UAAU;AACnE,kBAAM,oBAAoB,WAAW,6BAA6B,SAAS,QAAQ,EAAE,SAAS,IAAI;AAClG,kBAAM,gBAAgB,YAAY,OAAO,CAAC,eAAe;AACvD,oBAAM,UAAU,SAAS,UAAU;AACnC,kBAAI,CAAC,QAAS,QAAO;AACrB,qBAAO,qBAAqB,CAAC,QAAQ;AAAA,YACvC,CAAC;AACD,gBAAI,YAAY,cAAc,QAAQ;AACpC,kBAAI;AACF,sBAAM,kBAAkB,MAAM,4BAA4B,WAAW,YAAa,UAAU,eAAe,gBAAgB,MAAM;AACjI,uBAAO,OAAO,UAAU,eAAe;AAAA,cACzC,SAAS,OAAO;AACd,wBAAQ,MAAM,0EAA0E,MAAM,IAAI,KAAK;AACvG,8BAAc,QAAQ,CAAC,eAAe;AACpC,sBAAI,CAAC,SAAS,UAAU,GAAG;AACzB,6BAAS,UAAU,IAAI;AAAA,sBACrB,OAAO;AAAA,sBACP,MAAM,kBAAkB,SAAS,UAAU,UAAU;AAAA,oBACvD;AAAA,kBACF;AAAA,gBACF,CAAC;AAAA,cACH;AAAA,YACF;AAEA,gBAAI,OAAO,KAAK,QAAQ,EAAE,SAAS,GAAG;AACpC,2BAAa,MAAM,EAAE,IAAI;AAAA,YAC3B;AAAA,UACF,CAAC;AAAA,QACH;AAEA,YAAI,CAAC,gBAAgB,OAAO,SAAS;AACnC,qCAA2B,CAAC,SAAS;AACnC,kBAAM,eAAe,OAAO,KAAK,IAAI;AACrC,kBAAM,WAAW,OAAO,KAAK,YAAY;AACzC,gBACE,aAAa,WAAW,SAAS,UACjC,aAAa,MAAM,CAAC,QAAQ,KAAK,UAAU,KAAK,GAAG,CAAC,MAAM,KAAK,UAAU,aAAa,GAAG,CAAC,CAAC,GAC3F;AACA,qBAAO;AAAA,YACT;AACA,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF,UAAE;AACA,YAAI,CAAC,gBAAgB,OAAO,SAAS;AACnC,6BAAmB,KAAK;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAEA,SAAK,KAAK;AACV,WAAO,MAAM;AACX,sBAAgB,MAAM;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,aAAa,QAAQ,MAAM,CAAC;AAEhC,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO,UAAmC;AACxC,YAAM,SAAS,KAAK;AACpB,iBAAW,KAAK;AAAA,IAClB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,2CACb;AAAA,0BAAC,QAAG,WAAU,yBAAyB,iBAAM;AAAA,MAC7C;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,SAAS,MAAM;AACb,gBAAI,CAAC,aAAa,QAAS;AAC3B,uBAAW,CAAC,SAAS,CAAC,IAAI;AAAA,UAC5B;AAAA,UACA,UAAU,WAAW,CAAC;AAAA,UACtB,WACE,UACI,gDACA;AAAA,UAGL;AAAA,sBAAU,oBAAC,KAAE,WAAU,WAAU,IAAK,oBAAC,UAAO,WAAU,WAAU;AAAA,YACnE,oBAAC,UAAK,WAAU,WAAW,oBAAU,OAAO,UAAU,WAAW,OAAO,QAAQ,QAAO;AAAA;AAAA;AAAA,MACzF;AAAA,OACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,aAAY;AAAA,QACZ,WAAU;AAAA,QAET,oBACC;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,WAAU;AAAA,YACV,WAAW;AAAA,YAEX;AAAA,cAAC;AAAA;AAAA,gBACC,UAAQ;AAAA,gBACR,UAAU;AAAA,gBACV,WAAW;AAAA,gBACX;AAAA,gBACA,eAAe;AAAA,gBACf,UAAU;AAAA,gBACV,aAAa,OAAO;AAAA,gBACpB,WAAW;AAAA;AAAA,YACb;AAAA;AAAA,QACF,IAEA;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,aAAa,CAAC,UAAU,mBAAmB;AAAA,YAC7C;AAAA,YACA,MAAM,aAAa,CAAC,UAAU,WAAW;AAAA,YACzC,UAAU,aAAa,CAAC,UAAU,IAAI;AAAA,YACtC,SAAS,aAAa,CAAC,UAAU,iBAAiB;AAAA,YAClD,WAAW,aAAa,CAAC,UAAU,wBAAwB;AAAA,YAE1D,WAAC,YACA,qBAAC,OAAE,WAAU,iCACV;AAAA,qBAAO;AAAA,cAAU;AAAA,cACjB,kBAAkB,OAAO,eACxB;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAM;AAAA,kBACN,WAAU;AAAA,kBAET,iBAAO;AAAA;AAAA,cACV,IACE;AAAA,eACN,IAEA,OAAO,IAAI,CAAC,UACV,qBAAC,SAAmB,WAAU,aAC5B;AAAA,kCAAC,OAAE,WAAU,yDACV,gBAAM,OACT;AAAA,cACA,oBAAC,SAAI,WAAU,uBACZ;AAAA,gBACC;AAAA,gBACA,yBAAyB,QAAQ,MAAM,EAAE;AAAA,gBACzC,OAAO;AAAA,gBACP,sBAAsB,MAAM,EAAE;AAAA,gBAC9B;AAAA,gBACA,wBAAwB,MAAM,EAAE;AAAA,cAClC,GACF;AAAA,iBAbQ,MAAM,EAchB,CACD;AAAA;AAAA,QAEL;AAAA;AAAA,IAEJ;AAAA,KACF;AAEJ;AAEO,SAAS,kBAAkB,OAA+B;AAC/D,QAAM,SAAS,4BAA4B,QAAQ,aAAa,mBAAmB;AACnF,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,EACF;AAEA,SACE,oBAAC,SAAI,yBAAuB,QAC1B,8BAAC,YAAU,GAAG,OAAO,GACvB;AAEJ;AAEA,IAAO,4BAAQ;",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { PluggableList } from 'unified'\nimport { Pencil, X } from 'lucide-react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { DataLoader } from '@open-mercato/ui/primitives/DataLoader'\nimport type { CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { fetchCustomFieldFormFieldsWithDefinitions } from '@open-mercato/ui/backend/utils/customFieldForms'\nimport type { CustomFieldDefDto } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport {\n DictionaryValue,\n type DictionaryMap,\n} from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { ensureDictionaryEntries } from '@open-mercato/core/modules/dictionaries/components/hooks/useDictionaryEntries'\nimport {\n type ResolvedValueDisplay,\n collectRelationValueIds,\n extractOptionLookupKey,\n extractInlineOptionLabel,\n parseRelationOptionsMetadata,\n getRelationHrefContextFields,\n buildRelationHref,\n fetchRelationRecordDisplays,\n} from '@open-mercato/ui/backend/utils/customFieldRelationDisplay'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { MarkdownPreview } from '../markdown'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui').child({ component: 'CustomDataSection' })\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nlet markdownPluginsPromise: Promise<PluggableList> | null = null\n\nasync function loadMarkdownPlugins(): Promise<PluggableList> {\n if (isTestEnv) return []\n if (!markdownPluginsPromise) {\n markdownPluginsPromise = import('remark-gfm')\n .then((mod) => [mod.default ?? mod] as PluggableList)\n .catch(() => [])\n }\n return markdownPluginsPromise\n}\n\nconst MARKDOWN_FIELD_TYPES = new Set<CrudField['type']>(['text', 'textarea', 'richtext'])\nconst MARKDOWN_CLASSNAME =\n 'text-sm text-foreground break-words [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs'\n\nfunction renderMarkdownValue(content: string, remarkPlugins: PluggableList) {\n return (\n <MarkdownPreview remarkPlugins={remarkPlugins} className={MARKDOWN_CLASSNAME}>\n {content}\n </MarkdownPreview>\n )\n}\n\nfunction extractDictionaryValue(entry: unknown): string | null {\n if (typeof entry === 'string') {\n const trimmed = entry.trim()\n return trimmed.length ? trimmed : null\n }\n if (!entry || typeof entry !== 'object') return null\n const record = entry as Record<string, unknown>\n const candidate = record.value ?? record.name ?? record.id ?? record.key ?? record.label\n if (typeof candidate === 'string') {\n const trimmed = candidate.trim()\n return trimmed.length ? trimmed : null\n }\n return null\n}\n\nfunction readCustomDataFieldValue(values: Record<string, unknown> | undefined, fieldId: string): unknown {\n if (!values || !fieldId) return undefined\n const candidates = fieldId.startsWith('cf_')\n ? [fieldId, `cf:${fieldId.slice(3)}`, fieldId.slice(3)]\n : [fieldId, `cf_${fieldId}`, `cf:${fieldId}`]\n\n for (const candidate of candidates) {\n if (Object.prototype.hasOwnProperty.call(values, candidate)) {\n return values[candidate]\n }\n }\n return undefined\n}\n\nexport type CustomDataLabels = {\n loading: string\n emptyValue: string\n noFields: string\n defineFields?: string\n saveShortcut: string\n edit?: string\n cancel?: string\n}\n\nexport type CustomDataSectionProps = {\n entityId?: string\n entityIds?: string[]\n values: Record<string, unknown>\n onSubmit: (values: Record<string, unknown>) => Promise<void>\n title: string\n scopeVersion?: string | number | null\n loadFields?: (\n entityIds: string[],\n ) => Promise<{ fields: CrudField[]; definitions: CustomFieldDefDto[] }>\n labels: CustomDataLabels\n definitionHref?: string\n}\n\nfunction formatFieldValue(\n field: CrudField,\n value: unknown,\n emptyLabel: string,\n dictionaryMap?: DictionaryMap,\n remarkPlugins: PluggableList = [],\n resolvedDisplays?: Record<string, ResolvedValueDisplay>,\n): React.ReactNode {\n if (dictionaryMap) {\n if (value === undefined || value === null || value === '') {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n\n if (Array.isArray(value)) {\n const normalizedValues = value\n .map((entry) => extractDictionaryValue(entry))\n .filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)\n\n if (!normalizedValues.length) {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {normalizedValues.map((entry, index) => (\n <DictionaryValue\n key={`${field.id}-${entry}-${index}`}\n value={entry}\n map={dictionaryMap}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-card px-2 py-1 text-xs\"\n iconWrapperClassName=\"inline-flex h-4 w-4 items-center justify-center rounded-full border border-border bg-background\"\n iconClassName=\"h-3 w-3\"\n colorClassName=\"h-2.5 w-2.5 rounded-full\"\n />\n ))}\n </div>\n )\n }\n\n const resolved = extractDictionaryValue(value)\n if (!resolved) {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n\n return (\n <DictionaryValue\n value={resolved}\n map={dictionaryMap}\n className=\"inline-flex items-center gap-2 text-sm\"\n fallback={<span className=\"text-muted-foreground\">{emptyLabel}</span>}\n iconWrapperClassName=\"inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-card\"\n iconClassName=\"h-4 w-4\"\n colorClassName=\"h-3 w-3 rounded-full\"\n />\n )\n }\n\n const optionMap =\n 'options' in field && Array.isArray(field.options)\n ? field.options.reduce<Map<string, string>>((acc, option) => {\n acc.set(option.value, option.label)\n return acc\n }, new Map())\n : null\n\n const resolveOptionDisplay = (entry: unknown): ResolvedValueDisplay | null => {\n const lookupKey = extractOptionLookupKey(entry)\n if (lookupKey && resolvedDisplays?.[lookupKey]) {\n return resolvedDisplays[lookupKey]\n }\n const inlineLabel = extractInlineOptionLabel(entry)\n if (lookupKey) {\n return {\n label: inlineLabel ?? optionMap?.get(lookupKey) ?? lookupKey,\n }\n }\n if (inlineLabel) {\n return { label: inlineLabel }\n }\n return null\n }\n\n const renderResolvedDisplay = (display: ResolvedValueDisplay) => {\n if (!display.href) return display.label\n return (\n <Link\n href={display.href}\n className=\"font-medium text-primary underline-offset-2 hover:underline focus-visible:underline\"\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => event.stopPropagation()}\n >\n {display.label}\n </Link>\n )\n }\n\n if (value === undefined || value === null || value === '') {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n\n if (Array.isArray(value)) {\n if (!value.length) return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n return value.map((entry, index) => (\n <span\n key={`${field.id}-${index}`}\n className=\"mr-1 inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-xs\"\n >\n {(() => {\n const display = resolveOptionDisplay(entry)\n if (!display) return emptyLabel\n return renderResolvedDisplay(display)\n })()}\n </span>\n ))\n }\n\n if (typeof value === 'boolean') {\n return value ? 'Yes' : 'No'\n }\n\n if (resolvedDisplays && Object.keys(resolvedDisplays).length > 0) {\n const resolvedDisplay = resolveOptionDisplay(value)\n if (resolvedDisplay) {\n return renderResolvedDisplay(resolvedDisplay)\n }\n }\n\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n }\n\n const resolved = optionMap?.get(String(value)) ?? String(value)\n if (typeof value === 'string' && MARKDOWN_FIELD_TYPES.has(field.type)) {\n if (!resolved.trim().length) {\n return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n }\n return renderMarkdownValue(value, remarkPlugins)\n }\n if (!resolved.length) return <span className=\"text-muted-foreground\">{emptyLabel}</span>\n return resolved\n}\n\nfunction CustomDataSectionImpl({\n entityId,\n entityIds,\n values,\n onSubmit,\n title,\n scopeVersion: scopeVersionProp,\n loadFields,\n labels,\n definitionHref: explicitDefinitionHref,\n}: CustomDataSectionProps) {\n const queryClient = useQueryClient()\n const defaultScopeVersion = useOrganizationScopeVersion()\n const scopeVersion = scopeVersionProp ?? defaultScopeVersion\n const resolvedScopeVersion = React.useMemo(\n () => (typeof scopeVersion === 'number' ? scopeVersion : Number(scopeVersion) || 0),\n [scopeVersion],\n )\n const [dictionaryMapsByField, setDictionaryMapsByField] = React.useState<Record<string, DictionaryMap>>({})\n const [resolvedDisplaysByField, setResolvedDisplaysByField] = React.useState<Record<string, Record<string, ResolvedValueDisplay>>>({})\n const [editing, setEditing] = React.useState(false)\n const sectionRef = React.useRef<HTMLDivElement | null>(null)\n const [markdownPlugins, setMarkdownPlugins] = React.useState<PluggableList>([])\n React.useEffect(() => {\n if (isTestEnv) return\n let mounted = true\n void loadMarkdownPlugins().then((plugins) => {\n if (!mounted) return\n setMarkdownPlugins(plugins)\n })\n return () => {\n mounted = false\n }\n }, [])\n const resolvedEntityIds = React.useMemo(() => {\n if (Array.isArray(entityIds) && entityIds.length) {\n const dedup = new Set<string>()\n const list: string[] = []\n entityIds.forEach((id) => {\n const trimmed = typeof id === 'string' ? id.trim() : ''\n if (!trimmed || dedup.has(trimmed)) return\n dedup.add(trimmed)\n list.push(trimmed)\n })\n return list\n }\n if (typeof entityId === 'string' && entityId.trim().length > 0) {\n return [entityId.trim()]\n }\n return []\n }, [entityId, entityIds])\n const primaryEntityId = resolvedEntityIds.length ? resolvedEntityIds[0] : undefined\n const customFieldFormsQuery = useQuery({\n queryKey: ['customFieldForms', resolvedScopeVersion, ...resolvedEntityIds],\n enabled: resolvedEntityIds.length > 0,\n staleTime: 5 * 60 * 1000,\n gcTime: 30 * 60 * 1000,\n queryFn: async () => {\n const loader = loadFields ?? fetchCustomFieldFormFieldsWithDefinitions\n return loader(resolvedEntityIds)\n },\n })\n const fields = React.useMemo(() => customFieldFormsQuery.data?.fields ?? [], [customFieldFormsQuery.data])\n const definitions = React.useMemo(\n () => customFieldFormsQuery.data?.definitions ?? [],\n [customFieldFormsQuery.data],\n )\n const formInitialValues = React.useMemo(() => {\n const next: Record<string, unknown> = { ...(values ?? {}) }\n fields.forEach((field) => {\n if (!field.id || Object.prototype.hasOwnProperty.call(next, field.id)) return\n const resolved = readCustomDataFieldValue(values, field.id)\n if (resolved !== undefined) next[field.id] = resolved\n })\n return next\n }, [fields, values])\n const [dictionaryLoading, setDictionaryLoading] = React.useState(false)\n const [relationLoading, setRelationLoading] = React.useState(false)\n const loading = customFieldFormsQuery.isLoading || dictionaryLoading || relationLoading\n const hasFields = fields.length > 0\n const definitionHref = explicitDefinitionHref ?? (primaryEntityId\n ? `/backend/entities/system/${encodeURIComponent(primaryEntityId)}`\n : undefined)\n\n React.useEffect(() => {\n if (!hasFields && editing) {\n setEditing(false)\n }\n }, [editing, hasFields])\n\n const submitActiveForm = React.useCallback(() => {\n const node = sectionRef.current?.querySelector('form')\n if (!node) return\n const form = node as HTMLFormElement\n if (typeof form.requestSubmit === 'function') {\n form.requestSubmit()\n return\n }\n form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }))\n }, [])\n\n const handleEditingKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (!editing) return\n if (event.key === 'Escape') {\n event.preventDefault()\n setEditing(false)\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n submitActiveForm()\n }\n },\n [editing, submitActiveForm],\n )\n\n const handleActivate = React.useCallback(() => {\n if (loading || editing || !hasFields) return\n setEditing(true)\n }, [editing, hasFields, loading])\n\n const handleReadOnlyKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (loading || editing || !hasFields) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setEditing(true)\n }\n },\n [editing, hasFields, loading],\n )\n\n React.useEffect(() => {\n if (!resolvedEntityIds.length || !definitions.length) {\n setDictionaryLoading((prev) => (prev ? false : prev))\n setDictionaryMapsByField((prev) => (Object.keys(prev).length ? {} : prev))\n return\n }\n\n let cancelled = false\n const load = async () => {\n setDictionaryLoading(true)\n try {\n const dictionaryDefs = definitions\n .map((def) => {\n const rawId = typeof def.dictionaryId === 'string' ? def.dictionaryId.trim() : ''\n if (!rawId) return null\n return { keyLower: def.key.toLowerCase(), dictionaryId: rawId }\n })\n .filter((entry): entry is { keyLower: string; dictionaryId: string } => !!entry)\n\n if (!dictionaryDefs.length) {\n if (!cancelled) {\n setDictionaryMapsByField((prev) => (Object.keys(prev).length ? {} : prev))\n }\n return\n }\n\n const uniqueDictionaryIds = Array.from(new Set(dictionaryDefs.map((entry) => entry.dictionaryId)))\n const mapsByDictionaryId: Record<string, DictionaryMap> = {}\n\n await Promise.all(\n uniqueDictionaryIds.map(async (dictionaryId) => {\n try {\n const data = await ensureDictionaryEntries(queryClient, dictionaryId, resolvedScopeVersion)\n mapsByDictionaryId[dictionaryId] = data.map\n } catch {\n mapsByDictionaryId[dictionaryId] = {}\n }\n }),\n )\n\n const dictionaryByKey = dictionaryDefs.reduce<Map<string, string>>((acc, entry) => {\n acc.set(entry.keyLower, entry.dictionaryId)\n return acc\n }, new Map())\n\n const nextMaps: Record<string, DictionaryMap> = {}\n fields.forEach((field) => {\n const id = typeof field.id === 'string' ? field.id : ''\n if (!id) return\n const normalizedKey = id.startsWith('cf_') ? id.slice(3) : id\n const keyLower = normalizedKey.toLowerCase()\n if (!keyLower) return\n const dictionaryId = dictionaryByKey.get(keyLower)\n if (!dictionaryId) return\n nextMaps[id] = mapsByDictionaryId[dictionaryId] ?? {}\n })\n\n if (!cancelled) {\n setDictionaryMapsByField((prev) => {\n const prevKeys = Object.keys(prev)\n const nextKeys = Object.keys(nextMaps)\n if (\n prevKeys.length === nextKeys.length &&\n prevKeys.every((key) => prev[key] === nextMaps[key])\n ) {\n return prev\n }\n return nextMaps\n })\n }\n } catch {\n if (!cancelled) {\n setDictionaryMapsByField((prev) => (Object.keys(prev).length ? {} : prev))\n }\n } finally {\n if (!cancelled) {\n setDictionaryLoading(false)\n }\n }\n }\n\n load().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [definitions, fields, queryClient, resolvedEntityIds, resolvedScopeVersion])\n\n React.useEffect(() => {\n if (!definitions.length || !fields.length) {\n setRelationLoading((prev) => (prev ? false : prev))\n setResolvedDisplaysByField((prev) => (Object.keys(prev).length ? {} : prev))\n return\n }\n\n const definitionsByKey = definitions.reduce<Map<string, CustomFieldDefDto>>((acc, definition) => {\n acc.set(definition.key.toLowerCase(), definition)\n return acc\n }, new Map())\n\n const relationFields = fields\n .map((field) => {\n const normalizedKey = field.id.startsWith('cf_') ? field.id.slice(3) : field.id\n const definition = definitionsByKey.get(normalizedKey.toLowerCase())\n if (!definition || definition.kind !== 'relation') return null\n const relationIds = collectRelationValueIds(readCustomDataFieldValue(values, field.id))\n if (!relationIds.length) return null\n return { field, definition, relationIds }\n })\n .filter((entry): entry is { field: CrudField; definition: CustomFieldDefDto; relationIds: string[] } => !!entry)\n\n if (!relationFields.length) {\n setRelationLoading((prev) => (prev ? false : prev))\n setResolvedDisplaysByField((prev) => (Object.keys(prev).length ? {} : prev))\n return\n }\n\n const abortController = new AbortController()\n\n const load = async () => {\n setRelationLoading(true)\n try {\n const nextDisplays: Record<string, Record<string, ResolvedValueDisplay>> = {}\n\n await Promise.all(\n relationFields.map(async ({ field, definition, relationIds }) => {\n const displays: Record<string, ResolvedValueDisplay> = {}\n\n if ('options' in field && Array.isArray(field.options)) {\n field.options.forEach((option) => {\n displays[option.value] = { label: option.label }\n })\n }\n\n if ('loadOptions' in field && typeof field.loadOptions === 'function') {\n try {\n const remoteOptions = await field.loadOptions()\n remoteOptions.forEach((option) => {\n const href = (() => {\n const relation = parseRelationOptionsMetadata(definition.optionsUrl)\n return relation ? buildRelationHref(relation.entityId, option.value) : undefined\n })()\n displays[option.value] = { label: option.label, href }\n })\n } catch (error) {\n logger.debug('Failed to load remote options for field', { fieldId: field.id, err: error })\n }\n }\n\n const relation = parseRelationOptionsMetadata(definition.optionsUrl)\n const needsRouteContext = relation ? getRelationHrefContextFields(relation.entityId).length > 0 : false\n const unresolvedIds = relationIds.filter((relationId) => {\n const display = displays[relationId]\n if (!display) return true\n return needsRouteContext && !display.href\n })\n if (relation && unresolvedIds.length) {\n try {\n const fetchedDisplays = await fetchRelationRecordDisplays(definition.optionsUrl!, relation, unresolvedIds, abortController.signal)\n Object.assign(displays, fetchedDisplays)\n } catch (error) {\n logger.debug('Failed to fetch relation record displays for field', { fieldId: field.id, err: error })\n unresolvedIds.forEach((relationId) => {\n if (!displays[relationId]) {\n displays[relationId] = {\n label: relationId,\n href: buildRelationHref(relation.entityId, relationId),\n }\n }\n })\n }\n }\n\n if (Object.keys(displays).length > 0) {\n nextDisplays[field.id] = displays\n }\n }),\n )\n\n if (!abortController.signal.aborted) {\n setResolvedDisplaysByField((prev) => {\n const previousKeys = Object.keys(prev)\n const nextKeys = Object.keys(nextDisplays)\n if (\n previousKeys.length === nextKeys.length &&\n previousKeys.every((key) => JSON.stringify(prev[key]) === JSON.stringify(nextDisplays[key]))\n ) {\n return prev\n }\n return nextDisplays\n })\n }\n } finally {\n if (!abortController.signal.aborted) {\n setRelationLoading(false)\n }\n }\n }\n\n void load()\n return () => {\n abortController.abort()\n }\n }, [definitions, fields, values])\n\n const handleSubmit = React.useCallback(\n async (input: Record<string, unknown>) => {\n await onSubmit(input)\n setEditing(false)\n },\n [onSubmit],\n )\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between group\">\n <h2 className=\"text-sm font-semibold\">{title}</h2>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => {\n if (!hasFields || loading) return\n setEditing((prev) => !prev)\n }}\n disabled={loading || !hasFields}\n className={\n editing\n ? 'opacity-100 transition-opacity duration-150'\n : 'opacity-100 md:opacity-0 transition-opacity duration-150 md:group-hover:opacity-100 focus-visible:opacity-100'\n }\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n <span className=\"sr-only\">{editing ? labels.cancel ?? 'Cancel' : labels.edit ?? 'Edit'}</span>\n </Button>\n </div>\n <DataLoader\n isLoading={loading}\n loadingMessage={labels.loading}\n spinnerSize=\"md\"\n className=\"min-h-[120px]\"\n >\n {editing ? (\n <div\n ref={sectionRef}\n className=\"rounded-lg border bg-card p-3 sm:p-4\"\n onKeyDown={handleEditingKeyDown}\n >\n <CrudForm<Record<string, unknown>>\n embedded\n entityId={primaryEntityId}\n entityIds={resolvedEntityIds}\n fields={fields}\n initialValues={formInitialValues}\n onSubmit={handleSubmit}\n submitLabel={labels.saveShortcut}\n isLoading={loading}\n />\n </div>\n ) : (\n <div\n className={cn(\n 'rounded-lg border bg-muted/30 p-3 sm:p-4 space-y-2 sm:space-y-3 transition hover:border-border/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n hasFields && !loading ? 'cursor-pointer' : 'cursor-default',\n )}\n role={hasFields && !loading ? 'button' : undefined}\n tabIndex={hasFields && !loading ? 0 : -1}\n onClick={hasFields && !loading ? handleActivate : undefined}\n onKeyDown={hasFields && !loading ? handleReadOnlyKeyDown : undefined}\n >\n {!hasFields ? (\n <p className=\"text-sm text-muted-foreground\">\n {labels.noFields}{' '}\n {definitionHref && labels.defineFields ? (\n <Link\n href={definitionHref}\n className=\"font-medium text-primary underline-offset-2 hover:underline focus-visible:underline\"\n >\n {labels.defineFields}\n </Link>\n ) : null}\n </p>\n ) : (\n fields.map((field) => (\n <div key={field.id} className=\"space-y-1\">\n <p className=\"text-xs uppercase tracking-wide text-muted-foreground\">\n {field.label}\n </p>\n <div className=\"text-sm break-words\">\n {formatFieldValue(\n field,\n readCustomDataFieldValue(values, field.id),\n labels.emptyValue,\n dictionaryMapsByField[field.id],\n markdownPlugins,\n resolvedDisplaysByField[field.id],\n )}\n </div>\n </div>\n ))\n )}\n </div>\n )}\n </DataLoader>\n </div>\n )\n}\n\nexport function CustomDataSection(props: CustomDataSectionProps) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'CustomDataSection')\n const Resolved = useRegisteredComponent<CustomDataSectionProps>(\n handle,\n CustomDataSectionImpl as React.ComponentType<CustomDataSectionProps>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n\nexport default CustomDataSection\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["resolved", "relation"]
|
|
7
7
|
}
|
|
@@ -18,6 +18,8 @@ import { cn } from "@open-mercato/shared/lib/utils";
|
|
|
18
18
|
import { format } from "date-fns/format";
|
|
19
19
|
import { mapCrudServerErrorToFormErrors } from "../utils/serverErrors.js";
|
|
20
20
|
import { MarkdownPreview } from "../markdown/index.js";
|
|
21
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
22
|
+
const logger = createLogger("ui");
|
|
21
23
|
function resolveInlineErrorMessage(err, fallbackMessage) {
|
|
22
24
|
const { message, fieldErrors } = mapCrudServerErrorToFormErrors(err);
|
|
23
25
|
const firstFieldError = fieldErrors ? Object.values(fieldErrors).find((text) => typeof text === "string" && text.trim().length) : null;
|
|
@@ -630,7 +632,7 @@ function InlineSelectEditor({
|
|
|
630
632
|
setEditing(false);
|
|
631
633
|
} catch (err) {
|
|
632
634
|
const message = err instanceof Error ? err.message : t("ui.detail.inline.error", "Failed to save value.");
|
|
633
|
-
|
|
635
|
+
logger.error(message, { err });
|
|
634
636
|
} finally {
|
|
635
637
|
setSaving(false);
|
|
636
638
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/detail/InlineEditors.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { FileCode, Loader2, Mail, Pencil, Phone, X } from 'lucide-react'\nimport type { PluggableList } from 'unified'\nimport { PhoneNumberField } from '@open-mercato/ui/backend/inputs/PhoneNumberField'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { DatePicker } from '@open-mercato/ui/primitives/date-picker'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { format } from 'date-fns/format'\nimport { LoadingMessage } from './LoadingMessage'\nimport { mapCrudServerErrorToFormErrors } from '../utils/serverErrors'\nimport { MarkdownPreview } from '../markdown'\n\nfunction resolveInlineErrorMessage(err: unknown, fallbackMessage: string): string {\n const { message, fieldErrors } = mapCrudServerErrorToFormErrors(err)\n const firstFieldError = fieldErrors\n ? Object.values(fieldErrors).find((text) => typeof text === 'string' && text.trim().length)\n : null\n if (typeof firstFieldError === 'string' && firstFieldError.trim().length) {\n return firstFieldError.trim()\n }\n if (typeof message === 'string' && message.trim().length) {\n return message.trim()\n }\n if (err instanceof Error && typeof err.message === 'string' && err.message.trim().length) {\n return err.message.trim()\n }\n if (typeof err === 'string' && err.trim().length) {\n return err.trim()\n }\n return fallbackMessage\n}\n\ntype EditorVariant = 'default' | 'muted' | 'plain'\n\nexport type InlineFieldType = 'text' | 'email' | 'tel' | 'url'\n\nconst ALLOWED_INLINE_URL_PROTOCOLS = new Set(['http:', 'https:', 'mailto:', 'tel:'])\n\nexport function resolveSafeInlineUrlHref(value: string): string | null {\n const trimmed = value.trim()\n if (!trimmed.length) return null\n try {\n const parsed = new URL(trimmed)\n return ALLOWED_INLINE_URL_PROTOCOLS.has(parsed.protocol) ? trimmed : null\n } catch {\n return null\n }\n}\n\nexport type InlineTextEditorProps = {\n label: string\n value: string | null | undefined\n placeholder?: string\n emptyLabel: string\n onSave: (value: string | null) => Promise<void>\n type?: InlineFieldType\n inputType?: React.HTMLInputTypeAttribute\n validator?: (value: string) => string | null\n variant?: EditorVariant\n activateOnClick?: boolean\n containerClassName?: string\n triggerClassName?: string\n hideLabel?: boolean\n renderDisplay?: (params: { value: string | null | undefined; emptyLabel: string; type: InlineFieldType }) => React.ReactNode\n onEditingChange?: (editing: boolean) => void\n renderActions?: React.ReactNode\n saveLabel?: string\n recordId?: string\n onDraftChange?: (draft: string) => void\n renderBelowInput?: (params: {\n draft: string\n resolvedType: InlineFieldType\n error: string | null\n saving: boolean\n }) => React.ReactNode\n}\n\nexport function InlineTextEditor({\n label,\n value,\n placeholder,\n emptyLabel,\n onSave,\n type = 'text',\n inputType,\n validator,\n variant = 'default',\n activateOnClick = false,\n containerClassName,\n triggerClassName,\n hideLabel = false,\n renderDisplay,\n onEditingChange,\n renderActions,\n saveLabel,\n onDraftChange,\n renderBelowInput,\n}: InlineTextEditorProps) {\n const t = useT()\n const [editing, setEditing] = React.useState(false)\n const [draft, setDraft] = React.useState(value ?? '')\n const [error, setError] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const computedSaveLabel = saveLabel ?? t('ui.detail.inline.saveShortcut', 'Save \u2318\u23CE / Ctrl+Enter')\n const fallbackError = React.useMemo(\n () => t('ui.detail.inline.error', 'Failed to save value.'),\n [t],\n )\n const resolvedType = React.useMemo<InlineFieldType>(() => {\n if (type && typeof type === 'string') return type\n if (inputType && typeof inputType === 'string') {\n const normalized = inputType.toLowerCase()\n if (normalized === 'email' || normalized === 'tel' || normalized === 'url') {\n return normalized as InlineFieldType\n }\n }\n return 'text'\n }, [inputType, type])\n\n React.useEffect(() => {\n if (!editing) setDraft(value ?? '')\n }, [editing, value])\n\n React.useEffect(() => {\n if (onDraftChange) onDraftChange(draft)\n }, [draft, onDraftChange])\n\n const containerClasses = cn(\n 'group overflow-hidden',\n variant === 'muted'\n ? 'relative rounded border bg-muted/30 p-3'\n : variant === 'plain'\n ? 'relative flex items-center gap-3 rounded-none border-0 p-0'\n : 'rounded-lg border p-4',\n activateOnClick && !editing ? 'cursor-pointer' : null,\n containerClassName ?? null,\n )\n const readOnlyWrapperClasses = cn(\n 'flex-1 min-w-0',\n activateOnClick && !editing ? 'cursor-pointer' : null,\n variant === 'plain' ? 'flex items-center gap-2' : null,\n )\n const triggerClasses = cn(\n 'shrink-0 transition-opacity duration-150',\n editing\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100',\n variant === 'muted' ? 'h-8 w-8' : null,\n triggerClassName ?? null,\n )\n const triggerSize = variant === 'plain' ? 'icon' : 'sm'\n\n const setEditingSafe = React.useCallback(\n (next: boolean) => {\n setEditing(next)\n if (onEditingChange) onEditingChange(next)\n },\n [onEditingChange],\n )\n\n const handleActivate = React.useCallback(() => {\n if (!editing) setEditingSafe(true)\n }, [editing, setEditingSafe])\n\n const handleInteractiveClick = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!activateOnClick || editing) return\n const target = event.target as HTMLElement\n const interactiveElement = target.closest('button, input, select, textarea, a, [role=\"link\"]')\n if (interactiveElement) {\n if (interactiveElement.tagName.toLowerCase() === 'a') {\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return\n }\n event.preventDefault()\n // let the link click toggle edit mode instead of navigating away\n } else {\n return\n }\n }\n handleActivate()\n },\n [activateOnClick, editing, handleActivate],\n )\n\n const handleContainerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (!activateOnClick || editing) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleActivate()\n }\n },\n [activateOnClick, editing, handleActivate],\n )\n\n const handleSave = React.useCallback(async () => {\n const trimmed = draft.trim()\n if (validator) {\n const validationError = validator(trimmed)\n if (validationError) {\n setError(validationError)\n return\n }\n }\n setError(null)\n setSaving(true)\n try {\n await onSave(trimmed.length ? trimmed : null)\n setEditingSafe(false)\n } catch (err) {\n setError(resolveInlineErrorMessage(err, fallbackError))\n } finally {\n setSaving(false)\n }\n }, [draft, fallbackError, onSave, setEditingSafe, validator])\n\n const interactiveProps: React.HTMLAttributes<HTMLDivElement> =\n activateOnClick && !editing\n ? {\n role: 'button' as const,\n tabIndex: 0,\n onKeyDown: handleContainerKeyDown,\n }\n : {}\n\n const displayContent = React.useMemo(() => {\n if (renderDisplay) {\n return renderDisplay({ value, emptyLabel, type: resolvedType })\n }\n const baseValue = value && typeof value === 'string' ? value : ''\n const anchorClass =\n variant === 'plain'\n ? 'inline-flex max-w-full min-w-0 items-center gap-2 text-xl font-semibold leading-tight text-primary hover:text-primary/90 hover:underline'\n : 'flex max-w-full min-w-0 items-center gap-2 text-sm text-primary hover:text-primary/90 hover:underline'\n const textClass = variant === 'plain' ? 'text-2xl font-semibold leading-tight' : 'text-sm break-words'\n if (resolvedType === 'email') {\n if (!baseValue.length) {\n return (\n <p className={variant === 'plain' ? 'text-base text-muted-foreground' : 'text-sm text-muted-foreground'}>\n {emptyLabel}\n </p>\n )\n }\n return (\n <a className={anchorClass} href={`mailto:${baseValue}`}>\n <Mail aria-hidden className={variant === 'plain' ? 'h-5 w-5' : 'h-4 w-4'} />\n <span className=\"truncate min-w-0\">{baseValue}</span>\n </a>\n )\n }\n if (!baseValue.length) {\n return (\n <p className={variant === 'plain' ? 'text-base text-muted-foreground' : 'text-sm text-muted-foreground'}>\n {emptyLabel}\n </p>\n )\n }\n if (resolvedType === 'tel') {\n const sanitizedValue = baseValue.replace(/[^+\\d]/g, '')\n const hrefValue = sanitizedValue.length ? sanitizedValue : baseValue\n return (\n <a className={anchorClass} href={`tel:${hrefValue}`}>\n <Phone aria-hidden className={variant === 'plain' ? 'h-5 w-5' : 'h-4 w-4'} />\n <span className=\"truncate\">{baseValue}</span>\n </a>\n )\n }\n if (resolvedType === 'url') {\n const safeHref = resolveSafeInlineUrlHref(baseValue)\n if (!safeHref) {\n return <p className={textClass}>{baseValue}</p>\n }\n return (\n <a className={textClass} href={safeHref} target=\"_blank\" rel=\"noopener noreferrer\">\n {baseValue}\n </a>\n )\n }\n return <p className={textClass}>{baseValue}</p>\n }, [emptyLabel, renderDisplay, resolvedType, value, variant])\n\n const editingContainerClass = variant === 'plain' ? 'mt-0 w-full max-w-sm space-y-3' : 'mt-2 space-y-3'\n\n return (\n <div className={containerClasses} onClick={handleInteractiveClick}>\n <div className=\"flex items-start justify-between gap-2 min-w-0\">\n <div className={readOnlyWrapperClasses} {...interactiveProps}>\n {hideLabel ? null : <p className=\"text-xs uppercase tracking-wide text-muted-foreground\">{label}</p>}\n {editing ? (\n <form\n className={editingContainerClass}\n onSubmit={(event) => {\n event.preventDefault()\n if (!saving) void handleSave()\n }}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n setEditingSafe(false)\n setError(null)\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n if (!saving) void handleSave()\n }\n }}\n >\n {(() => {\n const lowerType = (inputType ?? resolvedType ?? '').toLowerCase()\n const isDate = lowerType === 'date'\n const isDateTime = lowerType === 'datetime-local' || lowerType === 'datetime'\n if (resolvedType === 'tel') {\n return (\n <PhoneNumberField\n value={draft.length ? draft : undefined}\n onValueChange={(next) => {\n if (error) setError(null)\n setDraft(next ?? '')\n }}\n placeholder={placeholder}\n autoFocus\n disabled={saving}\n minDigits={7}\n />\n )\n }\n if (isDate || isDateTime) {\n let parsed: Date | null = null\n if (draft && draft.length) {\n const candidate = new Date(draft)\n if (!Number.isNaN(candidate.getTime())) parsed = candidate\n }\n return (\n <DatePicker\n value={parsed}\n onChange={(date) => {\n if (error) setError(null)\n const formatted = !date\n ? ''\n : isDateTime\n ? format(date, \"yyyy-MM-dd'T'HH:mm\")\n : format(date, 'yyyy-MM-dd')\n setDraft(formatted)\n setSaving(true)\n ;(async () => {\n try {\n await onSave(formatted.length ? formatted : null)\n setEditingSafe(false)\n } catch (err) {\n setError(resolveInlineErrorMessage(err, fallbackError))\n } finally {\n setSaving(false)\n }\n })()\n }}\n withTime={isDateTime}\n footer=\"apply-cancel\"\n placeholder={placeholder}\n disabled={saving}\n />\n )\n }\n return (\n <input\n className=\"w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n value={draft}\n onChange={(event) => {\n if (error) setError(null)\n setDraft(event.target.value)\n }}\n placeholder={placeholder}\n type={inputType ?? resolvedType}\n autoFocus\n />\n )\n })()}\n {error ? <p className=\"text-xs text-destructive\">{error}</p> : null}\n {renderBelowInput ? renderBelowInput({ draft, resolvedType, error, saving }) : null}\n {(() => {\n const lowerType = (inputType ?? resolvedType ?? '').toLowerCase()\n const isDateLike = lowerType === 'date' || lowerType === 'datetime-local' || lowerType === 'datetime'\n if (isDateLike) return null\n return (\n <div className=\"flex items-center gap-2\">\n <Button type=\"submit\" size=\"sm\" disabled={saving}>\n {saving ? <Loader2 className=\"mr-2 h-3.5 w-3.5 animate-spin\" /> : null}\n {computedSaveLabel}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => setEditingSafe(false)} disabled={saving}>\n {t('ui.detail.inline.cancel', 'Cancel')}\n </Button>\n </div>\n )\n })()}\n </form>\n ) : (\n <div className={variant === 'plain' ? '' : 'mt-1'}>{displayContent}</div>\n )}\n </div>\n {renderActions ? <div className=\"flex items-center gap-2\">{renderActions}</div> : null}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={triggerSize}\n className={triggerClasses}\n onClick={(event) => {\n event.stopPropagation()\n const next = !editing\n setEditingSafe(next)\n }}\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n </Button>\n </div>\n </div>\n )\n}\n\nexport type InlineMultilineEditorProps = {\n label: string\n value: string | null | undefined\n placeholder?: string\n emptyLabel: string\n onSave: (value: string | null) => Promise<void>\n validator?: (value: string) => string | null\n variant?: EditorVariant\n activateOnClick?: boolean\n containerClassName?: string\n triggerClassName?: string\n renderDisplay?: (params: { value: string | null | undefined; emptyLabel: string }) => React.ReactNode\n}\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nlet markdownPluginsPromise: Promise<PluggableList> | null = null\n\nasync function loadMarkdownPlugins(): Promise<PluggableList> {\n if (isTestEnv) return []\n if (!markdownPluginsPromise) {\n markdownPluginsPromise = import('remark-gfm')\n .then((mod) => [mod.default ?? mod] as PluggableList)\n .catch(() => [])\n }\n return markdownPluginsPromise\n}\n\nexport function InlineMultilineEditor({\n label,\n value,\n placeholder,\n emptyLabel,\n onSave,\n validator,\n variant = 'default',\n activateOnClick = true,\n containerClassName,\n triggerClassName,\n renderDisplay,\n}: InlineMultilineEditorProps) {\n const t = useT()\n const [editing, setEditing] = React.useState(false)\n const [draft, setDraft] = React.useState(value ?? '')\n const [error, setError] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(true)\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [markdownPlugins, setMarkdownPlugins] = React.useState<PluggableList>([])\n const fallbackError = React.useMemo(\n () => t('ui.detail.inline.error', 'Failed to save value.'),\n [t],\n )\n React.useEffect(() => {\n if (isTestEnv) return\n let mounted = true\n void loadMarkdownPlugins().then((plugins) => {\n if (!mounted) return\n setMarkdownPlugins(plugins)\n })\n return () => {\n mounted = false\n }\n }, [])\n\n const adjustTextareaSize = React.useCallback((element: HTMLTextAreaElement | null) => {\n if (!element) return\n element.style.height = 'auto'\n element.style.height = `${element.scrollHeight}px`\n }, [])\n\n React.useEffect(() => {\n adjustTextareaSize(textareaRef.current)\n }, [adjustTextareaSize, draft, isMarkdownEnabled])\n\n React.useEffect(() => {\n if (!editing) return\n const element = textareaRef.current\n if (!element) return\n window.requestAnimationFrame(() => {\n adjustTextareaSize(element)\n element.focus()\n })\n }, [adjustTextareaSize, editing, isMarkdownEnabled])\n\n const handleMarkdownToggle = React.useCallback(() => {\n setIsMarkdownEnabled((prev) => !prev)\n }, [])\n\n React.useEffect(() => {\n if (!editing) {\n setDraft(value ?? '')\n setError(null)\n }\n }, [editing, value])\n\n const handleActivate = React.useCallback(() => {\n if (!editing) setEditing(true)\n }, [editing])\n\n const handleInteractiveClick = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!activateOnClick || editing) return\n const target = event.target as HTMLElement\n const interactiveElement = target.closest('button, input, select, textarea, a, [role=\"link\"]')\n if (interactiveElement) {\n if (interactiveElement.tagName.toLowerCase() === 'a') {\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return\n }\n event.preventDefault()\n // Links should not block activation; let the click toggle edit mode\n } else {\n return\n }\n }\n handleActivate()\n },\n [activateOnClick, editing, handleActivate],\n )\n\n const handleContainerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (!activateOnClick || editing) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleActivate()\n }\n },\n [activateOnClick, editing, handleActivate],\n )\n\n const adjustError = React.useCallback(\n (nextValue: string) => {\n if (!validator) return null\n const trimmed = nextValue.trim()\n return validator(trimmed)\n },\n [validator],\n )\n\n const containerClasses = cn(\n 'group rounded-lg border p-4',\n variant === 'muted' ? 'bg-muted/30' : null,\n activateOnClick && !editing ? 'cursor-pointer' : null,\n containerClassName ?? null,\n )\n const triggerClasses = cn(\n 'transition-opacity duration-150',\n editing\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100',\n triggerClassName ?? null,\n )\n\n const handleSave = React.useCallback(async () => {\n const trimmed = draft.trim()\n const validationError = adjustError(draft)\n if (validationError) {\n setError(validationError)\n return\n }\n setSaving(true)\n try {\n await onSave(trimmed.length ? trimmed : null)\n setEditing(false)\n } catch (err) {\n setError(resolveInlineErrorMessage(err, fallbackError))\n } finally {\n setSaving(false)\n }\n }, [adjustError, draft, fallbackError, onSave])\n\n return (\n <div className={containerClasses} onClick={handleInteractiveClick}>\n <div className=\"flex items-start justify-between gap-2\">\n <div\n className={cn('flex-1 min-w-0', activateOnClick && !editing ? 'cursor-pointer' : null)}\n {...(activateOnClick && !editing\n ? { role: 'button' as const, tabIndex: 0, onKeyDown: handleContainerKeyDown }\n : {})}\n >\n <p className=\"text-xs uppercase tracking-wide text-muted-foreground\">{label}</p>\n {editing ? (\n <form\n className=\"mt-2 space-y-3\"\n onSubmit={(event) => {\n event.preventDefault()\n if (!saving) void handleSave()\n }}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n setEditing(false)\n setError(null)\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n if (!saving) void handleSave()\n }\n }}\n >\n <Textarea\n ref={textareaRef}\n rows={isMarkdownEnabled ? 8 : 3}\n className=\"w-full resize-none overflow-hidden rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n placeholder={placeholder}\n value={draft}\n onChange={(event) => {\n if (error) setError(null)\n setDraft(event.target.value)\n }}\n onInput={(event) => adjustTextareaSize(event.currentTarget)}\n autoFocus\n disabled={saving}\n />\n {error ? <p className=\"text-xs text-destructive\">{error}</p> : null}\n <div className=\"flex items-center gap-2\">\n <Button type=\"submit\" size=\"sm\" disabled={saving}>\n {saving ? <Loader2 className=\"mr-2 h-3.5 w-3.5 animate-spin\" /> : null}\n {t('ui.detail.inline.saveShortcut', 'Save \u2318\u23CE / Ctrl+Enter')}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => setEditing(false)} disabled={saving}>\n {t('ui.detail.inline.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownEnabled}\n title={\n isMarkdownEnabled\n ? t('ui.detail.inline.markdownDisable', 'Disable Markdown')\n : t('ui.detail.inline.markdownEnable', 'Enable Markdown')\n }\n aria-label={\n isMarkdownEnabled\n ? t('ui.detail.inline.markdownDisable', 'Disable Markdown')\n : t('ui.detail.inline.markdownEnable', 'Enable Markdown')\n }\n className={cn('h-8 w-8', isMarkdownEnabled ? 'text-primary' : undefined)}\n disabled={saving}\n >\n <FileCode className=\"h-4 w-4\" aria-hidden />\n <span className=\"sr-only\">\n {isMarkdownEnabled\n ? t('ui.detail.inline.markdownDisable', 'Disable Markdown')\n : t('ui.detail.inline.markdownEnable', 'Enable Markdown')}\n </span>\n </Button>\n </div>\n </form>\n ) : (\n <div\n className={cn(\n 'mt-1 text-sm break-words',\n renderDisplay ? null : 'whitespace-pre-wrap',\n activateOnClick && !editing ? 'cursor-pointer' : null,\n )}\n >\n {renderDisplay ? (\n renderDisplay({ value, emptyLabel })\n ) : value && value.length ? (\n <MarkdownPreview\n remarkPlugins={markdownPlugins}\n className=\"prose prose-sm max-w-none text-foreground [&>*]:my-2 [&>*:last-child]:mb-0 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5\"\n >\n {value}\n </MarkdownPreview>\n ) : (\n <span className=\"text-muted-foreground\">{emptyLabel}</span>\n )}\n </div>\n )}\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={triggerClasses}\n onClick={(event) => {\n event.stopPropagation()\n setEditing((state) => !state)\n }}\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n </Button>\n </div>\n </div>\n )\n}\n\nexport type InlineSelectOption = { value: string; label: string; description?: string }\n\nexport type InlineSelectEditorProps = {\n label: string\n value: string | null | undefined\n emptyLabel: string\n options: InlineSelectOption[]\n onSave: (value: string | null) => Promise<void>\n variant?: EditorVariant\n activateOnClick?: boolean\n containerClassName?: string\n triggerClassName?: string\n hideLabel?: boolean\n renderEditor?: (params: { value: string; onChange: (next: string) => void }) => React.ReactNode\n renderDisplay?: (params: { value: string | null | undefined; emptyLabel: string }) => React.ReactNode\n}\n\nexport function InlineSelectEditor({\n label,\n value,\n emptyLabel,\n options,\n onSave,\n variant = 'default',\n activateOnClick = false,\n containerClassName,\n triggerClassName,\n hideLabel = false,\n renderEditor,\n renderDisplay,\n}: InlineSelectEditorProps) {\n const t = useT()\n const [editing, setEditing] = React.useState(false)\n const [draft, setDraft] = React.useState<string>(value ?? '')\n const [saving, setSaving] = React.useState(false)\n\n React.useEffect(() => {\n if (!editing) setDraft(value ?? '')\n }, [editing, value])\n\n const containerClasses = cn(\n 'group',\n variant === 'muted'\n ? 'relative rounded border bg-muted/30 p-3'\n : variant === 'plain'\n ? 'relative flex flex-col gap-1 rounded-none border-0 p-0'\n : 'rounded-lg border bg-card p-4',\n activateOnClick && !editing ? 'cursor-pointer' : null,\n containerClassName ?? null,\n )\n const triggerClasses = cn(\n 'shrink-0 transition-opacity duration-150',\n editing\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100',\n variant === 'muted' ? 'h-8 w-8' : null,\n triggerClassName ?? null,\n )\n\n const handleSave = React.useCallback(async () => {\n setSaving(true)\n try {\n await onSave(draft.length ? draft : null)\n setEditing(false)\n } catch (err) {\n const message = err instanceof Error ? err.message : t('ui.detail.inline.error', 'Failed to save value.')\n console.error(message, err)\n } finally {\n setSaving(false)\n }\n }, [draft, onSave, t])\n\n const selected = options.find((option) => option.value === value)\n\n const interactiveProps: React.HTMLAttributes<HTMLDivElement> =\n activateOnClick && !editing\n ? {\n role: 'button' as const,\n tabIndex: 0,\n onClick: () => setEditing(true),\n onKeyDown: (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setEditing(true)\n }\n },\n }\n : {}\n\n return (\n <div className={containerClasses}>\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\" {...interactiveProps}>\n {hideLabel ? null : <p className=\"text-xs uppercase tracking-wide text-muted-foreground\">{label}</p>}\n {editing ? (\n <div className={variant === 'plain' ? 'space-y-2 pt-1' : 'mt-2 space-y-2'}>\n {renderEditor ? (\n renderEditor({ value: draft, onChange: setDraft })\n ) : (\n <Select\n value={draft || undefined}\n onValueChange={(next) => setDraft(next ?? '')}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('ui.detail.inline.select.placeholder', 'Not set')} />\n </SelectTrigger>\n <SelectContent>\n {options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" size=\"sm\" onClick={() => void handleSave()} disabled={saving}>\n {saving ? <Loader2 className=\"mr-2 h-3.5 w-3.5 animate-spin\" /> : null}\n {t('ui.detail.inline.saveShortcut', 'Save \u2318\u23CE / Ctrl+Enter')}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => setEditing(false)} disabled={saving}>\n {t('ui.detail.inline.cancel', 'Cancel')}\n </Button>\n </div>\n </div>\n ) : (\n <div className={variant === 'plain' ? 'flex items-center gap-2' : 'mt-1 text-sm'}>\n {renderDisplay ? (\n renderDisplay({ value, emptyLabel })\n ) : selected ? (\n <div className=\"space-y-0.5\">\n <p className=\"font-medium leading-tight\">{selected.label}</p>\n {selected.description ? (\n <p className=\"text-xs text-muted-foreground\">{selected.description}</p>\n ) : null}\n </div>\n ) : (\n <span className=\"text-muted-foreground\">{emptyLabel}</span>\n )}\n </div>\n )}\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={variant === 'plain' ? 'icon' : 'sm'}\n className={triggerClasses}\n onClick={(event) => {\n event.stopPropagation()\n setEditing((state) => !state)\n }}\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n </Button>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { FileCode, Loader2, Mail, Pencil, Phone, X } from 'lucide-react'\nimport type { PluggableList } from 'unified'\nimport { PhoneNumberField } from '@open-mercato/ui/backend/inputs/PhoneNumberField'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { DatePicker } from '@open-mercato/ui/primitives/date-picker'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { format } from 'date-fns/format'\nimport { LoadingMessage } from './LoadingMessage'\nimport { mapCrudServerErrorToFormErrors } from '../utils/serverErrors'\nimport { MarkdownPreview } from '../markdown'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('ui')\n\nfunction resolveInlineErrorMessage(err: unknown, fallbackMessage: string): string {\n const { message, fieldErrors } = mapCrudServerErrorToFormErrors(err)\n const firstFieldError = fieldErrors\n ? Object.values(fieldErrors).find((text) => typeof text === 'string' && text.trim().length)\n : null\n if (typeof firstFieldError === 'string' && firstFieldError.trim().length) {\n return firstFieldError.trim()\n }\n if (typeof message === 'string' && message.trim().length) {\n return message.trim()\n }\n if (err instanceof Error && typeof err.message === 'string' && err.message.trim().length) {\n return err.message.trim()\n }\n if (typeof err === 'string' && err.trim().length) {\n return err.trim()\n }\n return fallbackMessage\n}\n\ntype EditorVariant = 'default' | 'muted' | 'plain'\n\nexport type InlineFieldType = 'text' | 'email' | 'tel' | 'url'\n\nconst ALLOWED_INLINE_URL_PROTOCOLS = new Set(['http:', 'https:', 'mailto:', 'tel:'])\n\nexport function resolveSafeInlineUrlHref(value: string): string | null {\n const trimmed = value.trim()\n if (!trimmed.length) return null\n try {\n const parsed = new URL(trimmed)\n return ALLOWED_INLINE_URL_PROTOCOLS.has(parsed.protocol) ? trimmed : null\n } catch {\n return null\n }\n}\n\nexport type InlineTextEditorProps = {\n label: string\n value: string | null | undefined\n placeholder?: string\n emptyLabel: string\n onSave: (value: string | null) => Promise<void>\n type?: InlineFieldType\n inputType?: React.HTMLInputTypeAttribute\n validator?: (value: string) => string | null\n variant?: EditorVariant\n activateOnClick?: boolean\n containerClassName?: string\n triggerClassName?: string\n hideLabel?: boolean\n renderDisplay?: (params: { value: string | null | undefined; emptyLabel: string; type: InlineFieldType }) => React.ReactNode\n onEditingChange?: (editing: boolean) => void\n renderActions?: React.ReactNode\n saveLabel?: string\n recordId?: string\n onDraftChange?: (draft: string) => void\n renderBelowInput?: (params: {\n draft: string\n resolvedType: InlineFieldType\n error: string | null\n saving: boolean\n }) => React.ReactNode\n}\n\nexport function InlineTextEditor({\n label,\n value,\n placeholder,\n emptyLabel,\n onSave,\n type = 'text',\n inputType,\n validator,\n variant = 'default',\n activateOnClick = false,\n containerClassName,\n triggerClassName,\n hideLabel = false,\n renderDisplay,\n onEditingChange,\n renderActions,\n saveLabel,\n onDraftChange,\n renderBelowInput,\n}: InlineTextEditorProps) {\n const t = useT()\n const [editing, setEditing] = React.useState(false)\n const [draft, setDraft] = React.useState(value ?? '')\n const [error, setError] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const computedSaveLabel = saveLabel ?? t('ui.detail.inline.saveShortcut', 'Save \u2318\u23CE / Ctrl+Enter')\n const fallbackError = React.useMemo(\n () => t('ui.detail.inline.error', 'Failed to save value.'),\n [t],\n )\n const resolvedType = React.useMemo<InlineFieldType>(() => {\n if (type && typeof type === 'string') return type\n if (inputType && typeof inputType === 'string') {\n const normalized = inputType.toLowerCase()\n if (normalized === 'email' || normalized === 'tel' || normalized === 'url') {\n return normalized as InlineFieldType\n }\n }\n return 'text'\n }, [inputType, type])\n\n React.useEffect(() => {\n if (!editing) setDraft(value ?? '')\n }, [editing, value])\n\n React.useEffect(() => {\n if (onDraftChange) onDraftChange(draft)\n }, [draft, onDraftChange])\n\n const containerClasses = cn(\n 'group overflow-hidden',\n variant === 'muted'\n ? 'relative rounded border bg-muted/30 p-3'\n : variant === 'plain'\n ? 'relative flex items-center gap-3 rounded-none border-0 p-0'\n : 'rounded-lg border p-4',\n activateOnClick && !editing ? 'cursor-pointer' : null,\n containerClassName ?? null,\n )\n const readOnlyWrapperClasses = cn(\n 'flex-1 min-w-0',\n activateOnClick && !editing ? 'cursor-pointer' : null,\n variant === 'plain' ? 'flex items-center gap-2' : null,\n )\n const triggerClasses = cn(\n 'shrink-0 transition-opacity duration-150',\n editing\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100',\n variant === 'muted' ? 'h-8 w-8' : null,\n triggerClassName ?? null,\n )\n const triggerSize = variant === 'plain' ? 'icon' : 'sm'\n\n const setEditingSafe = React.useCallback(\n (next: boolean) => {\n setEditing(next)\n if (onEditingChange) onEditingChange(next)\n },\n [onEditingChange],\n )\n\n const handleActivate = React.useCallback(() => {\n if (!editing) setEditingSafe(true)\n }, [editing, setEditingSafe])\n\n const handleInteractiveClick = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!activateOnClick || editing) return\n const target = event.target as HTMLElement\n const interactiveElement = target.closest('button, input, select, textarea, a, [role=\"link\"]')\n if (interactiveElement) {\n if (interactiveElement.tagName.toLowerCase() === 'a') {\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return\n }\n event.preventDefault()\n // let the link click toggle edit mode instead of navigating away\n } else {\n return\n }\n }\n handleActivate()\n },\n [activateOnClick, editing, handleActivate],\n )\n\n const handleContainerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (!activateOnClick || editing) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleActivate()\n }\n },\n [activateOnClick, editing, handleActivate],\n )\n\n const handleSave = React.useCallback(async () => {\n const trimmed = draft.trim()\n if (validator) {\n const validationError = validator(trimmed)\n if (validationError) {\n setError(validationError)\n return\n }\n }\n setError(null)\n setSaving(true)\n try {\n await onSave(trimmed.length ? trimmed : null)\n setEditingSafe(false)\n } catch (err) {\n setError(resolveInlineErrorMessage(err, fallbackError))\n } finally {\n setSaving(false)\n }\n }, [draft, fallbackError, onSave, setEditingSafe, validator])\n\n const interactiveProps: React.HTMLAttributes<HTMLDivElement> =\n activateOnClick && !editing\n ? {\n role: 'button' as const,\n tabIndex: 0,\n onKeyDown: handleContainerKeyDown,\n }\n : {}\n\n const displayContent = React.useMemo(() => {\n if (renderDisplay) {\n return renderDisplay({ value, emptyLabel, type: resolvedType })\n }\n const baseValue = value && typeof value === 'string' ? value : ''\n const anchorClass =\n variant === 'plain'\n ? 'inline-flex max-w-full min-w-0 items-center gap-2 text-xl font-semibold leading-tight text-primary hover:text-primary/90 hover:underline'\n : 'flex max-w-full min-w-0 items-center gap-2 text-sm text-primary hover:text-primary/90 hover:underline'\n const textClass = variant === 'plain' ? 'text-2xl font-semibold leading-tight' : 'text-sm break-words'\n if (resolvedType === 'email') {\n if (!baseValue.length) {\n return (\n <p className={variant === 'plain' ? 'text-base text-muted-foreground' : 'text-sm text-muted-foreground'}>\n {emptyLabel}\n </p>\n )\n }\n return (\n <a className={anchorClass} href={`mailto:${baseValue}`}>\n <Mail aria-hidden className={variant === 'plain' ? 'h-5 w-5' : 'h-4 w-4'} />\n <span className=\"truncate min-w-0\">{baseValue}</span>\n </a>\n )\n }\n if (!baseValue.length) {\n return (\n <p className={variant === 'plain' ? 'text-base text-muted-foreground' : 'text-sm text-muted-foreground'}>\n {emptyLabel}\n </p>\n )\n }\n if (resolvedType === 'tel') {\n const sanitizedValue = baseValue.replace(/[^+\\d]/g, '')\n const hrefValue = sanitizedValue.length ? sanitizedValue : baseValue\n return (\n <a className={anchorClass} href={`tel:${hrefValue}`}>\n <Phone aria-hidden className={variant === 'plain' ? 'h-5 w-5' : 'h-4 w-4'} />\n <span className=\"truncate\">{baseValue}</span>\n </a>\n )\n }\n if (resolvedType === 'url') {\n const safeHref = resolveSafeInlineUrlHref(baseValue)\n if (!safeHref) {\n return <p className={textClass}>{baseValue}</p>\n }\n return (\n <a className={textClass} href={safeHref} target=\"_blank\" rel=\"noopener noreferrer\">\n {baseValue}\n </a>\n )\n }\n return <p className={textClass}>{baseValue}</p>\n }, [emptyLabel, renderDisplay, resolvedType, value, variant])\n\n const editingContainerClass = variant === 'plain' ? 'mt-0 w-full max-w-sm space-y-3' : 'mt-2 space-y-3'\n\n return (\n <div className={containerClasses} onClick={handleInteractiveClick}>\n <div className=\"flex items-start justify-between gap-2 min-w-0\">\n <div className={readOnlyWrapperClasses} {...interactiveProps}>\n {hideLabel ? null : <p className=\"text-xs uppercase tracking-wide text-muted-foreground\">{label}</p>}\n {editing ? (\n <form\n className={editingContainerClass}\n onSubmit={(event) => {\n event.preventDefault()\n if (!saving) void handleSave()\n }}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n setEditingSafe(false)\n setError(null)\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n if (!saving) void handleSave()\n }\n }}\n >\n {(() => {\n const lowerType = (inputType ?? resolvedType ?? '').toLowerCase()\n const isDate = lowerType === 'date'\n const isDateTime = lowerType === 'datetime-local' || lowerType === 'datetime'\n if (resolvedType === 'tel') {\n return (\n <PhoneNumberField\n value={draft.length ? draft : undefined}\n onValueChange={(next) => {\n if (error) setError(null)\n setDraft(next ?? '')\n }}\n placeholder={placeholder}\n autoFocus\n disabled={saving}\n minDigits={7}\n />\n )\n }\n if (isDate || isDateTime) {\n let parsed: Date | null = null\n if (draft && draft.length) {\n const candidate = new Date(draft)\n if (!Number.isNaN(candidate.getTime())) parsed = candidate\n }\n return (\n <DatePicker\n value={parsed}\n onChange={(date) => {\n if (error) setError(null)\n const formatted = !date\n ? ''\n : isDateTime\n ? format(date, \"yyyy-MM-dd'T'HH:mm\")\n : format(date, 'yyyy-MM-dd')\n setDraft(formatted)\n setSaving(true)\n ;(async () => {\n try {\n await onSave(formatted.length ? formatted : null)\n setEditingSafe(false)\n } catch (err) {\n setError(resolveInlineErrorMessage(err, fallbackError))\n } finally {\n setSaving(false)\n }\n })()\n }}\n withTime={isDateTime}\n footer=\"apply-cancel\"\n placeholder={placeholder}\n disabled={saving}\n />\n )\n }\n return (\n <input\n className=\"w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n value={draft}\n onChange={(event) => {\n if (error) setError(null)\n setDraft(event.target.value)\n }}\n placeholder={placeholder}\n type={inputType ?? resolvedType}\n autoFocus\n />\n )\n })()}\n {error ? <p className=\"text-xs text-destructive\">{error}</p> : null}\n {renderBelowInput ? renderBelowInput({ draft, resolvedType, error, saving }) : null}\n {(() => {\n const lowerType = (inputType ?? resolvedType ?? '').toLowerCase()\n const isDateLike = lowerType === 'date' || lowerType === 'datetime-local' || lowerType === 'datetime'\n if (isDateLike) return null\n return (\n <div className=\"flex items-center gap-2\">\n <Button type=\"submit\" size=\"sm\" disabled={saving}>\n {saving ? <Loader2 className=\"mr-2 h-3.5 w-3.5 animate-spin\" /> : null}\n {computedSaveLabel}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => setEditingSafe(false)} disabled={saving}>\n {t('ui.detail.inline.cancel', 'Cancel')}\n </Button>\n </div>\n )\n })()}\n </form>\n ) : (\n <div className={variant === 'plain' ? '' : 'mt-1'}>{displayContent}</div>\n )}\n </div>\n {renderActions ? <div className=\"flex items-center gap-2\">{renderActions}</div> : null}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={triggerSize}\n className={triggerClasses}\n onClick={(event) => {\n event.stopPropagation()\n const next = !editing\n setEditingSafe(next)\n }}\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n </Button>\n </div>\n </div>\n )\n}\n\nexport type InlineMultilineEditorProps = {\n label: string\n value: string | null | undefined\n placeholder?: string\n emptyLabel: string\n onSave: (value: string | null) => Promise<void>\n validator?: (value: string) => string | null\n variant?: EditorVariant\n activateOnClick?: boolean\n containerClassName?: string\n triggerClassName?: string\n renderDisplay?: (params: { value: string | null | undefined; emptyLabel: string }) => React.ReactNode\n}\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nlet markdownPluginsPromise: Promise<PluggableList> | null = null\n\nasync function loadMarkdownPlugins(): Promise<PluggableList> {\n if (isTestEnv) return []\n if (!markdownPluginsPromise) {\n markdownPluginsPromise = import('remark-gfm')\n .then((mod) => [mod.default ?? mod] as PluggableList)\n .catch(() => [])\n }\n return markdownPluginsPromise\n}\n\nexport function InlineMultilineEditor({\n label,\n value,\n placeholder,\n emptyLabel,\n onSave,\n validator,\n variant = 'default',\n activateOnClick = true,\n containerClassName,\n triggerClassName,\n renderDisplay,\n}: InlineMultilineEditorProps) {\n const t = useT()\n const [editing, setEditing] = React.useState(false)\n const [draft, setDraft] = React.useState(value ?? '')\n const [error, setError] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(true)\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [markdownPlugins, setMarkdownPlugins] = React.useState<PluggableList>([])\n const fallbackError = React.useMemo(\n () => t('ui.detail.inline.error', 'Failed to save value.'),\n [t],\n )\n React.useEffect(() => {\n if (isTestEnv) return\n let mounted = true\n void loadMarkdownPlugins().then((plugins) => {\n if (!mounted) return\n setMarkdownPlugins(plugins)\n })\n return () => {\n mounted = false\n }\n }, [])\n\n const adjustTextareaSize = React.useCallback((element: HTMLTextAreaElement | null) => {\n if (!element) return\n element.style.height = 'auto'\n element.style.height = `${element.scrollHeight}px`\n }, [])\n\n React.useEffect(() => {\n adjustTextareaSize(textareaRef.current)\n }, [adjustTextareaSize, draft, isMarkdownEnabled])\n\n React.useEffect(() => {\n if (!editing) return\n const element = textareaRef.current\n if (!element) return\n window.requestAnimationFrame(() => {\n adjustTextareaSize(element)\n element.focus()\n })\n }, [adjustTextareaSize, editing, isMarkdownEnabled])\n\n const handleMarkdownToggle = React.useCallback(() => {\n setIsMarkdownEnabled((prev) => !prev)\n }, [])\n\n React.useEffect(() => {\n if (!editing) {\n setDraft(value ?? '')\n setError(null)\n }\n }, [editing, value])\n\n const handleActivate = React.useCallback(() => {\n if (!editing) setEditing(true)\n }, [editing])\n\n const handleInteractiveClick = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!activateOnClick || editing) return\n const target = event.target as HTMLElement\n const interactiveElement = target.closest('button, input, select, textarea, a, [role=\"link\"]')\n if (interactiveElement) {\n if (interactiveElement.tagName.toLowerCase() === 'a') {\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return\n }\n event.preventDefault()\n // Links should not block activation; let the click toggle edit mode\n } else {\n return\n }\n }\n handleActivate()\n },\n [activateOnClick, editing, handleActivate],\n )\n\n const handleContainerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (!activateOnClick || editing) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleActivate()\n }\n },\n [activateOnClick, editing, handleActivate],\n )\n\n const adjustError = React.useCallback(\n (nextValue: string) => {\n if (!validator) return null\n const trimmed = nextValue.trim()\n return validator(trimmed)\n },\n [validator],\n )\n\n const containerClasses = cn(\n 'group rounded-lg border p-4',\n variant === 'muted' ? 'bg-muted/30' : null,\n activateOnClick && !editing ? 'cursor-pointer' : null,\n containerClassName ?? null,\n )\n const triggerClasses = cn(\n 'transition-opacity duration-150',\n editing\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100',\n triggerClassName ?? null,\n )\n\n const handleSave = React.useCallback(async () => {\n const trimmed = draft.trim()\n const validationError = adjustError(draft)\n if (validationError) {\n setError(validationError)\n return\n }\n setSaving(true)\n try {\n await onSave(trimmed.length ? trimmed : null)\n setEditing(false)\n } catch (err) {\n setError(resolveInlineErrorMessage(err, fallbackError))\n } finally {\n setSaving(false)\n }\n }, [adjustError, draft, fallbackError, onSave])\n\n return (\n <div className={containerClasses} onClick={handleInteractiveClick}>\n <div className=\"flex items-start justify-between gap-2\">\n <div\n className={cn('flex-1 min-w-0', activateOnClick && !editing ? 'cursor-pointer' : null)}\n {...(activateOnClick && !editing\n ? { role: 'button' as const, tabIndex: 0, onKeyDown: handleContainerKeyDown }\n : {})}\n >\n <p className=\"text-xs uppercase tracking-wide text-muted-foreground\">{label}</p>\n {editing ? (\n <form\n className=\"mt-2 space-y-3\"\n onSubmit={(event) => {\n event.preventDefault()\n if (!saving) void handleSave()\n }}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n setEditing(false)\n setError(null)\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n if (!saving) void handleSave()\n }\n }}\n >\n <Textarea\n ref={textareaRef}\n rows={isMarkdownEnabled ? 8 : 3}\n className=\"w-full resize-none overflow-hidden rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n placeholder={placeholder}\n value={draft}\n onChange={(event) => {\n if (error) setError(null)\n setDraft(event.target.value)\n }}\n onInput={(event) => adjustTextareaSize(event.currentTarget)}\n autoFocus\n disabled={saving}\n />\n {error ? <p className=\"text-xs text-destructive\">{error}</p> : null}\n <div className=\"flex items-center gap-2\">\n <Button type=\"submit\" size=\"sm\" disabled={saving}>\n {saving ? <Loader2 className=\"mr-2 h-3.5 w-3.5 animate-spin\" /> : null}\n {t('ui.detail.inline.saveShortcut', 'Save \u2318\u23CE / Ctrl+Enter')}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => setEditing(false)} disabled={saving}>\n {t('ui.detail.inline.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownEnabled}\n title={\n isMarkdownEnabled\n ? t('ui.detail.inline.markdownDisable', 'Disable Markdown')\n : t('ui.detail.inline.markdownEnable', 'Enable Markdown')\n }\n aria-label={\n isMarkdownEnabled\n ? t('ui.detail.inline.markdownDisable', 'Disable Markdown')\n : t('ui.detail.inline.markdownEnable', 'Enable Markdown')\n }\n className={cn('h-8 w-8', isMarkdownEnabled ? 'text-primary' : undefined)}\n disabled={saving}\n >\n <FileCode className=\"h-4 w-4\" aria-hidden />\n <span className=\"sr-only\">\n {isMarkdownEnabled\n ? t('ui.detail.inline.markdownDisable', 'Disable Markdown')\n : t('ui.detail.inline.markdownEnable', 'Enable Markdown')}\n </span>\n </Button>\n </div>\n </form>\n ) : (\n <div\n className={cn(\n 'mt-1 text-sm break-words',\n renderDisplay ? null : 'whitespace-pre-wrap',\n activateOnClick && !editing ? 'cursor-pointer' : null,\n )}\n >\n {renderDisplay ? (\n renderDisplay({ value, emptyLabel })\n ) : value && value.length ? (\n <MarkdownPreview\n remarkPlugins={markdownPlugins}\n className=\"prose prose-sm max-w-none text-foreground [&>*]:my-2 [&>*:last-child]:mb-0 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5\"\n >\n {value}\n </MarkdownPreview>\n ) : (\n <span className=\"text-muted-foreground\">{emptyLabel}</span>\n )}\n </div>\n )}\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={triggerClasses}\n onClick={(event) => {\n event.stopPropagation()\n setEditing((state) => !state)\n }}\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n </Button>\n </div>\n </div>\n )\n}\n\nexport type InlineSelectOption = { value: string; label: string; description?: string }\n\nexport type InlineSelectEditorProps = {\n label: string\n value: string | null | undefined\n emptyLabel: string\n options: InlineSelectOption[]\n onSave: (value: string | null) => Promise<void>\n variant?: EditorVariant\n activateOnClick?: boolean\n containerClassName?: string\n triggerClassName?: string\n hideLabel?: boolean\n renderEditor?: (params: { value: string; onChange: (next: string) => void }) => React.ReactNode\n renderDisplay?: (params: { value: string | null | undefined; emptyLabel: string }) => React.ReactNode\n}\n\nexport function InlineSelectEditor({\n label,\n value,\n emptyLabel,\n options,\n onSave,\n variant = 'default',\n activateOnClick = false,\n containerClassName,\n triggerClassName,\n hideLabel = false,\n renderEditor,\n renderDisplay,\n}: InlineSelectEditorProps) {\n const t = useT()\n const [editing, setEditing] = React.useState(false)\n const [draft, setDraft] = React.useState<string>(value ?? '')\n const [saving, setSaving] = React.useState(false)\n\n React.useEffect(() => {\n if (!editing) setDraft(value ?? '')\n }, [editing, value])\n\n const containerClasses = cn(\n 'group',\n variant === 'muted'\n ? 'relative rounded border bg-muted/30 p-3'\n : variant === 'plain'\n ? 'relative flex flex-col gap-1 rounded-none border-0 p-0'\n : 'rounded-lg border bg-card p-4',\n activateOnClick && !editing ? 'cursor-pointer' : null,\n containerClassName ?? null,\n )\n const triggerClasses = cn(\n 'shrink-0 transition-opacity duration-150',\n editing\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100',\n variant === 'muted' ? 'h-8 w-8' : null,\n triggerClassName ?? null,\n )\n\n const handleSave = React.useCallback(async () => {\n setSaving(true)\n try {\n await onSave(draft.length ? draft : null)\n setEditing(false)\n } catch (err) {\n const message = err instanceof Error ? err.message : t('ui.detail.inline.error', 'Failed to save value.')\n logger.error(message, { err })\n } finally {\n setSaving(false)\n }\n }, [draft, onSave, t])\n\n const selected = options.find((option) => option.value === value)\n\n const interactiveProps: React.HTMLAttributes<HTMLDivElement> =\n activateOnClick && !editing\n ? {\n role: 'button' as const,\n tabIndex: 0,\n onClick: () => setEditing(true),\n onKeyDown: (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setEditing(true)\n }\n },\n }\n : {}\n\n return (\n <div className={containerClasses}>\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\" {...interactiveProps}>\n {hideLabel ? null : <p className=\"text-xs uppercase tracking-wide text-muted-foreground\">{label}</p>}\n {editing ? (\n <div className={variant === 'plain' ? 'space-y-2 pt-1' : 'mt-2 space-y-2'}>\n {renderEditor ? (\n renderEditor({ value: draft, onChange: setDraft })\n ) : (\n <Select\n value={draft || undefined}\n onValueChange={(next) => setDraft(next ?? '')}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('ui.detail.inline.select.placeholder', 'Not set')} />\n </SelectTrigger>\n <SelectContent>\n {options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" size=\"sm\" onClick={() => void handleSave()} disabled={saving}>\n {saving ? <Loader2 className=\"mr-2 h-3.5 w-3.5 animate-spin\" /> : null}\n {t('ui.detail.inline.saveShortcut', 'Save \u2318\u23CE / Ctrl+Enter')}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => setEditing(false)} disabled={saving}>\n {t('ui.detail.inline.cancel', 'Cancel')}\n </Button>\n </div>\n </div>\n ) : (\n <div className={variant === 'plain' ? 'flex items-center gap-2' : 'mt-1 text-sm'}>\n {renderDisplay ? (\n renderDisplay({ value, emptyLabel })\n ) : selected ? (\n <div className=\"space-y-0.5\">\n <p className=\"font-medium leading-tight\">{selected.label}</p>\n {selected.description ? (\n <p className=\"text-xs text-muted-foreground\">{selected.description}</p>\n ) : null}\n </div>\n ) : (\n <span className=\"text-muted-foreground\">{emptyLabel}</span>\n )}\n </div>\n )}\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={variant === 'plain' ? 'icon' : 'sm'}\n className={triggerClasses}\n onClick={(event) => {\n event.stopPropagation()\n setEditing((state) => !state)\n }}\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n </Button>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -8,6 +8,8 @@ import { DataLoader } from "@open-mercato/ui/primitives/DataLoader";
|
|
|
8
8
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
9
9
|
import { ComponentReplacementHandles } from "@open-mercato/shared/modules/widgets/component-registry";
|
|
10
10
|
import { useRegisteredComponent } from "../injection/useRegisteredComponent.js";
|
|
11
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
12
|
+
const logger = createLogger("ui");
|
|
11
13
|
function normalizeTagLabels(labels) {
|
|
12
14
|
return Array.from(
|
|
13
15
|
new Set(
|
|
@@ -80,7 +82,7 @@ function TagsSectionImpl({
|
|
|
80
82
|
syncFetchedOptions(fetched);
|
|
81
83
|
return fetched.map((tag) => tag.label);
|
|
82
84
|
} catch (err) {
|
|
83
|
-
|
|
85
|
+
logger.error("Failed to load tag suggestions", { err });
|
|
84
86
|
return [];
|
|
85
87
|
}
|
|
86
88
|
},
|