@open-mercato/ui 0.6.8-develop.7005.1.70a5747c3e → 0.6.8-develop.7012.1.1dbd6f5fbd
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.
|
@@ -134,7 +134,10 @@ function NotesSectionImpl({
|
|
|
134
134
|
iconSuggestions,
|
|
135
135
|
readMarkdownPreference,
|
|
136
136
|
writeMarkdownPreference,
|
|
137
|
-
disableMarkdown
|
|
137
|
+
disableMarkdown,
|
|
138
|
+
forceMarkdown,
|
|
139
|
+
hideMarkdownToggle,
|
|
140
|
+
disableAppearance
|
|
138
141
|
}) {
|
|
139
142
|
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
140
143
|
const t = React.useMemo(() => translator ?? ((key, fallback) => fallback ?? key), [translator]);
|
|
@@ -246,13 +249,16 @@ function NotesSectionImpl({
|
|
|
246
249
|
const [draftIcon, setDraftIcon] = React.useState(null);
|
|
247
250
|
const [draftColor, setDraftColor] = React.useState(null);
|
|
248
251
|
const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(false);
|
|
252
|
+
const isMarkdownActive = !disableMarkdown && (Boolean(forceMarkdown) || isMarkdownEnabled);
|
|
253
|
+
const showMarkdownToggle = !disableMarkdown && !forceMarkdown && !hideMarkdownToggle;
|
|
254
|
+
const showAppearanceControls = !disableAppearance;
|
|
249
255
|
const textareaRef = React.useRef(null);
|
|
250
256
|
const formRef = React.useRef(null);
|
|
251
257
|
const focusComposer = React.useCallback(() => {
|
|
252
258
|
if (!hasEntity) return;
|
|
253
259
|
setComposerOpen(true);
|
|
254
260
|
window.requestAnimationFrame(() => {
|
|
255
|
-
if (
|
|
261
|
+
if (isMarkdownActive) {
|
|
256
262
|
const markdownTextarea = formRef.current?.querySelector("textarea");
|
|
257
263
|
if (markdownTextarea instanceof HTMLTextAreaElement) {
|
|
258
264
|
markdownTextarea.focus();
|
|
@@ -265,7 +271,7 @@ function NotesSectionImpl({
|
|
|
265
271
|
element.focus();
|
|
266
272
|
element.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
267
273
|
});
|
|
268
|
-
}, [formRef, hasEntity,
|
|
274
|
+
}, [formRef, hasEntity, isMarkdownActive]);
|
|
269
275
|
const [appearanceDialogState, setAppearanceDialogState] = React.useState(null);
|
|
270
276
|
const [appearanceDialogSaving, setAppearanceDialogSaving] = React.useState(false);
|
|
271
277
|
const [appearanceDialogError, setAppearanceDialogError] = React.useState(null);
|
|
@@ -369,7 +375,7 @@ function NotesSectionImpl({
|
|
|
369
375
|
}, []);
|
|
370
376
|
React.useEffect(() => {
|
|
371
377
|
adjustTextareaSize(textareaRef.current);
|
|
372
|
-
}, [adjustTextareaSize, draftBody,
|
|
378
|
+
}, [adjustTextareaSize, draftBody, isMarkdownActive, composerOpen]);
|
|
373
379
|
React.useEffect(() => {
|
|
374
380
|
const preference = readMarkdownPreference ? readMarkdownPreference() : null;
|
|
375
381
|
if (preference !== null) {
|
|
@@ -731,7 +737,7 @@ function NotesSectionImpl({
|
|
|
731
737
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
732
738
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: label("addLabel") }),
|
|
733
739
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
734
|
-
/* @__PURE__ */ jsxs(
|
|
740
|
+
showAppearanceControls ? /* @__PURE__ */ jsxs(
|
|
735
741
|
Button,
|
|
736
742
|
{
|
|
737
743
|
type: "button",
|
|
@@ -747,19 +753,19 @@ function NotesSectionImpl({
|
|
|
747
753
|
/* @__PURE__ */ jsx(Palette, { className: "h-4 w-4" })
|
|
748
754
|
]
|
|
749
755
|
}
|
|
750
|
-
),
|
|
751
|
-
|
|
756
|
+
) : null,
|
|
757
|
+
showMarkdownToggle ? /* @__PURE__ */ jsx(
|
|
752
758
|
Button,
|
|
753
759
|
{
|
|
754
760
|
type: "button",
|
|
755
|
-
variant:
|
|
761
|
+
variant: isMarkdownActive ? "secondary" : "ghost",
|
|
756
762
|
size: "icon",
|
|
757
763
|
onClick: handleMarkdownToggle,
|
|
758
|
-
"aria-pressed":
|
|
764
|
+
"aria-pressed": isMarkdownActive,
|
|
759
765
|
disabled: isSubmitting || isLoading,
|
|
760
766
|
children: /* @__PURE__ */ jsx(FileCode, { className: "h-4 w-4" })
|
|
761
767
|
}
|
|
762
|
-
),
|
|
768
|
+
) : null,
|
|
763
769
|
/* @__PURE__ */ jsx(
|
|
764
770
|
Button,
|
|
765
771
|
{
|
|
@@ -829,7 +835,7 @@ function NotesSectionImpl({
|
|
|
829
835
|
{
|
|
830
836
|
value: draftBody,
|
|
831
837
|
onChange: setDraftBody,
|
|
832
|
-
isMarkdownEnabled,
|
|
838
|
+
isMarkdownEnabled: isMarkdownActive,
|
|
833
839
|
disableMarkdown,
|
|
834
840
|
rows: 1,
|
|
835
841
|
placeholder: label("placeholder"),
|
|
@@ -952,7 +958,7 @@ function NotesSectionImpl({
|
|
|
952
958
|
children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
|
|
953
959
|
}
|
|
954
960
|
),
|
|
955
|
-
/* @__PURE__ */ jsx(
|
|
961
|
+
showAppearanceControls ? /* @__PURE__ */ jsx(
|
|
956
962
|
Button,
|
|
957
963
|
{
|
|
958
964
|
type: "button",
|
|
@@ -971,7 +977,7 @@ function NotesSectionImpl({
|
|
|
971
977
|
disabled: appearanceDialogSaving && editingAppearanceNoteId === note.id,
|
|
972
978
|
children: isAppearanceSaving ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Palette, { className: "h-4 w-4" })
|
|
973
979
|
}
|
|
974
|
-
),
|
|
980
|
+
) : null,
|
|
975
981
|
/* @__PURE__ */ jsx(
|
|
976
982
|
Button,
|
|
977
983
|
{
|
|
@@ -996,7 +1002,7 @@ function NotesSectionImpl({
|
|
|
996
1002
|
{
|
|
997
1003
|
value: contentEditor.value,
|
|
998
1004
|
onChange: (nextValue) => setContentEditor((prev) => ({ ...prev, value: nextValue })),
|
|
999
|
-
isMarkdownEnabled,
|
|
1005
|
+
isMarkdownEnabled: isMarkdownActive,
|
|
1000
1006
|
disableMarkdown,
|
|
1001
1007
|
rows: 3,
|
|
1002
1008
|
textareaRef: contentTextareaRef,
|
|
@@ -1012,19 +1018,19 @@ function NotesSectionImpl({
|
|
|
1012
1018
|
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
1013
1019
|
label("saving")
|
|
1014
1020
|
] }) : inlineLabel("saveShortcut") }),
|
|
1015
|
-
|
|
1021
|
+
showMarkdownToggle ? /* @__PURE__ */ jsx(
|
|
1016
1022
|
Button,
|
|
1017
1023
|
{
|
|
1018
1024
|
type: "button",
|
|
1019
1025
|
variant: "ghost",
|
|
1020
1026
|
size: "icon",
|
|
1021
1027
|
onClick: handleMarkdownToggle,
|
|
1022
|
-
"aria-pressed":
|
|
1023
|
-
className:
|
|
1028
|
+
"aria-pressed": isMarkdownActive,
|
|
1029
|
+
className: isMarkdownActive ? "text-primary" : void 0,
|
|
1024
1030
|
disabled: contentSavingId === note.id,
|
|
1025
1031
|
children: /* @__PURE__ */ jsx(FileCode, { className: "h-4 w-4" })
|
|
1026
1032
|
}
|
|
1027
|
-
),
|
|
1033
|
+
) : null,
|
|
1028
1034
|
/* @__PURE__ */ jsx(
|
|
1029
1035
|
Button,
|
|
1030
1036
|
{
|
|
@@ -1070,7 +1076,7 @@ function NotesSectionImpl({
|
|
|
1070
1076
|
),
|
|
1071
1077
|
isLoading || (pagedMode ? !hasMore : visibleCount >= notes.length) ? null : /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleLoadMore, children: loadMoreLabel }) })
|
|
1072
1078
|
] }),
|
|
1073
|
-
/* @__PURE__ */ jsx(
|
|
1079
|
+
showAppearanceControls ? /* @__PURE__ */ jsx(
|
|
1074
1080
|
AppearanceDialog,
|
|
1075
1081
|
{
|
|
1076
1082
|
open: appearanceDialogOpen,
|
|
@@ -1091,7 +1097,7 @@ function NotesSectionImpl({
|
|
|
1091
1097
|
savingLabel: appearanceDialogSavingLabel,
|
|
1092
1098
|
cancelLabel: label("appearance.cancel")
|
|
1093
1099
|
}
|
|
1094
|
-
),
|
|
1100
|
+
) : null,
|
|
1095
1101
|
ConfirmDialogElement
|
|
1096
1102
|
] });
|
|
1097
1103
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/detail/NotesSection.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { PluggableList } from 'unified'\nimport type { AppearanceSelectorLabels } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport { AppearanceDialog } from '@open-mercato/core/modules/customers/components/detail/AppearanceDialog'\nimport type { IconOption } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { ArrowUpRightSquare, FileCode, Loader2, Palette, Pencil, Plus, Trash2 } from 'lucide-react'\nimport { formatRelativeTime } from '@open-mercato/shared/lib/time'\nimport { hasMoreFromPage } from '@open-mercato/shared/lib/pagination/load-more'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { flash } from '../FlashMessages'\nimport { surfaceRecordConflict } from '../conflicts'\nimport { SwitchableMarkdownInput } from '../inputs/SwitchableMarkdownInput'\nimport { ErrorMessage } from './ErrorMessage'\nimport { LoadingMessage } from './LoadingMessage'\nimport { TabEmptyState } from './TabEmptyState'\nimport { useConfirmDialog } from '../confirm-dialog'\nimport { formatDateTime } from '@open-mercato/shared/lib/time'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { MarkdownPreview } from '../markdown'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\n\nconst NOTES_PAGE_SIZE = 20\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nexport type SectionAction = {\n label: React.ReactNode\n onClick: () => void\n disabled?: boolean\n icon?: React.ReactNode\n}\n\nexport type TabEmptyStateConfig = {\n title: string\n actionLabel: string\n description?: string\n}\n\nexport type CommentSummary = {\n id: string\n body: string\n createdAt: string\n updatedAt?: string | null\n authorUserId?: string | null\n authorName?: string | null\n authorEmail?: string | null\n dealId?: string | null\n dealTitle?: string | null\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\nexport type NotesCreatePayload = {\n entityId: string\n body: string\n appearanceIcon: string | null\n appearanceColor: string | null\n dealId?: string | null\n}\n\nexport type NotesUpdatePayload = {\n body?: string\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\nexport type NotesDataAdapter<C = unknown> = {\n list: (params: { entityId: string | null; dealId: string | null; context?: C }) => Promise<CommentSummary[]>\n listPage?: (params: {\n entityId: string | null\n dealId: string | null\n page: number\n pageSize: number\n context?: C\n }) => Promise<{\n items: CommentSummary[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n }>\n create: (params: NotesCreatePayload & { context?: C }) => Promise<Partial<CommentSummary> | void>\n update: (params: { id: string; patch: NotesUpdatePayload; updatedAt?: string | null; context?: C }) => Promise<void>\n delete: (params: { id: string; updatedAt?: string | null; context?: C }) => Promise<void>\n}\n\ntype RenderIconFn = (icon: string, className?: string) => React.ReactNode\ntype RenderColorFn = (color: string, className?: string) => React.ReactNode\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\nfunction generateTempId() {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID()\n return `tmp_${Math.random().toString(36).slice(2)}`\n}\n\n\n\ntype TimelineItemHeaderProps = {\n title: React.ReactNode\n subtitle?: React.ReactNode\n timestamp?: string | Date | null\n fallbackTimestampLabel?: React.ReactNode\n icon?: string | null\n color?: string | null\n iconSize?: 'sm' | 'md'\n className?: string\n renderIcon?: RenderIconFn\n renderColor?: RenderColorFn\n}\n\nfunction TimelineItemHeader({\n title,\n subtitle,\n timestamp,\n fallbackTimestampLabel,\n icon,\n color,\n iconSize = 'md',\n className,\n renderIcon,\n renderColor,\n}: TimelineItemHeaderProps) {\n const wrapperSize = iconSize === 'sm' ? 'h-6 w-6' : 'h-8 w-8'\n const iconSizeClass = iconSize === 'sm' ? 'h-3.5 w-3.5' : 'h-4 w-4'\n const resolvedTimestamp = React.useMemo(() => {\n if (subtitle) return subtitle\n if (!timestamp) return fallbackTimestampLabel ?? null\n const value = typeof timestamp === 'string' ? timestamp : timestamp.toISOString()\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallbackTimestampLabel ?? null\n const now = Date.now()\n const diff = Math.abs(now - date.getTime())\n const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000\n const relativeLabel = diff <= THIRTY_DAYS_MS ? formatRelativeTime(value) : null\n const absoluteLabel = formatDateTime(value)\n if (relativeLabel) {\n return (\n <span title={absoluteLabel ?? undefined}>\n {relativeLabel}\n </span>\n )\n }\n return absoluteLabel ?? fallbackTimestampLabel ?? null\n }, [fallbackTimestampLabel, subtitle, timestamp])\n\n return (\n <div className={['flex items-start gap-3', className].filter(Boolean).join(' ')}>\n {icon && renderIcon ? (\n <span className={['inline-flex items-center justify-center rounded border border-border bg-muted/50', wrapperSize].join(' ')}>\n {renderIcon(icon, iconSizeClass)}\n </span>\n ) : null}\n <div className=\"space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">{title}</span>\n {color && renderColor ? renderColor(color, 'h-3 w-3 rounded-full border border-border') : null}\n </div>\n {resolvedTimestamp ? <div className=\"text-xs text-muted-foreground\">{resolvedTimestamp}</div> : null}\n </div>\n </div>\n )\n}\n\nexport type NotesSectionProps<C = unknown> = {\n entityId: string | null\n dealId?: string | null\n emptyLabel: string\n viewerUserId: string | null\n viewerName?: string | null\n viewerEmail?: string | null\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n translator?: Translator\n labelPrefix?: string\n inlineLabelPrefix?: string\n onLoadingChange?: (isLoading: boolean) => void\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n dataAdapter: NotesDataAdapter<C>\n dataContext?: C\n renderIcon?: RenderIconFn\n renderColor?: RenderColorFn\n iconSuggestions?: IconOption[]\n readMarkdownPreference?: () => boolean | null\n writeMarkdownPreference?: (value: boolean) => void\n disableMarkdown?: boolean\n}\n\nexport function sanitizeHexColor(value: string | null): string | null {\n if (!value) return null\n const trimmed = value.trim()\n return /^#([0-9a-f]{6})$/i.test(trimmed) ? trimmed.toLowerCase() : null\n}\n\nexport function mapCommentSummary(input: unknown): CommentSummary {\n const data = (typeof input === 'object' && input !== null ? input : {}) as Record<string, unknown>\n const id = typeof data.id === 'string' ? data.id : generateTempId()\n const body = typeof data.body === 'string' ? data.body : ''\n const createdAt =\n typeof data.createdAt === 'string'\n ? data.createdAt\n : typeof data.created_at === 'string'\n ? data.created_at\n : new Date().toISOString()\n const updatedAt =\n typeof data.updatedAt === 'string'\n ? data.updatedAt\n : typeof data.updated_at === 'string'\n ? data.updated_at\n : null\n const authorUserId =\n typeof data.authorUserId === 'string'\n ? data.authorUserId\n : typeof data.author_user_id === 'string'\n ? data.author_user_id\n : null\n const authorName =\n typeof data.authorName === 'string'\n ? data.authorName\n : typeof data.author_name === 'string'\n ? data.author_name\n : null\n const authorEmail =\n typeof data.authorEmail === 'string'\n ? data.authorEmail\n : typeof data.author_email === 'string'\n ? data.author_email\n : null\n const dealId =\n typeof data.dealId === 'string'\n ? data.dealId\n : typeof data.deal_id === 'string'\n ? data.deal_id\n : null\n const dealTitle =\n typeof data.dealTitle === 'string'\n ? data.dealTitle\n : typeof data.deal_title === 'string'\n ? data.deal_title\n : null\n const appearanceIcon =\n typeof data.appearanceIcon === 'string'\n ? data.appearanceIcon\n : typeof data.appearance_icon === 'string'\n ? data.appearance_icon\n : null\n const appearanceColor =\n typeof data.appearanceColor === 'string'\n ? data.appearanceColor\n : typeof data.appearance_color === 'string'\n ? data.appearance_color\n : null\n return {\n id,\n body,\n createdAt,\n updatedAt,\n authorUserId,\n authorName,\n authorEmail,\n dealId,\n dealTitle,\n appearanceIcon,\n appearanceColor,\n }\n}\n\nfunction NotesSectionImpl<C = unknown>({\n entityId,\n dealId,\n emptyLabel,\n viewerUserId,\n viewerName,\n viewerEmail,\n addActionLabel,\n emptyState,\n onActionChange,\n translator,\n labelPrefix = 'customers.people.detail.notes',\n inlineLabelPrefix = 'customers.people.detail.inline',\n onLoadingChange,\n dealOptions,\n entityOptions,\n dataAdapter,\n dataContext,\n renderIcon,\n renderColor,\n iconSuggestions,\n readMarkdownPreference,\n writeMarkdownPreference,\n disableMarkdown,\n}: NotesSectionProps<C>) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = React.useMemo<Translator>(() => translator ?? ((key, fallback) => fallback ?? key), [translator])\n const label = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback, params),\n [labelPrefix, t],\n )\n const inlineLabel = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${inlineLabelPrefix}.${suffix}`, fallback, params),\n [inlineLabelPrefix, t],\n )\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\n const normalizedDealOptions = React.useMemo(() => {\n if (!Array.isArray(dealOptions)) return []\n const seen = new Set<string>()\n return dealOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [dealOptions])\n\n const dealLabelMap = React.useMemo(() => {\n const map = new Map<string, string>()\n normalizedDealOptions.forEach((option) => {\n map.set(option.id, option.label)\n })\n return map\n }, [normalizedDealOptions])\n\n const normalizedEntityOptions = React.useMemo(() => {\n if (!Array.isArray(entityOptions)) return []\n const seen = new Set<string>()\n return entityOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [entityOptions])\n\n const [selectedDealId, setSelectedDealId] = React.useState<string>(() => {\n const initial = typeof dealId === 'string' ? dealId.trim() : ''\n return initial\n })\n React.useEffect(() => {\n const initial = typeof dealId === 'string' ? dealId.trim() : ''\n if (initial !== selectedDealId) {\n setSelectedDealId(initial)\n }\n }, [dealId, selectedDealId])\n\n const [selectedEntityId, setSelectedEntityId] = React.useState<string>(() => {\n if (normalizedEntityOptions.length) return normalizedEntityOptions[0].id\n return typeof entityId === 'string' ? entityId : ''\n })\n React.useEffect(() => {\n if (normalizedEntityOptions.length) {\n if (!normalizedEntityOptions.some((option) => option.id === selectedEntityId)) {\n setSelectedEntityId(normalizedEntityOptions[0].id)\n }\n } else {\n const initial = typeof entityId === 'string' ? entityId : ''\n if (initial !== selectedEntityId) {\n setSelectedEntityId(initial)\n }\n }\n }, [entityId, normalizedEntityOptions, selectedEntityId])\n\n const resolvedEntityId = React.useMemo(() => {\n if (normalizedEntityOptions.length) return selectedEntityId\n return typeof entityId === 'string' ? entityId : ''\n }, [entityId, normalizedEntityOptions, selectedEntityId])\n\n const resolvedDealId = React.useMemo(() => {\n const trimmed = typeof selectedDealId === 'string' ? selectedDealId.trim() : ''\n return trimmed\n }, [selectedDealId])\n\n const hasEntity = resolvedEntityId.length > 0\n\n const [notes, setNotes] = React.useState<CommentSummary[]>([])\n const [isLoading, setIsLoading] = React.useState<boolean>(() => Boolean(entityId || dealId))\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const pendingCounterRef = React.useRef(0)\n\n const pushLoading = React.useCallback(() => {\n pendingCounterRef.current += 1\n if (pendingCounterRef.current === 1) {\n onLoadingChange?.(true)\n }\n }, [onLoadingChange])\n\n const popLoading = React.useCallback(() => {\n pendingCounterRef.current = Math.max(0, pendingCounterRef.current - 1)\n if (pendingCounterRef.current === 0) {\n onLoadingChange?.(false)\n }\n }, [onLoadingChange])\n\n const [composerOpen, setComposerOpen] = React.useState(false)\n const [draftBody, setDraftBody] = React.useState('')\n const [draftIcon, setDraftIcon] = React.useState<string | null>(null)\n const [draftColor, setDraftColor] = React.useState<string | null>(null)\n const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(false)\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const formRef = React.useRef<HTMLFormElement | null>(null)\n const focusComposer = React.useCallback(() => {\n if (!hasEntity) return\n setComposerOpen(true)\n window.requestAnimationFrame(() => {\n if (isMarkdownEnabled) {\n const markdownTextarea = formRef.current?.querySelector('textarea')\n if (markdownTextarea instanceof HTMLTextAreaElement) {\n markdownTextarea.focus()\n markdownTextarea.scrollIntoView({ behavior: 'smooth', block: 'center' })\n return\n }\n }\n const element = textareaRef.current\n if (!element) return\n element.focus()\n element.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n }, [formRef, hasEntity, isMarkdownEnabled])\n const [appearanceDialogState, setAppearanceDialogState] = React.useState<\n | { mode: 'create'; icon: string | null; color: string | null }\n | { mode: 'edit'; noteId: string; icon: string | null; color: string | null }\n | null\n >(null)\n const [appearanceDialogSaving, setAppearanceDialogSaving] = React.useState(false)\n const [appearanceDialogError, setAppearanceDialogError] = React.useState<string | null>(null)\n const [contentEditor, setContentEditor] = React.useState<{ id: string; value: string }>({ id: '', value: '' })\n const [contentSavingId, setContentSavingId] = React.useState<string | null>(null)\n const [contentError, setContentError] = React.useState<string | null>(null)\n const contentTextareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [visibleCount, setVisibleCount] = React.useState(0)\n const [currentPage, setCurrentPage] = React.useState(1)\n // Short-page termination instead of `currentPage >= totalPages` \u2014 see\n // `hasMoreFromPage`. `/api/customers/comments` is a `makeCrudRoute` list, so\n // a page past the end comes back empty rather than clamped.\n const [hasMore, setHasMore] = React.useState(false)\n const [deletingNoteId, setDeletingNoteId] = React.useState<string | null>(null)\n const pagedMode = typeof dataAdapter.listPage === 'function'\n\n React.useEffect(() => {\n const queryEntityId = typeof entityId === 'string' ? entityId : ''\n const queryDealId = typeof dealId === 'string' ? dealId : ''\n if (!queryEntityId && !queryDealId) {\n setNotes([])\n setLoadError(null)\n setIsLoading(false)\n setCurrentPage(1)\n setHasMore(false)\n return\n }\n let cancelled = false\n setIsLoading(true)\n setLoadError(null)\n pushLoading()\n async function loadNotes() {\n try {\n if (dataAdapter.listPage) {\n const pageResult = await dataAdapter.listPage({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n page: 1,\n pageSize: NOTES_PAGE_SIZE,\n context: dataContext,\n })\n if (cancelled) return\n setNotes(pageResult.items)\n setCurrentPage(pageResult.page)\n setHasMore(hasMoreFromPage(pageResult.items.length, NOTES_PAGE_SIZE))\n return\n }\n const mapped = await dataAdapter.list({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n context: dataContext,\n })\n if (cancelled) return\n setNotes(mapped)\n setCurrentPage(1)\n setHasMore(false)\n } catch (err) {\n if (cancelled) return\n const message =\n err instanceof Error ? err.message : label('loadError', 'Failed to load notes.')\n setNotes([])\n setLoadError(message)\n setCurrentPage(1)\n setHasMore(false)\n flash(message, 'error')\n } finally {\n if (!cancelled) setIsLoading(false)\n popLoading()\n }\n }\n loadNotes().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [dataAdapter, dataContext, dealId, entityId, label, popLoading, pushLoading])\n\n const youLabel = label('you', 'You')\n const viewerLabel = React.useMemo(() => viewerName ?? viewerEmail ?? null, [viewerEmail, viewerName])\n\n const handleMarkdownToggle = React.useCallback(() => {\n setIsMarkdownEnabled((prev) => {\n const next = !prev\n if (writeMarkdownPreference) {\n writeMarkdownPreference(next)\n }\n return next\n })\n }, [writeMarkdownPreference])\n\n React.useEffect(() => {\n if (!onActionChange) return\n if (!notes.length) {\n onActionChange(null)\n return\n }\n onActionChange({\n label: addActionLabel,\n onClick: focusComposer,\n disabled: isSubmitting || isLoading || !hasEntity,\n icon: <Plus className=\"mr-2 h-4 w-4\" />,\n })\n return () => onActionChange(null)\n }, [onActionChange, addActionLabel, focusComposer, hasEntity, isLoading, isSubmitting, notes.length])\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, draftBody, isMarkdownEnabled, composerOpen])\n\n React.useEffect(() => {\n const preference = readMarkdownPreference ? readMarkdownPreference() : null\n if (preference !== null) {\n setIsMarkdownEnabled(preference)\n }\n }, [readMarkdownPreference])\n\n React.useEffect(() => {\n if (pagedMode) {\n setVisibleCount(notes.length)\n return\n }\n if (!notes.length) {\n setVisibleCount(0)\n return\n }\n const baseline = Math.min(5, notes.length)\n setVisibleCount((prev) => {\n if (prev >= notes.length) return prev\n return Math.min(Math.max(prev, baseline), notes.length)\n })\n }, [notes.length, pagedMode])\n\n React.useEffect(() => {\n if (hasEntity) return\n setComposerOpen(false)\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }, [hasEntity])\n\n const visibleNotes = React.useMemo(\n () => (pagedMode ? notes : notes.slice(0, visibleCount)),\n [notes, pagedMode, visibleCount],\n )\n const hasVisibleNotes = React.useMemo(\n () => (pagedMode ? notes.length > 0 : visibleCount > 0),\n [notes.length, pagedMode, visibleCount],\n )\n\n const loadMoreLabel = label('loadMore')\n\n const handleCreateNote = React.useCallback(\n async (input: { body: string; appearanceIcon: string | null; appearanceColor: string | null }) => {\n if (!hasEntity || !resolvedEntityId) {\n flash(label('entityMissing', 'Unable to determine current person.'), 'error')\n return false\n }\n const body = input.body.trim()\n const strippedBody = body\n .replace(/^[\\s#\\-*>_~`|+\\\\\\n\\r]+$/gm, '')\n .replace(/\\s+/g, '')\n if (!body || !strippedBody.length) {\n focusComposer()\n return false\n }\n const icon = input.appearanceIcon && input.appearanceIcon.trim().length ? input.appearanceIcon.trim() : null\n const color = sanitizeHexColor(input.appearanceColor)\n const targetDealId = resolvedDealId.length ? resolvedDealId : null\n const dealLabel = targetDealId ? dealLabelMap.get(targetDealId) ?? null : null\n setIsSubmitting(true)\n pushLoading()\n try {\n const responseBody =\n (await dataAdapter.create({\n entityId: resolvedEntityId,\n body,\n appearanceIcon: icon,\n appearanceColor: color,\n dealId: targetDealId,\n context: dataContext,\n })) ?? {}\n setNotes((prev) => {\n const viewerId = viewerUserId ?? null\n const resolvedAuthorId =\n typeof responseBody?.authorUserId === 'string' ? responseBody.authorUserId : viewerId ?? null\n const resolvedAuthorName = (() => {\n if (resolvedAuthorId && viewerId && resolvedAuthorId === viewerId) {\n return youLabel\n }\n return typeof responseBody?.authorName === 'string' ? responseBody.authorName : viewerLabel\n })()\n const resolvedAuthorEmail = (() => {\n if (resolvedAuthorId && viewerId && resolvedAuthorId === viewerId) {\n return viewerEmail ?? null\n }\n return typeof responseBody?.authorEmail === 'string' ? responseBody.authorEmail : null\n })()\n const newNote: CommentSummary = {\n id: typeof responseBody?.id === 'string' ? responseBody.id : generateTempId(),\n body,\n createdAt: new Date().toISOString(),\n authorUserId: resolvedAuthorId,\n authorName: resolvedAuthorName,\n authorEmail: resolvedAuthorEmail,\n dealId: targetDealId,\n dealTitle: dealLabel,\n appearanceIcon: icon,\n appearanceColor: color,\n }\n return [newNote, ...prev]\n })\n setVisibleCount((prev) => Math.max(prev, 1))\n flash(label('success'), 'success')\n return true\n } catch (err) {\n const message = err instanceof Error ? err.message : label('error')\n if (!surfaceRecordConflict(err, t)) flash(message, 'error')\n return false\n } finally {\n setIsSubmitting(false)\n popLoading()\n }\n },\n [dataAdapter, dataContext, dealLabelMap, focusComposer, hasEntity, popLoading, pushLoading, resolvedDealId, resolvedEntityId, t, viewerEmail, viewerLabel, viewerUserId, youLabel],\n )\n\n const handleUpdateNote = React.useCallback(\n async (noteId: string, patch: { body?: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const sanitizedBody = patch.body\n const sanitizedIcon =\n patch.appearanceIcon !== undefined && patch.appearanceIcon !== null && patch.appearanceIcon.trim().length\n ? patch.appearanceIcon.trim()\n : patch.appearanceIcon === null\n ? null\n : undefined\n const sanitizedColor =\n patch.appearanceColor !== undefined ? sanitizeHexColor(patch.appearanceColor ?? null) : undefined\n const noteUpdatedAt = notes.find((comment) => comment.id === noteId)?.updatedAt ?? null\n try {\n await dataAdapter.update({\n id: noteId,\n patch: {\n body: sanitizedBody,\n appearanceIcon: sanitizedIcon,\n appearanceColor: sanitizedColor,\n },\n updatedAt: noteUpdatedAt,\n context: dataContext,\n })\n setNotes((prev) => {\n const nextComments = prev.map((comment) => {\n if (comment.id !== noteId) return comment\n const next = { ...comment }\n if (sanitizedBody !== undefined) next.body = sanitizedBody\n if (sanitizedIcon !== undefined) next.appearanceIcon = sanitizedIcon ?? null\n if (sanitizedColor !== undefined) next.appearanceColor = sanitizedColor ?? null\n return next\n })\n return nextComments\n })\n flash(label('updateSuccess'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : label('updateError')\n if (!surfaceRecordConflict(error, t)) flash(message, 'error')\n throw error instanceof Error ? error : new Error(message)\n }\n },\n [dataAdapter, dataContext, notes, t],\n )\n\n const handleDeleteNote = React.useCallback(\n async (note: CommentSummary) => {\n const confirmed = await confirm({\n title: label('deleteConfirm', 'Delete this note? You can restore it using version history.'),\n variant: 'destructive',\n })\n if (!confirmed) return\n setDeletingNoteId(note.id)\n pushLoading()\n try {\n await dataAdapter.delete({ id: note.id, updatedAt: note.updatedAt ?? null, context: dataContext })\n setNotes((prev) => prev.filter((existing) => existing.id !== note.id))\n if (pagedMode) {\n setVisibleCount((prev) => Math.max(0, prev - 1))\n }\n flash(label('deleteSuccess', 'Note deleted'), 'success')\n } catch (err) {\n const message = err instanceof Error ? err.message : label('deleteError', 'Failed to delete note')\n if (!surfaceRecordConflict(err, t)) flash(message, 'error')\n } finally {\n setDeletingNoteId(null)\n popLoading()\n }\n },\n [confirm, dataAdapter, dataContext, label, popLoading, pushLoading, t],\n )\n\n const handleSubmit = React.useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n const created = await handleCreateNote({\n body: draftBody,\n appearanceIcon: draftIcon,\n appearanceColor: draftColor,\n })\n if (created) {\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }\n },\n [draftBody, draftColor, draftIcon, handleCreateNote],\n )\n\n const handleLoadMore = React.useCallback(() => {\n if (pagedMode && dataAdapter.listPage) {\n if (!hasMore || isLoading) return\n const queryEntityId = typeof entityId === 'string' ? entityId : ''\n const queryDealId = typeof dealId === 'string' ? dealId : ''\n setIsLoading(true)\n pushLoading()\n void dataAdapter.listPage({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n page: currentPage + 1,\n pageSize: NOTES_PAGE_SIZE,\n context: dataContext,\n })\n .then((pageResult) => {\n setNotes((prev) => {\n const merged = new Map(prev.map((note) => [note.id, note]))\n pageResult.items.forEach((note) => merged.set(note.id, note))\n return Array.from(merged.values())\n })\n setCurrentPage(pageResult.page)\n setHasMore(hasMoreFromPage(pageResult.items.length, NOTES_PAGE_SIZE))\n })\n .catch((error) => {\n const message =\n error instanceof Error ? error.message : label('loadError', 'Failed to load notes.')\n flash(message, 'error')\n })\n .finally(() => {\n setIsLoading(false)\n popLoading()\n })\n return\n }\n setVisibleCount((prev) => {\n if (prev >= notes.length) return prev\n return Math.min(prev + 5, notes.length)\n })\n }, [currentPage, dataAdapter, dataContext, dealId, entityId, flash, hasMore, isLoading, label, notes.length, pagedMode, popLoading, pushLoading])\n\n const handleAppearanceDialogSubmit = React.useCallback(async () => {\n if (!appearanceDialogState) return\n setAppearanceDialogError(null)\n const sanitizedIcon =\n appearanceDialogState.icon && appearanceDialogState.icon.trim().length\n ? appearanceDialogState.icon.trim()\n : null\n const sanitizedColor = sanitizeHexColor(appearanceDialogState.color ?? null)\n if (appearanceDialogState.mode === 'create') {\n setDraftIcon(sanitizedIcon)\n setDraftColor(sanitizedColor)\n setAppearanceDialogState(null)\n return\n }\n setAppearanceDialogSaving(true)\n try {\n await handleUpdateNote(appearanceDialogState.noteId, {\n appearanceIcon: sanitizedIcon,\n appearanceColor: sanitizedColor,\n })\n setAppearanceDialogState(null)\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : label('appearance.error', 'Failed to update appearance.')\n setAppearanceDialogError(message)\n } finally {\n setAppearanceDialogSaving(false)\n }\n }, [appearanceDialogState, handleUpdateNote, t])\n\n const handleAppearanceDialogClose = React.useCallback(() => {\n if (appearanceDialogSaving) return\n setAppearanceDialogState(null)\n setAppearanceDialogError(null)\n }, [appearanceDialogSaving])\n\n const handleContentSave = React.useCallback(async () => {\n if (!contentEditor.id) return\n const trimmed = contentEditor.value.trim()\n if (!trimmed) {\n setContentError(label('updateError', 'Failed to update note'))\n return\n }\n setContentSavingId(contentEditor.id)\n setContentError(null)\n try {\n await handleUpdateNote(contentEditor.id, { body: trimmed })\n setContentEditor({ id: '', value: '' })\n } catch (err) {\n const message =\n err instanceof Error ? err.message : label('updateError', 'Failed to update note')\n setContentError(message)\n } finally {\n setContentSavingId(null)\n }\n }, [contentEditor, handleUpdateNote, t])\n\n const handleContentEditorKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n if (!contentEditor.id) return\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (!contentSavingId) void handleContentSave()\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n setContentEditor({ id: '', value: '' })\n setContentError(null)\n }\n },\n [contentEditor.id, contentSavingId, handleContentSave],\n )\n\n const handleComposerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n formRef.current?.requestSubmit()\n }\n },\n [],\n )\n\n const handleContentKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>, note: CommentSummary) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setContentEditor({ id: note.id, value: note.body })\n }\n },\n [],\n )\n\n const noteAuthorLabel = React.useCallback(\n (note: CommentSummary) => {\n if (note.authorUserId && viewerUserId && note.authorUserId === viewerUserId) {\n return youLabel\n }\n return note.authorName ?? note.authorEmail ?? youLabel\n },\n [viewerUserId, youLabel],\n )\n\n const noteAppearanceLabels = React.useMemo<AppearanceSelectorLabels>(\n () => ({\n colorLabel: label('appearance.colorLabel'),\n colorHelp: label('appearance.colorHelp'),\n colorClearLabel: label('appearance.clearColor'),\n iconLabel: label('appearance.iconLabel'),\n iconPlaceholder: label('appearance.iconPlaceholder'),\n iconPickerTriggerLabel: label('appearance.iconPicker'),\n iconSearchPlaceholder: label('appearance.iconSearchPlaceholder'),\n iconSearchEmptyLabel: label('appearance.iconSearchEmpty'),\n iconSuggestionsLabel: label('appearance.iconSuggestions'),\n iconClearLabel: label('appearance.iconClear'),\n previewEmptyLabel: label('appearance.previewEmpty'),\n }),\n [label],\n )\n\n const composerAuthor = React.useMemo(\n () => youLabel,\n [youLabel],\n )\n const composerHasAppearance = Boolean(draftIcon) || Boolean(draftColor)\n const appearanceDialogOpen = appearanceDialogState !== null\n const editingAppearanceNoteId =\n appearanceDialogState?.mode === 'edit' ? appearanceDialogState.noteId : null\n const addNoteShortcutLabel = label('addShortcut', 'Add note \u2318\u23CE / Ctrl+Enter')\n const saveAppearanceShortcutLabel = label('appearance.saveShortcut', 'Save appearance \u2318\u23CE / Ctrl+Enter')\n const composerSubmitLabel = addNoteShortcutLabel\n const appearanceDialogPrimaryLabel = saveAppearanceShortcutLabel\n const appearanceDialogSavingLabel =\n appearanceDialogState?.mode === 'edit'\n ? label('appearance.saving')\n : label('saving', 'Saving note\u2026')\n\n return (\n <div className=\"mt-0 space-y-2\">\n <div\n className={[\n 'overflow-hidden rounded-xl transition-all duration-300 ease-out',\n composerOpen ? 'max-h-[1200px] bg-muted/30 p-4 opacity-100' : 'pointer-events-none max-h-0 p-0 opacity-0',\n ].join(' ')}\n aria-hidden={!composerOpen}\n >\n {composerOpen ? (\n <form\n ref={formRef}\n onSubmit={handleSubmit}\n onKeyDown={handleComposerKeyDown}\n className=\"space-y-3\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"text-sm font-medium\">{label('addLabel')}</h3>\n <div className=\"flex flex-wrap items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => {\n setAppearanceDialogError(null)\n setAppearanceDialogState({ mode: 'create', icon: draftIcon, color: draftColor })\n }}\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n <span className=\"sr-only\">{label('appearance.toggleOpen', 'Customize appearance')}</span>\n <Palette className=\"h-4 w-4\" />\n </Button>\n {disableMarkdown ? null : (\n <Button\n type=\"button\"\n variant={isMarkdownEnabled ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownEnabled}\n disabled={isSubmitting || isLoading}\n >\n <FileCode className=\"h-4 w-4\" />\n </Button>\n )}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setComposerOpen(false)\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }}\n disabled={isSubmitting || isLoading}\n >\n {inlineLabel('cancel')}\n </Button>\n </div>\n </div>\n {(normalizedEntityOptions.length || normalizedDealOptions.length) ? (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {normalizedEntityOptions.length ? (\n <div className=\"flex flex-col gap-1\">\n <label\n htmlFor=\"note-entity-select\"\n className=\"text-xs font-medium text-muted-foreground\"\n >\n {label('fields.entity', 'Assign to customer')}\n </label>\n <Select\n value={selectedEntityId || undefined}\n onValueChange={(next) => setSelectedEntityId(next ?? '')}\n disabled={isSubmitting || isLoading || !normalizedEntityOptions.length}\n >\n <SelectTrigger id=\"note-entity-select\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {normalizedEntityOptions.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n {normalizedDealOptions.length ? (\n <div className=\"flex flex-col gap-1\">\n <label\n htmlFor=\"note-deal-select\"\n className=\"text-xs font-medium text-muted-foreground\"\n >\n {label('fields.deal', 'Link to deal (optional)')}\n </label>\n <Select\n value={selectedDealId || undefined}\n onValueChange={(next) => setSelectedDealId(next ?? '')}\n disabled={isSubmitting || isLoading}\n >\n <SelectTrigger id=\"note-deal-select\">\n <SelectValue placeholder={label('fields.dealPlaceholder', 'No linked deal')} />\n </SelectTrigger>\n <SelectContent>\n {normalizedDealOptions.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n </div>\n ) : null}\n <SwitchableMarkdownInput\n value={draftBody}\n onChange={setDraftBody}\n isMarkdownEnabled={isMarkdownEnabled}\n disableMarkdown={disableMarkdown}\n rows={1}\n placeholder={label('placeholder')}\n textareaRef={textareaRef}\n onTextareaInput={(event) => adjustTextareaSize(event.currentTarget)}\n disabled={isSubmitting || isLoading || !hasEntity}\n remarkPlugins={markdownPlugins}\n />\n {composerHasAppearance ? (\n <div className=\"flex flex-wrap items-center justify-between gap-3 rounded-lg border border-dashed border-muted-foreground/40 px-3 py-2\">\n <div className=\"flex flex-wrap items-center gap-3 text-sm\">\n {draftIcon && renderIcon ? (\n <span className=\"inline-flex h-7 w-7 items-center justify-center rounded border border-border bg-muted/50\">\n {renderIcon(draftIcon, 'h-4 w-4')}\n </span>\n ) : null}\n <span className=\"font-semibold text-foreground\">{composerAuthor}</span>\n {draftColor && renderColor ? (\n <span className=\"flex items-center gap-2\">\n {renderColor(draftColor, 'h-3.5 w-3.5 rounded-full border border-border')}\n <span className=\"text-xs font-medium uppercase text-muted-foreground\">{draftColor}</span>\n </span>\n ) : null}\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setDraftIcon(null)\n setDraftColor(null)\n }}\n disabled={isSubmitting}\n >\n {label('appearance.clearAll', 'Clear')}\n </Button>\n </div>\n ) : null}\n <div className=\"flex justify-end\">\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n {isSubmitting ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {composerSubmitLabel}\n </Button>\n </div>\n </form>\n ) : null}\n </div>\n\n {loadError ? <ErrorMessage label={loadError} className=\"mt-3\" /> : null}\n\n <div className=\"space-y-3\">\n {!composerOpen && hasVisibleNotes && !onActionChange ? (\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={focusComposer}\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n <Plus className=\"size-4\" />\n {addActionLabel}\n </Button>\n </div>\n ) : null}\n {isLoading ? (\n <LoadingMessage\n label={label('loading', 'Loading notes\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : hasVisibleNotes ? (\n visibleNotes.map((note) => {\n const author = noteAuthorLabel(note)\n const isAppearanceSaving = appearanceDialogSaving && editingAppearanceNoteId === note.id\n const isEditingContent = contentEditor.id === note.id\n const displayIcon = note.appearanceIcon ?? null\n const displayColor = note.appearanceColor ?? null\n const timestampValue = note.createdAt\n const fallbackTimestampLabel = formatDateTime(note.createdAt) ?? emptyLabel\n return (\n <div key={note.id} className=\"group space-y-2 rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <TimelineItemHeader\n title={author}\n timestamp={timestampValue}\n fallbackTimestampLabel={fallbackTimestampLabel}\n icon={displayIcon}\n color={displayColor}\n renderIcon={renderIcon}\n renderColor={renderColor}\n />\n {note.dealId ? (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <ArrowUpRightSquare className=\"h-3.5 w-3.5\" />\n <a\n href={`/backend/customers/deals/${encodeURIComponent(note.dealId)}`}\n className=\"font-medium text-foreground hover:underline\"\n >\n {note.dealTitle && note.dealTitle.length\n ? note.dealTitle\n : label('linkedDeal', 'Linked deal')}\n </a>\n </div>\n ) : null}\n </div>\n <div\n className={`flex items-center gap-2 transition-opacity ${\n isEditingContent ? 'opacity-100' : 'opacity-100 md:opacity-0 md:group-hover:opacity-100 focus-within:opacity-100'\n }`}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setContentEditor({ id: note.id, value: note.body })}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n setAppearanceDialogError(null)\n setAppearanceDialogState({\n mode: 'edit',\n noteId: note.id,\n icon: note.appearanceIcon ?? null,\n color: note.appearanceColor ?? null,\n })\n }}\n disabled={appearanceDialogSaving && editingAppearanceNoteId === note.id}\n >\n {isAppearanceSaving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Palette className=\"h-4 w-4\" />}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n void handleDeleteNote(note)\n }}\n disabled={deletingNoteId === note.id}\n >\n {deletingNoteId === note.id ? (\n <span className=\"relative flex h-4 w-4 items-center justify-center text-destructive\">\n <span className=\"absolute h-4 w-4 animate-spin rounded-full border border-destructive border-t-transparent\" />\n </span>\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n </div>\n {isEditingContent ? (\n <div className=\"space-y-2\" onKeyDown={handleContentEditorKeyDown}>\n <SwitchableMarkdownInput\n value={contentEditor.value}\n onChange={(nextValue) => setContentEditor((prev) => ({ ...prev, value: nextValue }))}\n isMarkdownEnabled={isMarkdownEnabled}\n disableMarkdown={disableMarkdown}\n rows={3}\n textareaRef={contentTextareaRef}\n onTextareaInput={(event) => adjustTextareaSize(event.currentTarget)}\n textareaClassName=\"w-full resize-none overflow-hidden rounded-md border border-border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n editorWrapperClassName=\"w-full rounded-md border border-muted-foreground/20 bg-background p-2\"\n remarkPlugins={markdownPlugins}\n />\n {contentError ? <p className=\"text-xs text-status-error-text\">{contentError}</p> : null}\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"button\" size=\"sm\" onClick={handleContentSave} disabled={contentSavingId === note.id}>\n {contentSavingId === note.id ? (\n <>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n {label('saving')}\n </>\n ) : (\n inlineLabel('saveShortcut')\n )}\n </Button>\n {disableMarkdown ? null : (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownEnabled}\n className={isMarkdownEnabled ? 'text-primary' : undefined}\n disabled={contentSavingId === note.id}\n >\n <FileCode className=\"h-4 w-4\" />\n </Button>\n )}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setContentEditor({ id: '', value: '' })}\n disabled={contentSavingId === note.id}\n >\n {inlineLabel('cancel')}\n </Button>\n </div>\n </div>\n ) : (\n <div\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer text-sm\"\n onClick={() => setContentEditor({ id: note.id, value: note.body })}\n onKeyDown={(event) => handleContentKeyDown(event, note)}\n >\n <MarkdownPreview\n remarkPlugins={markdownPlugins}\n 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\"\n >\n {note.body}\n </MarkdownPreview>\n </div>\n )}\n </div>\n )\n })\n ) : composerOpen ? null : (\n <TabEmptyState\n title={emptyState.title}\n description={emptyState.description}\n action={{\n label: emptyState.actionLabel,\n onClick: focusComposer,\n disabled: isSubmitting || !hasEntity,\n }}\n />\n )}\n {isLoading || (pagedMode ? !hasMore : visibleCount >= notes.length) ? null : (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleLoadMore}>\n {loadMoreLabel}\n </Button>\n </div>\n )}\n </div>\n <AppearanceDialog\n open={appearanceDialogOpen}\n title={\n appearanceDialogState?.mode === 'edit'\n ? label('appearance.edit')\n : label('appearance.toggleOpen', 'Customize appearance')\n }\n icon={appearanceDialogState?.icon ?? null}\n color={appearanceDialogState?.color ?? null}\n labels={noteAppearanceLabels}\n iconSuggestions={iconSuggestions}\n onIconChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, icon: value ?? null } : prev))}\n onColorChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, color: value ?? null } : prev))}\n onSubmit={() => {\n void handleAppearanceDialogSubmit()\n }}\n onClose={handleAppearanceDialogClose}\n isSaving={appearanceDialogSaving}\n errorMessage={appearanceDialogError}\n primaryLabel={appearanceDialogPrimaryLabel}\n savingLabel={appearanceDialogSavingLabel}\n cancelLabel={label('appearance.cancel')}\n />\n {ConfirmDialogElement}\n </div>\n )\n}\n\nexport function NotesSection<C = unknown>(props: NotesSectionProps<C>) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'NotesSection')\n const Resolved = useRegisteredComponent<NotesSectionProps<C>>(\n handle,\n NotesSectionImpl as React.ComponentType<NotesSectionProps<C>>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { PluggableList } from 'unified'\nimport type { AppearanceSelectorLabels } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport { AppearanceDialog } from '@open-mercato/core/modules/customers/components/detail/AppearanceDialog'\nimport type { IconOption } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { ArrowUpRightSquare, FileCode, Loader2, Palette, Pencil, Plus, Trash2 } from 'lucide-react'\nimport { formatRelativeTime } from '@open-mercato/shared/lib/time'\nimport { hasMoreFromPage } from '@open-mercato/shared/lib/pagination/load-more'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { flash } from '../FlashMessages'\nimport { surfaceRecordConflict } from '../conflicts'\nimport { SwitchableMarkdownInput } from '../inputs/SwitchableMarkdownInput'\nimport { ErrorMessage } from './ErrorMessage'\nimport { LoadingMessage } from './LoadingMessage'\nimport { TabEmptyState } from './TabEmptyState'\nimport { useConfirmDialog } from '../confirm-dialog'\nimport { formatDateTime } from '@open-mercato/shared/lib/time'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { MarkdownPreview } from '../markdown'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\n\nconst NOTES_PAGE_SIZE = 20\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nexport type SectionAction = {\n label: React.ReactNode\n onClick: () => void\n disabled?: boolean\n icon?: React.ReactNode\n}\n\nexport type TabEmptyStateConfig = {\n title: string\n actionLabel: string\n description?: string\n}\n\nexport type CommentSummary = {\n id: string\n body: string\n createdAt: string\n updatedAt?: string | null\n authorUserId?: string | null\n authorName?: string | null\n authorEmail?: string | null\n dealId?: string | null\n dealTitle?: string | null\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\nexport type NotesCreatePayload = {\n entityId: string\n body: string\n appearanceIcon: string | null\n appearanceColor: string | null\n dealId?: string | null\n}\n\nexport type NotesUpdatePayload = {\n body?: string\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\nexport type NotesDataAdapter<C = unknown> = {\n list: (params: { entityId: string | null; dealId: string | null; context?: C }) => Promise<CommentSummary[]>\n listPage?: (params: {\n entityId: string | null\n dealId: string | null\n page: number\n pageSize: number\n context?: C\n }) => Promise<{\n items: CommentSummary[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n }>\n create: (params: NotesCreatePayload & { context?: C }) => Promise<Partial<CommentSummary> | void>\n update: (params: { id: string; patch: NotesUpdatePayload; updatedAt?: string | null; context?: C }) => Promise<void>\n delete: (params: { id: string; updatedAt?: string | null; context?: C }) => Promise<void>\n}\n\ntype RenderIconFn = (icon: string, className?: string) => React.ReactNode\ntype RenderColorFn = (color: string, className?: string) => React.ReactNode\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\nfunction generateTempId() {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID()\n return `tmp_${Math.random().toString(36).slice(2)}`\n}\n\n\n\ntype TimelineItemHeaderProps = {\n title: React.ReactNode\n subtitle?: React.ReactNode\n timestamp?: string | Date | null\n fallbackTimestampLabel?: React.ReactNode\n icon?: string | null\n color?: string | null\n iconSize?: 'sm' | 'md'\n className?: string\n renderIcon?: RenderIconFn\n renderColor?: RenderColorFn\n}\n\nfunction TimelineItemHeader({\n title,\n subtitle,\n timestamp,\n fallbackTimestampLabel,\n icon,\n color,\n iconSize = 'md',\n className,\n renderIcon,\n renderColor,\n}: TimelineItemHeaderProps) {\n const wrapperSize = iconSize === 'sm' ? 'h-6 w-6' : 'h-8 w-8'\n const iconSizeClass = iconSize === 'sm' ? 'h-3.5 w-3.5' : 'h-4 w-4'\n const resolvedTimestamp = React.useMemo(() => {\n if (subtitle) return subtitle\n if (!timestamp) return fallbackTimestampLabel ?? null\n const value = typeof timestamp === 'string' ? timestamp : timestamp.toISOString()\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallbackTimestampLabel ?? null\n const now = Date.now()\n const diff = Math.abs(now - date.getTime())\n const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000\n const relativeLabel = diff <= THIRTY_DAYS_MS ? formatRelativeTime(value) : null\n const absoluteLabel = formatDateTime(value)\n if (relativeLabel) {\n return (\n <span title={absoluteLabel ?? undefined}>\n {relativeLabel}\n </span>\n )\n }\n return absoluteLabel ?? fallbackTimestampLabel ?? null\n }, [fallbackTimestampLabel, subtitle, timestamp])\n\n return (\n <div className={['flex items-start gap-3', className].filter(Boolean).join(' ')}>\n {icon && renderIcon ? (\n <span className={['inline-flex items-center justify-center rounded border border-border bg-muted/50', wrapperSize].join(' ')}>\n {renderIcon(icon, iconSizeClass)}\n </span>\n ) : null}\n <div className=\"space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">{title}</span>\n {color && renderColor ? renderColor(color, 'h-3 w-3 rounded-full border border-border') : null}\n </div>\n {resolvedTimestamp ? <div className=\"text-xs text-muted-foreground\">{resolvedTimestamp}</div> : null}\n </div>\n </div>\n )\n}\n\nexport type NotesSectionProps<C = unknown> = {\n entityId: string | null\n dealId?: string | null\n emptyLabel: string\n viewerUserId: string | null\n viewerName?: string | null\n viewerEmail?: string | null\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n translator?: Translator\n labelPrefix?: string\n inlineLabelPrefix?: string\n onLoadingChange?: (isLoading: boolean) => void\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n dataAdapter: NotesDataAdapter<C>\n dataContext?: C\n renderIcon?: RenderIconFn\n renderColor?: RenderColorFn\n iconSuggestions?: IconOption[]\n readMarkdownPreference?: () => boolean | null\n writeMarkdownPreference?: (value: boolean) => void\n /** Force the plain textarea and hide the markdown toggle. Wins over `forceMarkdown`. */\n disableMarkdown?: boolean\n /**\n * Keep the rich markdown editor active for every note and hide the markdown toggle,\n * so the editor shape cannot be switched. Ignored when `disableMarkdown` is set.\n */\n forceMarkdown?: boolean\n /**\n * Hide the markdown toggle without changing which editor renders \u2014 the seeded\n * `readMarkdownPreference` value stays in effect. Implied by `forceMarkdown`.\n */\n hideMarkdownToggle?: boolean\n /** Hide the appearance (icon/color) affordances: the palette buttons and the appearance dialog. */\n disableAppearance?: boolean\n}\n\nexport function sanitizeHexColor(value: string | null): string | null {\n if (!value) return null\n const trimmed = value.trim()\n return /^#([0-9a-f]{6})$/i.test(trimmed) ? trimmed.toLowerCase() : null\n}\n\nexport function mapCommentSummary(input: unknown): CommentSummary {\n const data = (typeof input === 'object' && input !== null ? input : {}) as Record<string, unknown>\n const id = typeof data.id === 'string' ? data.id : generateTempId()\n const body = typeof data.body === 'string' ? data.body : ''\n const createdAt =\n typeof data.createdAt === 'string'\n ? data.createdAt\n : typeof data.created_at === 'string'\n ? data.created_at\n : new Date().toISOString()\n const updatedAt =\n typeof data.updatedAt === 'string'\n ? data.updatedAt\n : typeof data.updated_at === 'string'\n ? data.updated_at\n : null\n const authorUserId =\n typeof data.authorUserId === 'string'\n ? data.authorUserId\n : typeof data.author_user_id === 'string'\n ? data.author_user_id\n : null\n const authorName =\n typeof data.authorName === 'string'\n ? data.authorName\n : typeof data.author_name === 'string'\n ? data.author_name\n : null\n const authorEmail =\n typeof data.authorEmail === 'string'\n ? data.authorEmail\n : typeof data.author_email === 'string'\n ? data.author_email\n : null\n const dealId =\n typeof data.dealId === 'string'\n ? data.dealId\n : typeof data.deal_id === 'string'\n ? data.deal_id\n : null\n const dealTitle =\n typeof data.dealTitle === 'string'\n ? data.dealTitle\n : typeof data.deal_title === 'string'\n ? data.deal_title\n : null\n const appearanceIcon =\n typeof data.appearanceIcon === 'string'\n ? data.appearanceIcon\n : typeof data.appearance_icon === 'string'\n ? data.appearance_icon\n : null\n const appearanceColor =\n typeof data.appearanceColor === 'string'\n ? data.appearanceColor\n : typeof data.appearance_color === 'string'\n ? data.appearance_color\n : null\n return {\n id,\n body,\n createdAt,\n updatedAt,\n authorUserId,\n authorName,\n authorEmail,\n dealId,\n dealTitle,\n appearanceIcon,\n appearanceColor,\n }\n}\n\nfunction NotesSectionImpl<C = unknown>({\n entityId,\n dealId,\n emptyLabel,\n viewerUserId,\n viewerName,\n viewerEmail,\n addActionLabel,\n emptyState,\n onActionChange,\n translator,\n labelPrefix = 'customers.people.detail.notes',\n inlineLabelPrefix = 'customers.people.detail.inline',\n onLoadingChange,\n dealOptions,\n entityOptions,\n dataAdapter,\n dataContext,\n renderIcon,\n renderColor,\n iconSuggestions,\n readMarkdownPreference,\n writeMarkdownPreference,\n disableMarkdown,\n forceMarkdown,\n hideMarkdownToggle,\n disableAppearance,\n}: NotesSectionProps<C>) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = React.useMemo<Translator>(() => translator ?? ((key, fallback) => fallback ?? key), [translator])\n const label = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback, params),\n [labelPrefix, t],\n )\n const inlineLabel = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${inlineLabelPrefix}.${suffix}`, fallback, params),\n [inlineLabelPrefix, t],\n )\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\n const normalizedDealOptions = React.useMemo(() => {\n if (!Array.isArray(dealOptions)) return []\n const seen = new Set<string>()\n return dealOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [dealOptions])\n\n const dealLabelMap = React.useMemo(() => {\n const map = new Map<string, string>()\n normalizedDealOptions.forEach((option) => {\n map.set(option.id, option.label)\n })\n return map\n }, [normalizedDealOptions])\n\n const normalizedEntityOptions = React.useMemo(() => {\n if (!Array.isArray(entityOptions)) return []\n const seen = new Set<string>()\n return entityOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [entityOptions])\n\n const [selectedDealId, setSelectedDealId] = React.useState<string>(() => {\n const initial = typeof dealId === 'string' ? dealId.trim() : ''\n return initial\n })\n React.useEffect(() => {\n const initial = typeof dealId === 'string' ? dealId.trim() : ''\n if (initial !== selectedDealId) {\n setSelectedDealId(initial)\n }\n }, [dealId, selectedDealId])\n\n const [selectedEntityId, setSelectedEntityId] = React.useState<string>(() => {\n if (normalizedEntityOptions.length) return normalizedEntityOptions[0].id\n return typeof entityId === 'string' ? entityId : ''\n })\n React.useEffect(() => {\n if (normalizedEntityOptions.length) {\n if (!normalizedEntityOptions.some((option) => option.id === selectedEntityId)) {\n setSelectedEntityId(normalizedEntityOptions[0].id)\n }\n } else {\n const initial = typeof entityId === 'string' ? entityId : ''\n if (initial !== selectedEntityId) {\n setSelectedEntityId(initial)\n }\n }\n }, [entityId, normalizedEntityOptions, selectedEntityId])\n\n const resolvedEntityId = React.useMemo(() => {\n if (normalizedEntityOptions.length) return selectedEntityId\n return typeof entityId === 'string' ? entityId : ''\n }, [entityId, normalizedEntityOptions, selectedEntityId])\n\n const resolvedDealId = React.useMemo(() => {\n const trimmed = typeof selectedDealId === 'string' ? selectedDealId.trim() : ''\n return trimmed\n }, [selectedDealId])\n\n const hasEntity = resolvedEntityId.length > 0\n\n const [notes, setNotes] = React.useState<CommentSummary[]>([])\n const [isLoading, setIsLoading] = React.useState<boolean>(() => Boolean(entityId || dealId))\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const pendingCounterRef = React.useRef(0)\n\n const pushLoading = React.useCallback(() => {\n pendingCounterRef.current += 1\n if (pendingCounterRef.current === 1) {\n onLoadingChange?.(true)\n }\n }, [onLoadingChange])\n\n const popLoading = React.useCallback(() => {\n pendingCounterRef.current = Math.max(0, pendingCounterRef.current - 1)\n if (pendingCounterRef.current === 0) {\n onLoadingChange?.(false)\n }\n }, [onLoadingChange])\n\n const [composerOpen, setComposerOpen] = React.useState(false)\n const [draftBody, setDraftBody] = React.useState('')\n const [draftIcon, setDraftIcon] = React.useState<string | null>(null)\n const [draftColor, setDraftColor] = React.useState<string | null>(null)\n const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(false)\n const isMarkdownActive = !disableMarkdown && (Boolean(forceMarkdown) || isMarkdownEnabled)\n const showMarkdownToggle = !disableMarkdown && !forceMarkdown && !hideMarkdownToggle\n const showAppearanceControls = !disableAppearance\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const formRef = React.useRef<HTMLFormElement | null>(null)\n const focusComposer = React.useCallback(() => {\n if (!hasEntity) return\n setComposerOpen(true)\n window.requestAnimationFrame(() => {\n if (isMarkdownActive) {\n const markdownTextarea = formRef.current?.querySelector('textarea')\n if (markdownTextarea instanceof HTMLTextAreaElement) {\n markdownTextarea.focus()\n markdownTextarea.scrollIntoView({ behavior: 'smooth', block: 'center' })\n return\n }\n }\n const element = textareaRef.current\n if (!element) return\n element.focus()\n element.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n }, [formRef, hasEntity, isMarkdownActive])\n const [appearanceDialogState, setAppearanceDialogState] = React.useState<\n | { mode: 'create'; icon: string | null; color: string | null }\n | { mode: 'edit'; noteId: string; icon: string | null; color: string | null }\n | null\n >(null)\n const [appearanceDialogSaving, setAppearanceDialogSaving] = React.useState(false)\n const [appearanceDialogError, setAppearanceDialogError] = React.useState<string | null>(null)\n const [contentEditor, setContentEditor] = React.useState<{ id: string; value: string }>({ id: '', value: '' })\n const [contentSavingId, setContentSavingId] = React.useState<string | null>(null)\n const [contentError, setContentError] = React.useState<string | null>(null)\n const contentTextareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [visibleCount, setVisibleCount] = React.useState(0)\n const [currentPage, setCurrentPage] = React.useState(1)\n // Short-page termination instead of `currentPage >= totalPages` \u2014 see\n // `hasMoreFromPage`. `/api/customers/comments` is a `makeCrudRoute` list, so\n // a page past the end comes back empty rather than clamped.\n const [hasMore, setHasMore] = React.useState(false)\n const [deletingNoteId, setDeletingNoteId] = React.useState<string | null>(null)\n const pagedMode = typeof dataAdapter.listPage === 'function'\n\n React.useEffect(() => {\n const queryEntityId = typeof entityId === 'string' ? entityId : ''\n const queryDealId = typeof dealId === 'string' ? dealId : ''\n if (!queryEntityId && !queryDealId) {\n setNotes([])\n setLoadError(null)\n setIsLoading(false)\n setCurrentPage(1)\n setHasMore(false)\n return\n }\n let cancelled = false\n setIsLoading(true)\n setLoadError(null)\n pushLoading()\n async function loadNotes() {\n try {\n if (dataAdapter.listPage) {\n const pageResult = await dataAdapter.listPage({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n page: 1,\n pageSize: NOTES_PAGE_SIZE,\n context: dataContext,\n })\n if (cancelled) return\n setNotes(pageResult.items)\n setCurrentPage(pageResult.page)\n setHasMore(hasMoreFromPage(pageResult.items.length, NOTES_PAGE_SIZE))\n return\n }\n const mapped = await dataAdapter.list({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n context: dataContext,\n })\n if (cancelled) return\n setNotes(mapped)\n setCurrentPage(1)\n setHasMore(false)\n } catch (err) {\n if (cancelled) return\n const message =\n err instanceof Error ? err.message : label('loadError', 'Failed to load notes.')\n setNotes([])\n setLoadError(message)\n setCurrentPage(1)\n setHasMore(false)\n flash(message, 'error')\n } finally {\n if (!cancelled) setIsLoading(false)\n popLoading()\n }\n }\n loadNotes().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [dataAdapter, dataContext, dealId, entityId, label, popLoading, pushLoading])\n\n const youLabel = label('you', 'You')\n const viewerLabel = React.useMemo(() => viewerName ?? viewerEmail ?? null, [viewerEmail, viewerName])\n\n const handleMarkdownToggle = React.useCallback(() => {\n setIsMarkdownEnabled((prev) => {\n const next = !prev\n if (writeMarkdownPreference) {\n writeMarkdownPreference(next)\n }\n return next\n })\n }, [writeMarkdownPreference])\n\n React.useEffect(() => {\n if (!onActionChange) return\n if (!notes.length) {\n onActionChange(null)\n return\n }\n onActionChange({\n label: addActionLabel,\n onClick: focusComposer,\n disabled: isSubmitting || isLoading || !hasEntity,\n icon: <Plus className=\"mr-2 h-4 w-4\" />,\n })\n return () => onActionChange(null)\n }, [onActionChange, addActionLabel, focusComposer, hasEntity, isLoading, isSubmitting, notes.length])\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, draftBody, isMarkdownActive, composerOpen])\n\n React.useEffect(() => {\n const preference = readMarkdownPreference ? readMarkdownPreference() : null\n if (preference !== null) {\n setIsMarkdownEnabled(preference)\n }\n }, [readMarkdownPreference])\n\n React.useEffect(() => {\n if (pagedMode) {\n setVisibleCount(notes.length)\n return\n }\n if (!notes.length) {\n setVisibleCount(0)\n return\n }\n const baseline = Math.min(5, notes.length)\n setVisibleCount((prev) => {\n if (prev >= notes.length) return prev\n return Math.min(Math.max(prev, baseline), notes.length)\n })\n }, [notes.length, pagedMode])\n\n React.useEffect(() => {\n if (hasEntity) return\n setComposerOpen(false)\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }, [hasEntity])\n\n const visibleNotes = React.useMemo(\n () => (pagedMode ? notes : notes.slice(0, visibleCount)),\n [notes, pagedMode, visibleCount],\n )\n const hasVisibleNotes = React.useMemo(\n () => (pagedMode ? notes.length > 0 : visibleCount > 0),\n [notes.length, pagedMode, visibleCount],\n )\n\n const loadMoreLabel = label('loadMore')\n\n const handleCreateNote = React.useCallback(\n async (input: { body: string; appearanceIcon: string | null; appearanceColor: string | null }) => {\n if (!hasEntity || !resolvedEntityId) {\n flash(label('entityMissing', 'Unable to determine current person.'), 'error')\n return false\n }\n const body = input.body.trim()\n const strippedBody = body\n .replace(/^[\\s#\\-*>_~`|+\\\\\\n\\r]+$/gm, '')\n .replace(/\\s+/g, '')\n if (!body || !strippedBody.length) {\n focusComposer()\n return false\n }\n const icon = input.appearanceIcon && input.appearanceIcon.trim().length ? input.appearanceIcon.trim() : null\n const color = sanitizeHexColor(input.appearanceColor)\n const targetDealId = resolvedDealId.length ? resolvedDealId : null\n const dealLabel = targetDealId ? dealLabelMap.get(targetDealId) ?? null : null\n setIsSubmitting(true)\n pushLoading()\n try {\n const responseBody =\n (await dataAdapter.create({\n entityId: resolvedEntityId,\n body,\n appearanceIcon: icon,\n appearanceColor: color,\n dealId: targetDealId,\n context: dataContext,\n })) ?? {}\n setNotes((prev) => {\n const viewerId = viewerUserId ?? null\n const resolvedAuthorId =\n typeof responseBody?.authorUserId === 'string' ? responseBody.authorUserId : viewerId ?? null\n const resolvedAuthorName = (() => {\n if (resolvedAuthorId && viewerId && resolvedAuthorId === viewerId) {\n return youLabel\n }\n return typeof responseBody?.authorName === 'string' ? responseBody.authorName : viewerLabel\n })()\n const resolvedAuthorEmail = (() => {\n if (resolvedAuthorId && viewerId && resolvedAuthorId === viewerId) {\n return viewerEmail ?? null\n }\n return typeof responseBody?.authorEmail === 'string' ? responseBody.authorEmail : null\n })()\n const newNote: CommentSummary = {\n id: typeof responseBody?.id === 'string' ? responseBody.id : generateTempId(),\n body,\n createdAt: new Date().toISOString(),\n authorUserId: resolvedAuthorId,\n authorName: resolvedAuthorName,\n authorEmail: resolvedAuthorEmail,\n dealId: targetDealId,\n dealTitle: dealLabel,\n appearanceIcon: icon,\n appearanceColor: color,\n }\n return [newNote, ...prev]\n })\n setVisibleCount((prev) => Math.max(prev, 1))\n flash(label('success'), 'success')\n return true\n } catch (err) {\n const message = err instanceof Error ? err.message : label('error')\n if (!surfaceRecordConflict(err, t)) flash(message, 'error')\n return false\n } finally {\n setIsSubmitting(false)\n popLoading()\n }\n },\n [dataAdapter, dataContext, dealLabelMap, focusComposer, hasEntity, popLoading, pushLoading, resolvedDealId, resolvedEntityId, t, viewerEmail, viewerLabel, viewerUserId, youLabel],\n )\n\n const handleUpdateNote = React.useCallback(\n async (noteId: string, patch: { body?: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const sanitizedBody = patch.body\n const sanitizedIcon =\n patch.appearanceIcon !== undefined && patch.appearanceIcon !== null && patch.appearanceIcon.trim().length\n ? patch.appearanceIcon.trim()\n : patch.appearanceIcon === null\n ? null\n : undefined\n const sanitizedColor =\n patch.appearanceColor !== undefined ? sanitizeHexColor(patch.appearanceColor ?? null) : undefined\n const noteUpdatedAt = notes.find((comment) => comment.id === noteId)?.updatedAt ?? null\n try {\n await dataAdapter.update({\n id: noteId,\n patch: {\n body: sanitizedBody,\n appearanceIcon: sanitizedIcon,\n appearanceColor: sanitizedColor,\n },\n updatedAt: noteUpdatedAt,\n context: dataContext,\n })\n setNotes((prev) => {\n const nextComments = prev.map((comment) => {\n if (comment.id !== noteId) return comment\n const next = { ...comment }\n if (sanitizedBody !== undefined) next.body = sanitizedBody\n if (sanitizedIcon !== undefined) next.appearanceIcon = sanitizedIcon ?? null\n if (sanitizedColor !== undefined) next.appearanceColor = sanitizedColor ?? null\n return next\n })\n return nextComments\n })\n flash(label('updateSuccess'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : label('updateError')\n if (!surfaceRecordConflict(error, t)) flash(message, 'error')\n throw error instanceof Error ? error : new Error(message)\n }\n },\n [dataAdapter, dataContext, notes, t],\n )\n\n const handleDeleteNote = React.useCallback(\n async (note: CommentSummary) => {\n const confirmed = await confirm({\n title: label('deleteConfirm', 'Delete this note? You can restore it using version history.'),\n variant: 'destructive',\n })\n if (!confirmed) return\n setDeletingNoteId(note.id)\n pushLoading()\n try {\n await dataAdapter.delete({ id: note.id, updatedAt: note.updatedAt ?? null, context: dataContext })\n setNotes((prev) => prev.filter((existing) => existing.id !== note.id))\n if (pagedMode) {\n setVisibleCount((prev) => Math.max(0, prev - 1))\n }\n flash(label('deleteSuccess', 'Note deleted'), 'success')\n } catch (err) {\n const message = err instanceof Error ? err.message : label('deleteError', 'Failed to delete note')\n if (!surfaceRecordConflict(err, t)) flash(message, 'error')\n } finally {\n setDeletingNoteId(null)\n popLoading()\n }\n },\n [confirm, dataAdapter, dataContext, label, popLoading, pushLoading, t],\n )\n\n const handleSubmit = React.useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n const created = await handleCreateNote({\n body: draftBody,\n appearanceIcon: draftIcon,\n appearanceColor: draftColor,\n })\n if (created) {\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }\n },\n [draftBody, draftColor, draftIcon, handleCreateNote],\n )\n\n const handleLoadMore = React.useCallback(() => {\n if (pagedMode && dataAdapter.listPage) {\n if (!hasMore || isLoading) return\n const queryEntityId = typeof entityId === 'string' ? entityId : ''\n const queryDealId = typeof dealId === 'string' ? dealId : ''\n setIsLoading(true)\n pushLoading()\n void dataAdapter.listPage({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n page: currentPage + 1,\n pageSize: NOTES_PAGE_SIZE,\n context: dataContext,\n })\n .then((pageResult) => {\n setNotes((prev) => {\n const merged = new Map(prev.map((note) => [note.id, note]))\n pageResult.items.forEach((note) => merged.set(note.id, note))\n return Array.from(merged.values())\n })\n setCurrentPage(pageResult.page)\n setHasMore(hasMoreFromPage(pageResult.items.length, NOTES_PAGE_SIZE))\n })\n .catch((error) => {\n const message =\n error instanceof Error ? error.message : label('loadError', 'Failed to load notes.')\n flash(message, 'error')\n })\n .finally(() => {\n setIsLoading(false)\n popLoading()\n })\n return\n }\n setVisibleCount((prev) => {\n if (prev >= notes.length) return prev\n return Math.min(prev + 5, notes.length)\n })\n }, [currentPage, dataAdapter, dataContext, dealId, entityId, flash, hasMore, isLoading, label, notes.length, pagedMode, popLoading, pushLoading])\n\n const handleAppearanceDialogSubmit = React.useCallback(async () => {\n if (!appearanceDialogState) return\n setAppearanceDialogError(null)\n const sanitizedIcon =\n appearanceDialogState.icon && appearanceDialogState.icon.trim().length\n ? appearanceDialogState.icon.trim()\n : null\n const sanitizedColor = sanitizeHexColor(appearanceDialogState.color ?? null)\n if (appearanceDialogState.mode === 'create') {\n setDraftIcon(sanitizedIcon)\n setDraftColor(sanitizedColor)\n setAppearanceDialogState(null)\n return\n }\n setAppearanceDialogSaving(true)\n try {\n await handleUpdateNote(appearanceDialogState.noteId, {\n appearanceIcon: sanitizedIcon,\n appearanceColor: sanitizedColor,\n })\n setAppearanceDialogState(null)\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : label('appearance.error', 'Failed to update appearance.')\n setAppearanceDialogError(message)\n } finally {\n setAppearanceDialogSaving(false)\n }\n }, [appearanceDialogState, handleUpdateNote, t])\n\n const handleAppearanceDialogClose = React.useCallback(() => {\n if (appearanceDialogSaving) return\n setAppearanceDialogState(null)\n setAppearanceDialogError(null)\n }, [appearanceDialogSaving])\n\n const handleContentSave = React.useCallback(async () => {\n if (!contentEditor.id) return\n const trimmed = contentEditor.value.trim()\n if (!trimmed) {\n setContentError(label('updateError', 'Failed to update note'))\n return\n }\n setContentSavingId(contentEditor.id)\n setContentError(null)\n try {\n await handleUpdateNote(contentEditor.id, { body: trimmed })\n setContentEditor({ id: '', value: '' })\n } catch (err) {\n const message =\n err instanceof Error ? err.message : label('updateError', 'Failed to update note')\n setContentError(message)\n } finally {\n setContentSavingId(null)\n }\n }, [contentEditor, handleUpdateNote, t])\n\n const handleContentEditorKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n if (!contentEditor.id) return\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (!contentSavingId) void handleContentSave()\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n setContentEditor({ id: '', value: '' })\n setContentError(null)\n }\n },\n [contentEditor.id, contentSavingId, handleContentSave],\n )\n\n const handleComposerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n formRef.current?.requestSubmit()\n }\n },\n [],\n )\n\n const handleContentKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>, note: CommentSummary) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setContentEditor({ id: note.id, value: note.body })\n }\n },\n [],\n )\n\n const noteAuthorLabel = React.useCallback(\n (note: CommentSummary) => {\n if (note.authorUserId && viewerUserId && note.authorUserId === viewerUserId) {\n return youLabel\n }\n return note.authorName ?? note.authorEmail ?? youLabel\n },\n [viewerUserId, youLabel],\n )\n\n const noteAppearanceLabels = React.useMemo<AppearanceSelectorLabels>(\n () => ({\n colorLabel: label('appearance.colorLabel'),\n colorHelp: label('appearance.colorHelp'),\n colorClearLabel: label('appearance.clearColor'),\n iconLabel: label('appearance.iconLabel'),\n iconPlaceholder: label('appearance.iconPlaceholder'),\n iconPickerTriggerLabel: label('appearance.iconPicker'),\n iconSearchPlaceholder: label('appearance.iconSearchPlaceholder'),\n iconSearchEmptyLabel: label('appearance.iconSearchEmpty'),\n iconSuggestionsLabel: label('appearance.iconSuggestions'),\n iconClearLabel: label('appearance.iconClear'),\n previewEmptyLabel: label('appearance.previewEmpty'),\n }),\n [label],\n )\n\n const composerAuthor = React.useMemo(\n () => youLabel,\n [youLabel],\n )\n const composerHasAppearance = Boolean(draftIcon) || Boolean(draftColor)\n const appearanceDialogOpen = appearanceDialogState !== null\n const editingAppearanceNoteId =\n appearanceDialogState?.mode === 'edit' ? appearanceDialogState.noteId : null\n const addNoteShortcutLabel = label('addShortcut', 'Add note \u2318\u23CE / Ctrl+Enter')\n const saveAppearanceShortcutLabel = label('appearance.saveShortcut', 'Save appearance \u2318\u23CE / Ctrl+Enter')\n const composerSubmitLabel = addNoteShortcutLabel\n const appearanceDialogPrimaryLabel = saveAppearanceShortcutLabel\n const appearanceDialogSavingLabel =\n appearanceDialogState?.mode === 'edit'\n ? label('appearance.saving')\n : label('saving', 'Saving note\u2026')\n\n return (\n <div className=\"mt-0 space-y-2\">\n <div\n className={[\n 'overflow-hidden rounded-xl transition-all duration-300 ease-out',\n composerOpen ? 'max-h-[1200px] bg-muted/30 p-4 opacity-100' : 'pointer-events-none max-h-0 p-0 opacity-0',\n ].join(' ')}\n aria-hidden={!composerOpen}\n >\n {composerOpen ? (\n <form\n ref={formRef}\n onSubmit={handleSubmit}\n onKeyDown={handleComposerKeyDown}\n className=\"space-y-3\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"text-sm font-medium\">{label('addLabel')}</h3>\n <div className=\"flex flex-wrap items-center gap-1\">\n {showAppearanceControls ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => {\n setAppearanceDialogError(null)\n setAppearanceDialogState({ mode: 'create', icon: draftIcon, color: draftColor })\n }}\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n <span className=\"sr-only\">{label('appearance.toggleOpen', 'Customize appearance')}</span>\n <Palette className=\"h-4 w-4\" />\n </Button>\n ) : null}\n {showMarkdownToggle ? (\n <Button\n type=\"button\"\n variant={isMarkdownActive ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownActive}\n disabled={isSubmitting || isLoading}\n >\n <FileCode className=\"h-4 w-4\" />\n </Button>\n ) : null}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setComposerOpen(false)\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }}\n disabled={isSubmitting || isLoading}\n >\n {inlineLabel('cancel')}\n </Button>\n </div>\n </div>\n {(normalizedEntityOptions.length || normalizedDealOptions.length) ? (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {normalizedEntityOptions.length ? (\n <div className=\"flex flex-col gap-1\">\n <label\n htmlFor=\"note-entity-select\"\n className=\"text-xs font-medium text-muted-foreground\"\n >\n {label('fields.entity', 'Assign to customer')}\n </label>\n <Select\n value={selectedEntityId || undefined}\n onValueChange={(next) => setSelectedEntityId(next ?? '')}\n disabled={isSubmitting || isLoading || !normalizedEntityOptions.length}\n >\n <SelectTrigger id=\"note-entity-select\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {normalizedEntityOptions.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n {normalizedDealOptions.length ? (\n <div className=\"flex flex-col gap-1\">\n <label\n htmlFor=\"note-deal-select\"\n className=\"text-xs font-medium text-muted-foreground\"\n >\n {label('fields.deal', 'Link to deal (optional)')}\n </label>\n <Select\n value={selectedDealId || undefined}\n onValueChange={(next) => setSelectedDealId(next ?? '')}\n disabled={isSubmitting || isLoading}\n >\n <SelectTrigger id=\"note-deal-select\">\n <SelectValue placeholder={label('fields.dealPlaceholder', 'No linked deal')} />\n </SelectTrigger>\n <SelectContent>\n {normalizedDealOptions.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n </div>\n ) : null}\n <SwitchableMarkdownInput\n value={draftBody}\n onChange={setDraftBody}\n isMarkdownEnabled={isMarkdownActive}\n disableMarkdown={disableMarkdown}\n rows={1}\n placeholder={label('placeholder')}\n textareaRef={textareaRef}\n onTextareaInput={(event) => adjustTextareaSize(event.currentTarget)}\n disabled={isSubmitting || isLoading || !hasEntity}\n remarkPlugins={markdownPlugins}\n />\n {composerHasAppearance ? (\n <div className=\"flex flex-wrap items-center justify-between gap-3 rounded-lg border border-dashed border-muted-foreground/40 px-3 py-2\">\n <div className=\"flex flex-wrap items-center gap-3 text-sm\">\n {draftIcon && renderIcon ? (\n <span className=\"inline-flex h-7 w-7 items-center justify-center rounded border border-border bg-muted/50\">\n {renderIcon(draftIcon, 'h-4 w-4')}\n </span>\n ) : null}\n <span className=\"font-semibold text-foreground\">{composerAuthor}</span>\n {draftColor && renderColor ? (\n <span className=\"flex items-center gap-2\">\n {renderColor(draftColor, 'h-3.5 w-3.5 rounded-full border border-border')}\n <span className=\"text-xs font-medium uppercase text-muted-foreground\">{draftColor}</span>\n </span>\n ) : null}\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setDraftIcon(null)\n setDraftColor(null)\n }}\n disabled={isSubmitting}\n >\n {label('appearance.clearAll', 'Clear')}\n </Button>\n </div>\n ) : null}\n <div className=\"flex justify-end\">\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n {isSubmitting ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {composerSubmitLabel}\n </Button>\n </div>\n </form>\n ) : null}\n </div>\n\n {loadError ? <ErrorMessage label={loadError} className=\"mt-3\" /> : null}\n\n <div className=\"space-y-3\">\n {!composerOpen && hasVisibleNotes && !onActionChange ? (\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={focusComposer}\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n <Plus className=\"size-4\" />\n {addActionLabel}\n </Button>\n </div>\n ) : null}\n {isLoading ? (\n <LoadingMessage\n label={label('loading', 'Loading notes\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : hasVisibleNotes ? (\n visibleNotes.map((note) => {\n const author = noteAuthorLabel(note)\n const isAppearanceSaving = appearanceDialogSaving && editingAppearanceNoteId === note.id\n const isEditingContent = contentEditor.id === note.id\n const displayIcon = note.appearanceIcon ?? null\n const displayColor = note.appearanceColor ?? null\n const timestampValue = note.createdAt\n const fallbackTimestampLabel = formatDateTime(note.createdAt) ?? emptyLabel\n return (\n <div key={note.id} className=\"group space-y-2 rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <TimelineItemHeader\n title={author}\n timestamp={timestampValue}\n fallbackTimestampLabel={fallbackTimestampLabel}\n icon={displayIcon}\n color={displayColor}\n renderIcon={renderIcon}\n renderColor={renderColor}\n />\n {note.dealId ? (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <ArrowUpRightSquare className=\"h-3.5 w-3.5\" />\n <a\n href={`/backend/customers/deals/${encodeURIComponent(note.dealId)}`}\n className=\"font-medium text-foreground hover:underline\"\n >\n {note.dealTitle && note.dealTitle.length\n ? note.dealTitle\n : label('linkedDeal', 'Linked deal')}\n </a>\n </div>\n ) : null}\n </div>\n <div\n className={`flex items-center gap-2 transition-opacity ${\n isEditingContent ? 'opacity-100' : 'opacity-100 md:opacity-0 md:group-hover:opacity-100 focus-within:opacity-100'\n }`}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setContentEditor({ id: note.id, value: note.body })}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n {showAppearanceControls ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n setAppearanceDialogError(null)\n setAppearanceDialogState({\n mode: 'edit',\n noteId: note.id,\n icon: note.appearanceIcon ?? null,\n color: note.appearanceColor ?? null,\n })\n }}\n disabled={appearanceDialogSaving && editingAppearanceNoteId === note.id}\n >\n {isAppearanceSaving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Palette className=\"h-4 w-4\" />}\n </Button>\n ) : null}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n void handleDeleteNote(note)\n }}\n disabled={deletingNoteId === note.id}\n >\n {deletingNoteId === note.id ? (\n <span className=\"relative flex h-4 w-4 items-center justify-center text-destructive\">\n <span className=\"absolute h-4 w-4 animate-spin rounded-full border border-destructive border-t-transparent\" />\n </span>\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n </div>\n {isEditingContent ? (\n <div className=\"space-y-2\" onKeyDown={handleContentEditorKeyDown}>\n <SwitchableMarkdownInput\n value={contentEditor.value}\n onChange={(nextValue) => setContentEditor((prev) => ({ ...prev, value: nextValue }))}\n isMarkdownEnabled={isMarkdownActive}\n disableMarkdown={disableMarkdown}\n rows={3}\n textareaRef={contentTextareaRef}\n onTextareaInput={(event) => adjustTextareaSize(event.currentTarget)}\n textareaClassName=\"w-full resize-none overflow-hidden rounded-md border border-border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n editorWrapperClassName=\"w-full rounded-md border border-muted-foreground/20 bg-background p-2\"\n remarkPlugins={markdownPlugins}\n />\n {contentError ? <p className=\"text-xs text-status-error-text\">{contentError}</p> : null}\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"button\" size=\"sm\" onClick={handleContentSave} disabled={contentSavingId === note.id}>\n {contentSavingId === note.id ? (\n <>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n {label('saving')}\n </>\n ) : (\n inlineLabel('saveShortcut')\n )}\n </Button>\n {showMarkdownToggle ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownActive}\n className={isMarkdownActive ? 'text-primary' : undefined}\n disabled={contentSavingId === note.id}\n >\n <FileCode className=\"h-4 w-4\" />\n </Button>\n ) : null}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setContentEditor({ id: '', value: '' })}\n disabled={contentSavingId === note.id}\n >\n {inlineLabel('cancel')}\n </Button>\n </div>\n </div>\n ) : (\n <div\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer text-sm\"\n onClick={() => setContentEditor({ id: note.id, value: note.body })}\n onKeyDown={(event) => handleContentKeyDown(event, note)}\n >\n <MarkdownPreview\n remarkPlugins={markdownPlugins}\n 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\"\n >\n {note.body}\n </MarkdownPreview>\n </div>\n )}\n </div>\n )\n })\n ) : composerOpen ? null : (\n <TabEmptyState\n title={emptyState.title}\n description={emptyState.description}\n action={{\n label: emptyState.actionLabel,\n onClick: focusComposer,\n disabled: isSubmitting || !hasEntity,\n }}\n />\n )}\n {isLoading || (pagedMode ? !hasMore : visibleCount >= notes.length) ? null : (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleLoadMore}>\n {loadMoreLabel}\n </Button>\n </div>\n )}\n </div>\n {showAppearanceControls ? (\n <AppearanceDialog\n open={appearanceDialogOpen}\n title={\n appearanceDialogState?.mode === 'edit'\n ? label('appearance.edit')\n : label('appearance.toggleOpen', 'Customize appearance')\n }\n icon={appearanceDialogState?.icon ?? null}\n color={appearanceDialogState?.color ?? null}\n labels={noteAppearanceLabels}\n iconSuggestions={iconSuggestions}\n onIconChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, icon: value ?? null } : prev))}\n onColorChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, color: value ?? null } : prev))}\n onSubmit={() => {\n void handleAppearanceDialogSubmit()\n }}\n onClose={handleAppearanceDialogClose}\n isSaving={appearanceDialogSaving}\n errorMessage={appearanceDialogError}\n primaryLabel={appearanceDialogPrimaryLabel}\n savingLabel={appearanceDialogSavingLabel}\n cancelLabel={label('appearance.cancel')}\n />\n ) : null}\n {ConfirmDialogElement}\n </div>\n )\n}\n\nexport function NotesSection<C = unknown>(props: NotesSectionProps<C>) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'NotesSection')\n const Resolved = useRegisteredComponent<NotesSectionProps<C>>(\n handle,\n NotesSectionImpl as React.ComponentType<NotesSectionProps<C>>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["label"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.8-develop.
|
|
3
|
+
"version": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -155,14 +155,14 @@
|
|
|
155
155
|
"remark-gfm": "^4.0.1"
|
|
156
156
|
},
|
|
157
157
|
"peerDependencies": {
|
|
158
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
158
|
+
"@open-mercato/shared": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
159
159
|
"react": ">=18.0.0",
|
|
160
160
|
"react-dom": ">=18.0.0",
|
|
161
161
|
"react-is": ">=18.0.0"
|
|
162
162
|
},
|
|
163
163
|
"devDependencies": {
|
|
164
164
|
"@figma/code-connect": "^1.3.4",
|
|
165
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
165
|
+
"@open-mercato/shared": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
166
166
|
"@testing-library/dom": "^10.4.1",
|
|
167
167
|
"@testing-library/jest-dom": "^7.0.0",
|
|
168
168
|
"@testing-library/react": "^16.3.1",
|
|
@@ -7,6 +7,33 @@ import { NotesSection, type NotesDataAdapter } from '../detail/NotesSection'
|
|
|
7
7
|
import { dismissRecordConflict, getRecordConflictForTest } from '../conflicts'
|
|
8
8
|
import { OPTIMISTIC_LOCK_CONFLICT_CODE } from '@open-mercato/shared/lib/crud/optimistic-lock-headers'
|
|
9
9
|
|
|
10
|
+
const createDataAdapter = (): NotesDataAdapter => ({
|
|
11
|
+
list: jest.fn(async () => [
|
|
12
|
+
{
|
|
13
|
+
id: 'note-1',
|
|
14
|
+
body: 'Existing note',
|
|
15
|
+
createdAt: '2026-04-10T08:00:00.000Z',
|
|
16
|
+
authorName: 'Ada Lovelace',
|
|
17
|
+
},
|
|
18
|
+
]),
|
|
19
|
+
create: jest.fn(async () => ({ id: 'note-2' })),
|
|
20
|
+
update: jest.fn(async () => undefined),
|
|
21
|
+
delete: jest.fn(async () => undefined),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const baseProps = (dataAdapter: NotesDataAdapter) => ({
|
|
25
|
+
entityId: 'person-1',
|
|
26
|
+
emptyLabel: '—',
|
|
27
|
+
viewerUserId: 'user-1',
|
|
28
|
+
viewerName: 'Ada Lovelace',
|
|
29
|
+
addActionLabel: 'Add note',
|
|
30
|
+
emptyState: {
|
|
31
|
+
title: 'No notes yet',
|
|
32
|
+
actionLabel: 'Add note',
|
|
33
|
+
},
|
|
34
|
+
dataAdapter,
|
|
35
|
+
})
|
|
36
|
+
|
|
10
37
|
describe('NotesSection', () => {
|
|
11
38
|
beforeEach(() => {
|
|
12
39
|
dismissRecordConflict()
|
|
@@ -171,6 +198,68 @@ describe('NotesSection', () => {
|
|
|
171
198
|
})
|
|
172
199
|
})
|
|
173
200
|
|
|
201
|
+
it('renders the markdown toggle and appearance controls by default', async () => {
|
|
202
|
+
const { container } = renderWithProviders(<NotesSection {...baseProps(createDataAdapter())} />)
|
|
203
|
+
|
|
204
|
+
await screen.findByText('Existing note')
|
|
205
|
+
expect(container.querySelectorAll('svg.lucide-palette').length).toBe(1)
|
|
206
|
+
|
|
207
|
+
fireEvent.click(screen.getByRole('button', { name: 'Add note' }))
|
|
208
|
+
|
|
209
|
+
await waitFor(() => {
|
|
210
|
+
expect(container.querySelector('button[aria-pressed]')).not.toBeNull()
|
|
211
|
+
})
|
|
212
|
+
expect(screen.getByRole('button', { name: 'Customize appearance' })).toBeTruthy()
|
|
213
|
+
expect(screen.queryByTestId('markdown-field')).toBeNull()
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('keeps the rich editor active and hides the toggle when forceMarkdown is set', async () => {
|
|
217
|
+
const { container } = renderWithProviders(
|
|
218
|
+
<NotesSection {...baseProps(createDataAdapter())} forceMarkdown />,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
await screen.findByText('Existing note')
|
|
222
|
+
fireEvent.click(screen.getByRole('button', { name: 'Add note' }))
|
|
223
|
+
|
|
224
|
+
await waitFor(() => {
|
|
225
|
+
expect(screen.getByTestId('markdown-field')).toBeTruthy()
|
|
226
|
+
})
|
|
227
|
+
expect(container.querySelector('button[aria-pressed]')).toBeNull()
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('hides the toggle but honors the seeded preference when hideMarkdownToggle is set', async () => {
|
|
231
|
+
const { container } = renderWithProviders(
|
|
232
|
+
<NotesSection
|
|
233
|
+
{...baseProps(createDataAdapter())}
|
|
234
|
+
hideMarkdownToggle
|
|
235
|
+
readMarkdownPreference={() => true}
|
|
236
|
+
/>,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
await screen.findByText('Existing note')
|
|
240
|
+
fireEvent.click(screen.getByRole('button', { name: 'Add note' }))
|
|
241
|
+
|
|
242
|
+
await waitFor(() => {
|
|
243
|
+
expect(screen.getByTestId('markdown-field')).toBeTruthy()
|
|
244
|
+
})
|
|
245
|
+
expect(container.querySelector('button[aria-pressed]')).toBeNull()
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
it('removes every appearance affordance when disableAppearance is set', async () => {
|
|
249
|
+
const { container } = renderWithProviders(
|
|
250
|
+
<NotesSection {...baseProps(createDataAdapter())} disableAppearance />,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
await screen.findByText('Existing note')
|
|
254
|
+
fireEvent.click(screen.getByRole('button', { name: 'Add note' }))
|
|
255
|
+
|
|
256
|
+
await waitFor(() => {
|
|
257
|
+
expect(container.querySelector('button[aria-pressed]')).not.toBeNull()
|
|
258
|
+
})
|
|
259
|
+
expect(screen.queryByRole('button', { name: 'Customize appearance' })).toBeNull()
|
|
260
|
+
expect(container.querySelectorAll('svg.lucide-palette').length).toBe(0)
|
|
261
|
+
})
|
|
262
|
+
|
|
174
263
|
it('surfaces the unified conflict bar when a write fails with a 409', async () => {
|
|
175
264
|
const conflict = {
|
|
176
265
|
status: 409,
|
|
@@ -207,7 +207,20 @@ export type NotesSectionProps<C = unknown> = {
|
|
|
207
207
|
iconSuggestions?: IconOption[]
|
|
208
208
|
readMarkdownPreference?: () => boolean | null
|
|
209
209
|
writeMarkdownPreference?: (value: boolean) => void
|
|
210
|
+
/** Force the plain textarea and hide the markdown toggle. Wins over `forceMarkdown`. */
|
|
210
211
|
disableMarkdown?: boolean
|
|
212
|
+
/**
|
|
213
|
+
* Keep the rich markdown editor active for every note and hide the markdown toggle,
|
|
214
|
+
* so the editor shape cannot be switched. Ignored when `disableMarkdown` is set.
|
|
215
|
+
*/
|
|
216
|
+
forceMarkdown?: boolean
|
|
217
|
+
/**
|
|
218
|
+
* Hide the markdown toggle without changing which editor renders — the seeded
|
|
219
|
+
* `readMarkdownPreference` value stays in effect. Implied by `forceMarkdown`.
|
|
220
|
+
*/
|
|
221
|
+
hideMarkdownToggle?: boolean
|
|
222
|
+
/** Hide the appearance (icon/color) affordances: the palette buttons and the appearance dialog. */
|
|
223
|
+
disableAppearance?: boolean
|
|
211
224
|
}
|
|
212
225
|
|
|
213
226
|
export function sanitizeHexColor(value: string | null): string | null {
|
|
@@ -313,6 +326,9 @@ function NotesSectionImpl<C = unknown>({
|
|
|
313
326
|
readMarkdownPreference,
|
|
314
327
|
writeMarkdownPreference,
|
|
315
328
|
disableMarkdown,
|
|
329
|
+
forceMarkdown,
|
|
330
|
+
hideMarkdownToggle,
|
|
331
|
+
disableAppearance,
|
|
316
332
|
}: NotesSectionProps<C>) {
|
|
317
333
|
const { confirm, ConfirmDialogElement } = useConfirmDialog()
|
|
318
334
|
const t = React.useMemo<Translator>(() => translator ?? ((key, fallback) => fallback ?? key), [translator])
|
|
@@ -448,13 +464,16 @@ function NotesSectionImpl<C = unknown>({
|
|
|
448
464
|
const [draftIcon, setDraftIcon] = React.useState<string | null>(null)
|
|
449
465
|
const [draftColor, setDraftColor] = React.useState<string | null>(null)
|
|
450
466
|
const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(false)
|
|
467
|
+
const isMarkdownActive = !disableMarkdown && (Boolean(forceMarkdown) || isMarkdownEnabled)
|
|
468
|
+
const showMarkdownToggle = !disableMarkdown && !forceMarkdown && !hideMarkdownToggle
|
|
469
|
+
const showAppearanceControls = !disableAppearance
|
|
451
470
|
const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)
|
|
452
471
|
const formRef = React.useRef<HTMLFormElement | null>(null)
|
|
453
472
|
const focusComposer = React.useCallback(() => {
|
|
454
473
|
if (!hasEntity) return
|
|
455
474
|
setComposerOpen(true)
|
|
456
475
|
window.requestAnimationFrame(() => {
|
|
457
|
-
if (
|
|
476
|
+
if (isMarkdownActive) {
|
|
458
477
|
const markdownTextarea = formRef.current?.querySelector('textarea')
|
|
459
478
|
if (markdownTextarea instanceof HTMLTextAreaElement) {
|
|
460
479
|
markdownTextarea.focus()
|
|
@@ -467,7 +486,7 @@ function NotesSectionImpl<C = unknown>({
|
|
|
467
486
|
element.focus()
|
|
468
487
|
element.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
469
488
|
})
|
|
470
|
-
}, [formRef, hasEntity,
|
|
489
|
+
}, [formRef, hasEntity, isMarkdownActive])
|
|
471
490
|
const [appearanceDialogState, setAppearanceDialogState] = React.useState<
|
|
472
491
|
| { mode: 'create'; icon: string | null; color: string | null }
|
|
473
492
|
| { mode: 'edit'; noteId: string; icon: string | null; color: string | null }
|
|
@@ -584,7 +603,7 @@ function NotesSectionImpl<C = unknown>({
|
|
|
584
603
|
|
|
585
604
|
React.useEffect(() => {
|
|
586
605
|
adjustTextareaSize(textareaRef.current)
|
|
587
|
-
}, [adjustTextareaSize, draftBody,
|
|
606
|
+
}, [adjustTextareaSize, draftBody, isMarkdownActive, composerOpen])
|
|
588
607
|
|
|
589
608
|
React.useEffect(() => {
|
|
590
609
|
const preference = readMarkdownPreference ? readMarkdownPreference() : null
|
|
@@ -990,31 +1009,33 @@ function NotesSectionImpl<C = unknown>({
|
|
|
990
1009
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
991
1010
|
<h3 className="text-sm font-medium">{label('addLabel')}</h3>
|
|
992
1011
|
<div className="flex flex-wrap items-center gap-1">
|
|
993
|
-
|
|
994
|
-
type="button"
|
|
995
|
-
variant="ghost"
|
|
996
|
-
size="icon"
|
|
997
|
-
onClick={() => {
|
|
998
|
-
setAppearanceDialogError(null)
|
|
999
|
-
setAppearanceDialogState({ mode: 'create', icon: draftIcon, color: draftColor })
|
|
1000
|
-
}}
|
|
1001
|
-
disabled={isSubmitting || isLoading || !hasEntity}
|
|
1002
|
-
>
|
|
1003
|
-
<span className="sr-only">{label('appearance.toggleOpen', 'Customize appearance')}</span>
|
|
1004
|
-
<Palette className="h-4 w-4" />
|
|
1005
|
-
</Button>
|
|
1006
|
-
{disableMarkdown ? null : (
|
|
1012
|
+
{showAppearanceControls ? (
|
|
1007
1013
|
<Button
|
|
1008
1014
|
type="button"
|
|
1009
|
-
variant=
|
|
1015
|
+
variant="ghost"
|
|
1016
|
+
size="icon"
|
|
1017
|
+
onClick={() => {
|
|
1018
|
+
setAppearanceDialogError(null)
|
|
1019
|
+
setAppearanceDialogState({ mode: 'create', icon: draftIcon, color: draftColor })
|
|
1020
|
+
}}
|
|
1021
|
+
disabled={isSubmitting || isLoading || !hasEntity}
|
|
1022
|
+
>
|
|
1023
|
+
<span className="sr-only">{label('appearance.toggleOpen', 'Customize appearance')}</span>
|
|
1024
|
+
<Palette className="h-4 w-4" />
|
|
1025
|
+
</Button>
|
|
1026
|
+
) : null}
|
|
1027
|
+
{showMarkdownToggle ? (
|
|
1028
|
+
<Button
|
|
1029
|
+
type="button"
|
|
1030
|
+
variant={isMarkdownActive ? 'secondary' : 'ghost'}
|
|
1010
1031
|
size="icon"
|
|
1011
1032
|
onClick={handleMarkdownToggle}
|
|
1012
|
-
aria-pressed={
|
|
1033
|
+
aria-pressed={isMarkdownActive}
|
|
1013
1034
|
disabled={isSubmitting || isLoading}
|
|
1014
1035
|
>
|
|
1015
1036
|
<FileCode className="h-4 w-4" />
|
|
1016
1037
|
</Button>
|
|
1017
|
-
)}
|
|
1038
|
+
) : null}
|
|
1018
1039
|
<Button
|
|
1019
1040
|
type="button"
|
|
1020
1041
|
size="sm"
|
|
@@ -1090,7 +1111,7 @@ function NotesSectionImpl<C = unknown>({
|
|
|
1090
1111
|
<SwitchableMarkdownInput
|
|
1091
1112
|
value={draftBody}
|
|
1092
1113
|
onChange={setDraftBody}
|
|
1093
|
-
isMarkdownEnabled={
|
|
1114
|
+
isMarkdownEnabled={isMarkdownActive}
|
|
1094
1115
|
disableMarkdown={disableMarkdown}
|
|
1095
1116
|
rows={1}
|
|
1096
1117
|
placeholder={label('placeholder')}
|
|
@@ -1214,24 +1235,26 @@ function NotesSectionImpl<C = unknown>({
|
|
|
1214
1235
|
>
|
|
1215
1236
|
<Pencil className="h-4 w-4" />
|
|
1216
1237
|
</Button>
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
event
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1238
|
+
{showAppearanceControls ? (
|
|
1239
|
+
<Button
|
|
1240
|
+
type="button"
|
|
1241
|
+
variant="ghost"
|
|
1242
|
+
size="icon"
|
|
1243
|
+
onClick={(event) => {
|
|
1244
|
+
event.stopPropagation()
|
|
1245
|
+
setAppearanceDialogError(null)
|
|
1246
|
+
setAppearanceDialogState({
|
|
1247
|
+
mode: 'edit',
|
|
1248
|
+
noteId: note.id,
|
|
1249
|
+
icon: note.appearanceIcon ?? null,
|
|
1250
|
+
color: note.appearanceColor ?? null,
|
|
1251
|
+
})
|
|
1252
|
+
}}
|
|
1253
|
+
disabled={appearanceDialogSaving && editingAppearanceNoteId === note.id}
|
|
1254
|
+
>
|
|
1255
|
+
{isAppearanceSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Palette className="h-4 w-4" />}
|
|
1256
|
+
</Button>
|
|
1257
|
+
) : null}
|
|
1235
1258
|
<Button
|
|
1236
1259
|
type="button"
|
|
1237
1260
|
variant="ghost"
|
|
@@ -1257,7 +1280,7 @@ function NotesSectionImpl<C = unknown>({
|
|
|
1257
1280
|
<SwitchableMarkdownInput
|
|
1258
1281
|
value={contentEditor.value}
|
|
1259
1282
|
onChange={(nextValue) => setContentEditor((prev) => ({ ...prev, value: nextValue }))}
|
|
1260
|
-
isMarkdownEnabled={
|
|
1283
|
+
isMarkdownEnabled={isMarkdownActive}
|
|
1261
1284
|
disableMarkdown={disableMarkdown}
|
|
1262
1285
|
rows={3}
|
|
1263
1286
|
textareaRef={contentTextareaRef}
|
|
@@ -1278,19 +1301,19 @@ function NotesSectionImpl<C = unknown>({
|
|
|
1278
1301
|
inlineLabel('saveShortcut')
|
|
1279
1302
|
)}
|
|
1280
1303
|
</Button>
|
|
1281
|
-
{
|
|
1304
|
+
{showMarkdownToggle ? (
|
|
1282
1305
|
<Button
|
|
1283
1306
|
type="button"
|
|
1284
1307
|
variant="ghost"
|
|
1285
1308
|
size="icon"
|
|
1286
1309
|
onClick={handleMarkdownToggle}
|
|
1287
|
-
aria-pressed={
|
|
1288
|
-
className={
|
|
1310
|
+
aria-pressed={isMarkdownActive}
|
|
1311
|
+
className={isMarkdownActive ? 'text-primary' : undefined}
|
|
1289
1312
|
disabled={contentSavingId === note.id}
|
|
1290
1313
|
>
|
|
1291
1314
|
<FileCode className="h-4 w-4" />
|
|
1292
1315
|
</Button>
|
|
1293
|
-
)}
|
|
1316
|
+
) : null}
|
|
1294
1317
|
<Button
|
|
1295
1318
|
type="button"
|
|
1296
1319
|
size="sm"
|
|
@@ -1340,29 +1363,31 @@ function NotesSectionImpl<C = unknown>({
|
|
|
1340
1363
|
</div>
|
|
1341
1364
|
)}
|
|
1342
1365
|
</div>
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
+
{showAppearanceControls ? (
|
|
1367
|
+
<AppearanceDialog
|
|
1368
|
+
open={appearanceDialogOpen}
|
|
1369
|
+
title={
|
|
1370
|
+
appearanceDialogState?.mode === 'edit'
|
|
1371
|
+
? label('appearance.edit')
|
|
1372
|
+
: label('appearance.toggleOpen', 'Customize appearance')
|
|
1373
|
+
}
|
|
1374
|
+
icon={appearanceDialogState?.icon ?? null}
|
|
1375
|
+
color={appearanceDialogState?.color ?? null}
|
|
1376
|
+
labels={noteAppearanceLabels}
|
|
1377
|
+
iconSuggestions={iconSuggestions}
|
|
1378
|
+
onIconChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, icon: value ?? null } : prev))}
|
|
1379
|
+
onColorChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, color: value ?? null } : prev))}
|
|
1380
|
+
onSubmit={() => {
|
|
1381
|
+
void handleAppearanceDialogSubmit()
|
|
1382
|
+
}}
|
|
1383
|
+
onClose={handleAppearanceDialogClose}
|
|
1384
|
+
isSaving={appearanceDialogSaving}
|
|
1385
|
+
errorMessage={appearanceDialogError}
|
|
1386
|
+
primaryLabel={appearanceDialogPrimaryLabel}
|
|
1387
|
+
savingLabel={appearanceDialogSavingLabel}
|
|
1388
|
+
cancelLabel={label('appearance.cancel')}
|
|
1389
|
+
/>
|
|
1390
|
+
) : null}
|
|
1366
1391
|
{ConfirmDialogElement}
|
|
1367
1392
|
</div>
|
|
1368
1393
|
)
|