@open-mercato/ui 0.4.11-develop.2631.481e9df5b0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/AGENTS.md +28 -4
- package/agentic/standalone-guide.md +97 -0
- package/build.mjs +10 -6
- package/dist/backend/AppShell.js +15 -2
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/DataTable.js +22 -1
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/detail/CustomDataSection.js +1 -5
- package/dist/backend/detail/CustomDataSection.js.map +2 -2
- package/dist/backend/detail/InlineEditors.js +2 -5
- package/dist/backend/detail/InlineEditors.js.map +2 -2
- package/dist/backend/detail/NotesSection.js +2 -6
- package/dist/backend/detail/NotesSection.js.map +2 -2
- package/dist/backend/icons/lucideRegistry.generated.js +93 -3
- package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
- package/dist/backend/markdown/MarkdownContent.js +47 -4
- package/dist/backend/markdown/MarkdownContent.js.map +2 -2
- package/dist/portal/PortalShell.js +41 -11
- package/dist/portal/PortalShell.js.map +2 -2
- package/dist/portal/hooks/usePortalDashboardWidgets.js +40 -1
- package/dist/portal/hooks/usePortalDashboardWidgets.js.map +2 -2
- package/dist/portal/utils/nav.js +84 -0
- package/dist/portal/utils/nav.js.map +7 -0
- package/package.json +13 -9
- package/src/backend/AppShell.tsx +22 -2
- package/src/backend/DataTable.tsx +28 -5
- package/src/backend/__tests__/AppShell.test.tsx +67 -0
- package/src/backend/__tests__/FormHeader.test.tsx +0 -1
- package/src/backend/detail/CustomDataSection.tsx +1 -10
- package/src/backend/detail/InlineEditors.tsx +3 -15
- package/src/backend/detail/NotesSection.tsx +5 -14
- package/src/backend/icons/lucideRegistry.generated.tsx +93 -3
- package/src/backend/injection/__tests__/resolveInjectedIcon.test.tsx +7 -0
- package/src/backend/markdown/MarkdownContent.tsx +76 -6
- package/src/backend/section-page/types.ts +1 -0
- package/src/portal/PortalShell.tsx +43 -11
- package/src/portal/hooks/__tests__/usePortalDashboardWidgets.test.tsx +117 -0
- package/src/portal/hooks/usePortalDashboardWidgets.ts +55 -1
- package/src/portal/utils/__tests__/nav.test.ts +199 -0
- package/src/portal/utils/nav.ts +150 -0
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import Link from "next/link";
|
|
5
|
-
import dynamic from "next/dynamic";
|
|
6
5
|
import { Pencil, X } from "lucide-react";
|
|
7
6
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
8
7
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
@@ -25,12 +24,9 @@ import {
|
|
|
25
24
|
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
26
25
|
import { cn } from "@open-mercato/shared/lib/utils";
|
|
27
26
|
import { ComponentReplacementHandles } from "@open-mercato/shared/modules/widgets/component-registry";
|
|
27
|
+
import { MarkdownPreview } from "../markdown/index.js";
|
|
28
28
|
import { useRegisteredComponent } from "../injection/useRegisteredComponent.js";
|
|
29
29
|
const isTestEnv = typeof process !== "undefined" && process.env.NODE_ENV === "test";
|
|
30
|
-
const MarkdownPreview = isTestEnv ? ({ children, className }) => /* @__PURE__ */ jsx("div", { className, children }) : dynamic(() => import("react-markdown").then((mod) => mod.default), {
|
|
31
|
-
ssr: false,
|
|
32
|
-
loading: () => null
|
|
33
|
-
});
|
|
34
30
|
let markdownPluginsPromise = null;
|
|
35
31
|
async function loadMarkdownPlugins() {
|
|
36
32
|
if (isTestEnv) return [];
|
|
@@ -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 dynamic from 'next/dynamic'\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 { useRegisteredComponent } from '../injection/useRegisteredComponent'\n\ntype MarkdownPreviewProps = { children: string; className?: string; remarkPlugins?: PluggableList }\n\nconst isTestEnv = typeof process !== 'undefined' && process.env.NODE_ENV === 'test'\n\nconst MarkdownPreview: React.ComponentType<MarkdownPreviewProps> = isTestEnv\n ? ({ children, className }) => <div className={className}>{children}</div>\n : (dynamic(() => import('react-markdown').then((mod) => mod.default as React.ComponentType<MarkdownPreviewProps>), {\n ssr: false,\n loading: () => null,\n }) as unknown as React.ComponentType<MarkdownPreviewProps>)\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\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 [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(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={values}\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/20 p-3 sm:p-4 space-y-2 sm:space-y-3 transition hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary',\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 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": ";AAuCiC,cA2iBzB,YA3iByB;AArCjC,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,OAAO,aAAa;AAEpB,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,8BAA8B;AAIvC,MAAM,YAAY,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;AAE7E,MAAM,kBAA6D,YAC/D,CAAC,EAAE,UAAU,UAAU,MAAM,oBAAC,SAAI,WAAuB,UAAS,IACjE,QAAQ,MAAM,OAAO,gBAAgB,EAAE,KAAK,CAAC,QAAQ,IAAI,OAAoD,GAAG;AAAA,EAC/G,KAAK;AAAA,EACL,SAAS,MAAM;AACjB,CAAC;AAEL,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;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,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,SAAS,MAAM,EAAE,CAAC;AAC9D,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,SAAS,MAAM,EAAE;AAAA,gBACjB,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'\n\nconst isTestEnv = typeof process !== 'undefined' && process.env.NODE_ENV === 'test'\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\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 [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(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={values}\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/20 p-3 sm:p-4 space-y-2 sm:space-y-3 transition hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary',\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 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
|
}
|
|
@@ -10,6 +10,7 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
|
10
10
|
import { cn } from "@open-mercato/shared/lib/utils";
|
|
11
11
|
import { LoadingMessage } from "./LoadingMessage.js";
|
|
12
12
|
import { mapCrudServerErrorToFormErrors } from "../utils/serverErrors.js";
|
|
13
|
+
import { MarkdownPreview } from "../markdown/index.js";
|
|
13
14
|
function resolveInlineErrorMessage(err, fallbackMessage) {
|
|
14
15
|
const { message, fieldErrors } = mapCrudServerErrorToFormErrors(err);
|
|
15
16
|
const firstFieldError = fieldErrors ? Object.values(fieldErrors).find((text) => typeof text === "string" && text.trim().length) : null;
|
|
@@ -304,10 +305,6 @@ const MarkdownEditorComponent = isTestEnv ? MarkdownEditorTestStub : dynamic(()
|
|
|
304
305
|
ssr: false,
|
|
305
306
|
loading: () => /* @__PURE__ */ jsx(MarkdownEditorFallback, {})
|
|
306
307
|
});
|
|
307
|
-
const MarkdownPreviewComponent = isTestEnv ? ({ children, className }) => /* @__PURE__ */ jsx("div", { className, children }) : dynamic(() => import("react-markdown").then((mod) => mod.default), {
|
|
308
|
-
ssr: false,
|
|
309
|
-
loading: () => null
|
|
310
|
-
});
|
|
311
308
|
let markdownPluginsPromise = null;
|
|
312
309
|
async function loadMarkdownPlugins() {
|
|
313
310
|
if (isTestEnv) return [];
|
|
@@ -559,7 +556,7 @@ function InlineMultilineEditor({
|
|
|
559
556
|
activateOnClick && !editing ? "cursor-pointer" : null
|
|
560
557
|
),
|
|
561
558
|
children: renderDisplay ? renderDisplay({ value, emptyLabel }) : value && value.length ? /* @__PURE__ */ jsx(
|
|
562
|
-
|
|
559
|
+
MarkdownPreview,
|
|
563
560
|
{
|
|
564
561
|
remarkPlugins: markdownPlugins,
|
|
565
562
|
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",
|
|
@@ -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 dynamic from 'next/dynamic'\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 { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { LoadingMessage } from './LoadingMessage'\nimport { mapCrudServerErrorToFormErrors } from '../utils/serverErrors'\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/20 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 {resolvedType === 'tel' ? (\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 <input\n className=\"w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus: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 {error ? <p className=\"text-xs text-destructive\">{error}</p> : null}\n {renderBelowInput ? renderBelowInput({ draft, resolvedType, error, saving }) : 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 {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 </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\ntype UiMarkdownEditorProps = {\n value?: string\n height?: number\n onChange?: (value?: string) => void\n previewOptions?: { remarkPlugins?: unknown[] }\n}\n\ntype MarkdownPreviewProps = {\n children: string\n className?: string\n remarkPlugins?: PluggableList\n}\n\nconst isTestEnv = typeof process !== 'undefined' && process.env.NODE_ENV === 'test'\n\nfunction MarkdownEditorFallback() {\n const t = useT()\n return (\n <LoadingMessage label={t('ui.detail.inline.editorLoading', 'Loading editor\u2026')} className=\"min-h-[200px] justify-center\" />\n )\n}\n\nconst MarkdownEditorTestStub: React.ComponentType<UiMarkdownEditorProps> = ({ value, onChange }) => (\n <Textarea\n data-testid=\"markdown-editor\"\n rows={8}\n value={value ?? ''}\n onChange={(event) => onChange?.(event.target.value)}\n />\n)\n\nconst MarkdownEditorComponent: React.ComponentType<UiMarkdownEditorProps> = isTestEnv\n ? MarkdownEditorTestStub\n : (dynamic(() => import('@uiw/react-md-editor'), {\n ssr: false,\n loading: () => <MarkdownEditorFallback />,\n }) as unknown as React.ComponentType<UiMarkdownEditorProps>)\n\nconst MarkdownPreviewComponent: React.ComponentType<MarkdownPreviewProps> = isTestEnv\n ? ({ children, className }) => <div className={className}>{children}</div>\n : (dynamic(() => import('react-markdown').then((mod) => mod.default as React.ComponentType<MarkdownPreviewProps>), {\n ssr: false,\n loading: () => null,\n }) as unknown as React.ComponentType<MarkdownPreviewProps>)\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 markdownEditorRef = React.useRef<HTMLDivElement | 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 if (isMarkdownEnabled) {\n const element = markdownEditorRef.current?.querySelector('textarea')\n if (!element) return\n window.requestAnimationFrame(() => {\n element.focus()\n })\n return\n }\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/20' : 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 {isMarkdownEnabled ? (\n <div\n ref={markdownEditorRef}\n className={cn(\n 'w-full rounded-md border border-muted-foreground/30 bg-background p-2',\n saving ? 'pointer-events-none opacity-75' : null,\n )}\n >\n <div data-color-mode=\"light\" className=\"w-full\">\n <MarkdownEditorComponent\n value={draft}\n height={220}\n onChange={(nextValue) => {\n if (error) setError(null)\n setDraft(typeof nextValue === 'string' ? nextValue : '')\n }}\n previewOptions={{ remarkPlugins: markdownPlugins }}\n />\n </div>\n </div>\n ) : (\n <Textarea\n ref={textareaRef}\n rows={3}\n className=\"w-full resize-none overflow-hidden rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus: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 )}\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 <MarkdownPreviewComponent\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 </MarkdownPreviewComponent>\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 className=\"w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring\"\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n >\n <option value=\"\">{t('ui.detail.inline.select.placeholder', 'Not set')}</option>\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\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 dynamic from 'next/dynamic'\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 { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\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/20 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 {resolvedType === 'tel' ? (\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 <input\n className=\"w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus: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 {error ? <p className=\"text-xs text-destructive\">{error}</p> : null}\n {renderBelowInput ? renderBelowInput({ draft, resolvedType, error, saving }) : 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 {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 </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\ntype UiMarkdownEditorProps = {\n value?: string\n height?: number\n onChange?: (value?: string) => void\n previewOptions?: { remarkPlugins?: unknown[] }\n}\n\nconst isTestEnv = typeof process !== 'undefined' && process.env.NODE_ENV === 'test'\n\nfunction MarkdownEditorFallback() {\n const t = useT()\n return (\n <LoadingMessage label={t('ui.detail.inline.editorLoading', 'Loading editor\u2026')} className=\"min-h-[200px] justify-center\" />\n )\n}\n\nconst MarkdownEditorTestStub: React.ComponentType<UiMarkdownEditorProps> = ({ value, onChange }) => (\n <Textarea\n data-testid=\"markdown-editor\"\n rows={8}\n value={value ?? ''}\n onChange={(event) => onChange?.(event.target.value)}\n />\n)\n\nconst MarkdownEditorComponent: React.ComponentType<UiMarkdownEditorProps> = isTestEnv\n ? MarkdownEditorTestStub\n : (dynamic(() => import('@uiw/react-md-editor'), {\n ssr: false,\n loading: () => <MarkdownEditorFallback />,\n }) as unknown as React.ComponentType<UiMarkdownEditorProps>)\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 markdownEditorRef = React.useRef<HTMLDivElement | 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 if (isMarkdownEnabled) {\n const element = markdownEditorRef.current?.querySelector('textarea')\n if (!element) return\n window.requestAnimationFrame(() => {\n element.focus()\n })\n return\n }\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/20' : 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 {isMarkdownEnabled ? (\n <div\n ref={markdownEditorRef}\n className={cn(\n 'w-full rounded-md border border-muted-foreground/30 bg-background p-2',\n saving ? 'pointer-events-none opacity-75' : null,\n )}\n >\n <div data-color-mode=\"light\" className=\"w-full\">\n <MarkdownEditorComponent\n value={draft}\n height={220}\n onChange={(nextValue) => {\n if (error) setError(null)\n setDraft(typeof nextValue === 'string' ? nextValue : '')\n }}\n previewOptions={{ remarkPlugins: markdownPlugins }}\n />\n </div>\n </div>\n ) : (\n <Textarea\n ref={textareaRef}\n rows={3}\n className=\"w-full resize-none overflow-hidden rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus: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 )}\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 className=\"w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring\"\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n >\n <option value=\"\">{t('ui.detail.inline.select.placeholder', 'Not set')}</option>\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\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": ["element"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import dynamic from "next/dynamic";
|
|
5
4
|
import { AppearanceDialog } from "@open-mercato/core/modules/customers/components/detail/AppearanceDialog";
|
|
6
5
|
import { ArrowUpRightSquare, FileCode, Loader2, Palette, Pencil, Plus, Trash2 } from "lucide-react";
|
|
7
6
|
import { formatRelativeTime } from "@open-mercato/shared/lib/time";
|
|
@@ -14,12 +13,9 @@ import { TabEmptyState } from "./TabEmptyState.js";
|
|
|
14
13
|
import { useConfirmDialog } from "../confirm-dialog/index.js";
|
|
15
14
|
import { formatDateTime } from "@open-mercato/shared/lib/time";
|
|
16
15
|
import { ComponentReplacementHandles } from "@open-mercato/shared/modules/widgets/component-registry";
|
|
16
|
+
import { MarkdownPreview } from "../markdown/index.js";
|
|
17
17
|
import { useRegisteredComponent } from "../injection/useRegisteredComponent.js";
|
|
18
18
|
const isTestEnv = typeof process !== "undefined" && process.env.NODE_ENV === "test";
|
|
19
|
-
const MarkdownPreviewComponent = isTestEnv ? ({ children, className }) => /* @__PURE__ */ jsx("div", { className, children }) : dynamic(() => import("react-markdown").then((mod) => mod.default), {
|
|
20
|
-
ssr: false,
|
|
21
|
-
loading: () => null
|
|
22
|
-
});
|
|
23
19
|
let markdownPluginsPromise = null;
|
|
24
20
|
async function loadMarkdownPlugins() {
|
|
25
21
|
if (isTestEnv) return [];
|
|
@@ -957,7 +953,7 @@ function NotesSectionImpl({
|
|
|
957
953
|
onClick: () => setContentEditor({ id: note.id, value: note.body }),
|
|
958
954
|
onKeyDown: (event) => handleContentKeyDown(event, note),
|
|
959
955
|
children: /* @__PURE__ */ jsx(
|
|
960
|
-
|
|
956
|
+
MarkdownPreview,
|
|
961
957
|
{
|
|
962
958
|
remarkPlugins: markdownPlugins,
|
|
963
959
|
className: "break-words text-foreground [&>*]: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",
|