@open-mercato/ui 0.6.1-develop.3054.g0367114684 → 0.6.1-develop.3060.d6cca7ade1

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.
@@ -167,6 +167,7 @@ function SidebarCustomizationEditor({
167
167
  const [canApplyToRoles, setCanApplyToRoles] = React.useState(false);
168
168
  const [addDialogOpen, setAddDialogOpen] = React.useState(false);
169
169
  const [addDialogName, setAddDialogName] = React.useState("");
170
+ const [addDialogError, setAddDialogError] = React.useState(null);
170
171
  const baseSnapshotRef = React.useRef(null);
171
172
  const hasInitializedRef = React.useRef(false);
172
173
  const { runMutation, retryLastMutation } = useGuardedMutation({
@@ -278,8 +279,8 @@ function SidebarCustomizationEditor({
278
279
  setSelectedRoleIds((prev) => prev.includes(roleId) ? prev.filter((id) => id !== roleId) : [...prev, roleId]);
279
280
  setDirty(true);
280
281
  }, []);
281
- const createNewVariant = React.useCallback(async (proposedName) => {
282
- if (saving || deleting) return false;
282
+ const createNewVariant = React.useCallback(async (proposedName, options = {}) => {
283
+ if (saving || deleting) return { ok: false };
283
284
  if (dirty && selectedVariantId !== null) {
284
285
  const proceed = await confirmDialog({
285
286
  title: t("appShell.sidebarCustomizationSwitchConfirmTitle", "Discard unsaved changes?"),
@@ -288,10 +289,10 @@ function SidebarCustomizationEditor({
288
289
  cancelText: t("common.cancel", "Cancel"),
289
290
  variant: "destructive"
290
291
  });
291
- if (!proceed) return false;
292
+ if (!proceed) return { ok: false };
292
293
  }
293
294
  setSaving(true);
294
- setError(null);
295
+ if (!options.suppressPageError) setError(null);
295
296
  try {
296
297
  const baseSnapshot = baseSnapshotRef.current ?? buildBaseSnapshot();
297
298
  baseSnapshotRef.current = baseSnapshot;
@@ -312,8 +313,9 @@ function SidebarCustomizationEditor({
312
313
  mutationPayload: { name: trimmed.length > 0 ? trimmed : null }
313
314
  });
314
315
  if (!call.ok) {
315
- setError(formatVariantApiError(call, t));
316
- return false;
316
+ const message = formatVariantApiError(call, t);
317
+ if (!options.suppressPageError) setError(message);
318
+ return { ok: false, error: message };
317
319
  }
318
320
  const created = call.result?.variant ?? null;
319
321
  let nextList;
@@ -331,11 +333,12 @@ function SidebarCustomizationEditor({
331
333
  selectVariantInternal(fresh, nextList);
332
334
  }
333
335
  flash(t("appShell.sidebarCustomizationVariantCreated", "Variant created."), "success");
334
- return true;
336
+ return { ok: true };
335
337
  } catch (err) {
336
338
  console.error("Failed to create sidebar variant", err);
337
- setError(t("appShell.sidebarCustomizationSaveError"));
338
- return false;
339
+ const message = t("appShell.sidebarCustomizationSaveError");
340
+ if (!options.suppressPageError) setError(message);
341
+ return { ok: false, error: message };
339
342
  } finally {
340
343
  setSaving(false);
341
344
  }
@@ -446,12 +449,16 @@ function SidebarCustomizationEditor({
446
449
  onCanceled?.();
447
450
  }, [onCanceled]);
448
451
  const submitAddDialog = React.useCallback(async () => {
449
- const ok = await createNewVariant(addDialogName);
450
- if (ok) {
452
+ setAddDialogError(null);
453
+ const result = await createNewVariant(addDialogName, { suppressPageError: true });
454
+ if (result.ok) {
451
455
  setAddDialogOpen(false);
452
456
  setAddDialogName("");
457
+ setAddDialogError(null);
458
+ return;
453
459
  }
454
- }, [createNewVariant, addDialogName]);
460
+ setAddDialogError(result.error ?? t("appShell.sidebarCustomizationSaveError"));
461
+ }, [createNewVariant, addDialogName, t]);
455
462
  const sanitizeSettingsPayload = React.useCallback(() => {
456
463
  if (!draft || !baseSnapshotRef.current) return null;
457
464
  const baseGroups = baseSnapshotRef.current;
@@ -719,6 +726,7 @@ function SidebarCustomizationEditor({
719
726
  if (!next) {
720
727
  setAddDialogOpen(false);
721
728
  setAddDialogName("");
729
+ setAddDialogError(null);
722
730
  }
723
731
  },
724
732
  children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
@@ -733,7 +741,10 @@ function SidebarCustomizationEditor({
733
741
  {
734
742
  autoFocus: true,
735
743
  value: addDialogName,
736
- onChange: (event) => setAddDialogName(event.target.value),
744
+ onChange: (event) => {
745
+ setAddDialogName(event.target.value);
746
+ if (addDialogError) setAddDialogError(null);
747
+ },
737
748
  onKeyDown: (event) => {
738
749
  if (event.key === "Enter" && !event.shiftKey) {
739
750
  event.preventDefault();
@@ -741,9 +752,20 @@ function SidebarCustomizationEditor({
741
752
  }
742
753
  },
743
754
  placeholder: t("appShell.sidebarCustomizationVariantNamePlaceholder", "My preferences"),
744
- disabled: saving
755
+ disabled: saving,
756
+ "aria-invalid": addDialogError ? true : void 0,
757
+ "aria-describedby": addDialogError ? "sidebar-add-variant-error" : void 0
745
758
  }
746
- )
759
+ ),
760
+ addDialogError ? /* @__PURE__ */ jsx(
761
+ "p",
762
+ {
763
+ id: "sidebar-add-variant-error",
764
+ role: "alert",
765
+ className: "text-xs text-destructive",
766
+ children: addDialogError
767
+ }
768
+ ) : null
747
769
  ] }),
748
770
  /* @__PURE__ */ jsxs(DialogFooter, { className: "mt-2", children: [
749
771
  /* @__PURE__ */ jsx(
@@ -754,6 +776,7 @@ function SidebarCustomizationEditor({
754
776
  onClick: () => {
755
777
  setAddDialogOpen(false);
756
778
  setAddDialogName("");
779
+ setAddDialogError(null);
757
780
  },
758
781
  disabled: saving,
759
782
  children: t("appShell.sidebarCustomizationCancel")
@@ -832,6 +855,7 @@ function SidebarCustomizationEditor({
832
855
  type: "button",
833
856
  onClick: () => {
834
857
  setAddDialogName("");
858
+ setAddDialogError(null);
835
859
  setAddDialogOpen(true);
836
860
  },
837
861
  disabled: isBusy,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/sidebar/SidebarCustomizationEditor.tsx"],
4
- "sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { ChevronUp, ChevronDown, GripVertical, RotateCcw, Trash2, Plus, Search, AlertTriangle } from 'lucide-react'\nimport { DndContext, closestCenter, PointerSensor, KeyboardSensor, useSensor, useSensors, type DragEndEvent } from '@dnd-kit/core'\nimport { SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\nimport Image from 'next/image'\nimport { resolveInjectedIcon } from '../injection/resolveInjectedIcon'\nimport { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport { IconButton } from '../../primitives/icon-button'\nimport { Input } from '../../primitives/input'\nimport { Switch } from '../../primitives/switch'\nimport { Card, CardContent, CardHeader, CardTitle } from '../../primitives/card'\nimport { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../../primitives/dialog'\nimport { Tag } from '../../primitives/tag'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../../primitives/select'\nimport { apiCall } from '../utils/apiCall'\nimport { flash } from '../FlashMessages'\nimport { Page, PageBody } from '../Page'\nimport { useBackendChrome } from '../BackendChromeProvider'\nimport { useConfirmDialog } from '../confirm-dialog'\nimport { useGuardedMutation } from '../injection/useGuardedMutation'\nimport {\n applyCustomizationDraft,\n applyItemOrder,\n cloneSidebarGroups,\n collectSidebarDefaults,\n filterMainSidebarGroups,\n mergeGroupOrder,\n resolveGroupKey,\n resolveItemKey,\n type SidebarCustomizationDraft,\n type SidebarGroup,\n type SidebarItem,\n} from './customization-helpers'\n\nexport type SidebarCustomizationEditorProps = {\n onSaved?: () => void\n onCanceled?: () => void\n variantsApiPath?: string\n preferencesApiPath?: string\n groups?: SidebarGroup[]\n}\n\nconst VARIANTS_API_DEFAULT = '/api/auth/sidebar/variants'\nconst PREFERENCES_API_DEFAULT = '/api/auth/sidebar/preferences'\nconst REFRESH_SIDEBAR_EVENT = 'om:refresh-sidebar'\nconst NEW_VARIANT_KEY = '__new__'\n\n// Surface server-provided error messages directly when present (4xx with `error` field\n// like 409 duplicate-name); fall back to the generic copy + status code for opaque 5xx.\nfunction formatVariantApiError(\n call: { ok: boolean; status: number; result: unknown },\n t: (key: string, fallback?: string) => string,\n): string {\n const detail = (call.result as { error?: unknown } | null)?.error\n if (typeof detail === 'string' && detail.length > 0 && call.status >= 400 && call.status < 500) {\n return detail\n }\n if (typeof detail === 'string' && detail.length > 0) {\n return `${t('appShell.sidebarCustomizationSaveError')} (${call.status}: ${detail})`\n }\n return `${t('appShell.sidebarCustomizationSaveError')} (${call.status})`\n}\n\ntype RoleTarget = {\n id: string\n name: string\n hasPreference: boolean\n}\n\ntype VariantSettings = {\n version: number\n groupOrder: string[]\n groupLabels: Record<string, string>\n itemLabels: Record<string, string>\n hiddenItems: string[]\n itemOrder?: Record<string, string[]>\n}\n\ntype Variant = {\n id: string\n name: string\n isActive: boolean\n settings: VariantSettings\n createdAt: string\n updatedAt: string | null\n}\n\ntype VariantListResponse = { locale: string; variants: Variant[] }\ntype VariantSingleResponse = { locale: string; variant: Variant }\n\nfunction findItemByKey(items: SidebarItem[], targetKey: string): SidebarItem | null {\n for (const item of items) {\n if (resolveItemKey(item) === targetKey) return item\n if (item.children && item.children.length > 0) {\n const found = findItemByKey(item.children, targetKey)\n if (found) return found\n }\n }\n return null\n}\n\nfunction collectDescendantKeys(item: SidebarItem): string[] {\n const out: string[] = []\n const walk = (node: SidebarItem) => {\n if (!node.children) return\n for (const child of node.children) {\n out.push(resolveItemKey(child))\n walk(child)\n }\n }\n walk(item)\n return out\n}\n\nfunction parseDraftFromSettings(\n rawSettings: VariantSettings | null | undefined,\n baseSnapshot: SidebarGroup[],\n): SidebarCustomizationDraft {\n const responseOrder = Array.isArray(rawSettings?.groupOrder)\n ? rawSettings.groupOrder\n .map((id) => (typeof id === 'string' ? id.trim() : ''))\n .filter((id) => id.length > 0)\n : []\n const responseGroupLabels: Record<string, string> = {}\n if (rawSettings?.groupLabels && typeof rawSettings.groupLabels === 'object') {\n for (const [key, value] of Object.entries(rawSettings.groupLabels)) {\n if (typeof value !== 'string') continue\n const trimmedKey = key.trim()\n if (!trimmedKey) continue\n responseGroupLabels[trimmedKey] = value\n }\n }\n const responseItemLabels: Record<string, string> = {}\n if (rawSettings?.itemLabels && typeof rawSettings.itemLabels === 'object') {\n for (const [key, value] of Object.entries(rawSettings.itemLabels)) {\n if (typeof value !== 'string') continue\n const trimmedKey = key.trim()\n if (!trimmedKey) continue\n responseItemLabels[trimmedKey] = value\n }\n }\n const responseHiddenItems = Array.isArray(rawSettings?.hiddenItems)\n ? rawSettings.hiddenItems\n .map((itemId) => (typeof itemId === 'string' ? itemId.trim() : ''))\n .filter((itemId) => itemId.length > 0)\n : []\n const responseItemOrder: Record<string, string[]> = {}\n if (rawSettings?.itemOrder && typeof rawSettings.itemOrder === 'object') {\n for (const [groupKey, list] of Object.entries(rawSettings.itemOrder)) {\n if (!Array.isArray(list)) continue\n const trimmedGroup = groupKey.trim()\n if (!trimmedGroup) continue\n const seen = new Set<string>()\n const values: string[] = []\n for (const itemKey of list) {\n if (typeof itemKey !== 'string') continue\n const trimmedItem = itemKey.trim()\n if (!trimmedItem || seen.has(trimmedItem)) continue\n seen.add(trimmedItem)\n values.push(trimmedItem)\n }\n if (values.length > 0) responseItemOrder[trimmedGroup] = values\n }\n }\n const currentIds = baseSnapshot.map((group) => resolveGroupKey(group))\n const order = mergeGroupOrder(responseOrder, currentIds)\n const { itemDefaults } = collectSidebarDefaults(baseSnapshot)\n const hiddenItemIds: Record<string, boolean> = {}\n for (const itemId of responseHiddenItems) {\n if (!itemDefaults.has(itemId)) continue\n hiddenItemIds[itemId] = true\n }\n return {\n order,\n groupLabels: responseGroupLabels,\n itemLabels: responseItemLabels,\n hiddenItemIds,\n itemOrder: responseItemOrder,\n }\n}\n\nfunction emptyDraftFor(baseSnapshot: SidebarGroup[]): SidebarCustomizationDraft {\n return {\n order: baseSnapshot.map((group) => resolveGroupKey(group)),\n groupLabels: {},\n itemLabels: {},\n hiddenItemIds: {},\n itemOrder: {},\n }\n}\n\nexport function SidebarCustomizationEditor({\n onSaved,\n onCanceled,\n variantsApiPath = VARIANTS_API_DEFAULT,\n preferencesApiPath = PREFERENCES_API_DEFAULT,\n groups: groupsProp,\n}: SidebarCustomizationEditorProps) {\n const t = useT()\n const locale = useLocale()\n const localeLabel = (locale || '').toUpperCase()\n const { payload: chromePayload, isLoading: chromeIsLoading } = useBackendChrome()\n const groupsFromChrome = chromePayload?.groups as SidebarGroup[] | undefined\n const sourceGroups = groupsProp ?? groupsFromChrome ?? []\n const { confirm: confirmDialog, ConfirmDialogElement } = useConfirmDialog()\n\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [deleting, setDeleting] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [variants, setVariants] = React.useState<Variant[]>([])\n const [selectedVariantId, setSelectedVariantId] = React.useState<string | null>(null)\n const [variantName, setVariantName] = React.useState('')\n const [draft, setDraft] = React.useState<SidebarCustomizationDraft | null>(null)\n const [previewGroups, setPreviewGroups] = React.useState<SidebarGroup[]>([])\n const [dirty, setDirty] = React.useState(false)\n const [availableRoleTargets, setAvailableRoleTargets] = React.useState<RoleTarget[]>([])\n const [selectedRoleIds, setSelectedRoleIds] = React.useState<string[]>([])\n const [canApplyToRoles, setCanApplyToRoles] = React.useState(false)\n const [addDialogOpen, setAddDialogOpen] = React.useState(false)\n const [addDialogName, setAddDialogName] = React.useState('')\n const baseSnapshotRef = React.useRef<SidebarGroup[] | null>(null)\n const hasInitializedRef = React.useRef(false)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n variantId?: string | null\n operation: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: 'sidebar-customization',\n blockedMessage: t('appShell.sidebarCustomizationSaveError'),\n })\n\n const buildMutationContext = React.useCallback(\n (operation: string, variantId?: string | null) => ({\n formId: 'sidebar-customization',\n variantId: variantId ?? null,\n operation,\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n\n const isNewVariant = selectedVariantId === null\n const selectedVariant = React.useMemo(\n () => (selectedVariantId ? variants.find((v) => v.id === selectedVariantId) ?? null : null),\n [selectedVariantId, variants],\n )\n\n const updateDraft = React.useCallback((updater: (draft: SidebarCustomizationDraft) => SidebarCustomizationDraft) => {\n setDraft((prev) => {\n if (!prev) return prev\n const next = updater(prev)\n if (baseSnapshotRef.current) {\n setPreviewGroups(applyCustomizationDraft(baseSnapshotRef.current, next))\n }\n return next\n })\n setDirty(true)\n }, [])\n\n const buildBaseSnapshot = React.useCallback((): SidebarGroup[] => {\n return filterMainSidebarGroups(cloneSidebarGroups(sourceGroups))\n }, [sourceGroups])\n\n const loadVariantsList = React.useCallback(async (): Promise<Variant[]> => {\n // Cache-bust to prevent stale browser/Next caches from masking just-created variants.\n const url = `${variantsApiPath}?_=${Date.now()}`\n const call = await apiCall<VariantListResponse>(url, { cache: 'no-store' })\n if (!call.ok) {\n throw new Error('list-failed')\n }\n return call.result?.variants ?? []\n }, [variantsApiPath])\n\n const loadRolesPayload = React.useCallback(async (): Promise<{ canApplyToRoles: boolean; roles: RoleTarget[] }> => {\n const call = await apiCall<{ canApplyToRoles?: boolean; roles?: Array<{ id?: string; name?: string; hasPreference?: boolean }> }>(preferencesApiPath)\n if (!call.ok) {\n return { canApplyToRoles: false, roles: [] }\n }\n const data = call.result ?? null\n const can = data?.canApplyToRoles === true\n const roles = Array.isArray(data?.roles)\n ? (data!.roles as Array<{ id?: string; name?: string; hasPreference?: boolean }>)\n .filter((r) => typeof r?.id === 'string' && typeof r?.name === 'string')\n .map((r) => ({ id: r.id as string, name: r.name as string, hasPreference: r.hasPreference === true }))\n : []\n return { canApplyToRoles: can, roles }\n }, [preferencesApiPath])\n\n const selectVariantInternal = React.useCallback((variant: Variant | null, list: Variant[]) => {\n const baseSnapshot = baseSnapshotRef.current ?? buildBaseSnapshot()\n baseSnapshotRef.current = baseSnapshot\n if (variant) {\n const initialDraft = parseDraftFromSettings(variant.settings, baseSnapshot)\n setSelectedVariantId(variant.id)\n setVariantName(variant.name)\n setDraft(initialDraft)\n setPreviewGroups(applyCustomizationDraft(baseSnapshot, initialDraft))\n } else {\n const empty = emptyDraftFor(baseSnapshot)\n setSelectedVariantId(null)\n // Suggest a default name based on the existing variants count.\n const usedNumbers = new Set<number>()\n for (const v of list) {\n if (v.name === 'My preferences') usedNumbers.add(1)\n const match = v.name.match(/^My preferences\\s+(\\d+)$/)\n if (match) usedNumbers.add(Number.parseInt(match[1], 10))\n }\n let next = 1\n while (usedNumbers.has(next)) next += 1\n const suggestion = next === 1 ? 'My preferences' : `My preferences ${next}`\n setVariantName(suggestion)\n setDraft(empty)\n setPreviewGroups(applyCustomizationDraft(baseSnapshot, empty))\n }\n setDirty(false)\n }, [buildBaseSnapshot])\n\n // Initial load. No cancelled flag because React Strict Mode in dev runs effects twice\n // and the cleanup-driven cancellation made the only init pass abort silently \u2014 leaving\n // `loading` true forever and the editor stuck on the \"Loading\u2026\" placeholder. The init\n // gate (`hasInitializedRef`) prevents the second Strict-Mode run from doubling work.\n React.useEffect(() => {\n if (hasInitializedRef.current) return\n if (sourceGroups.length === 0) return\n hasInitializedRef.current = true\n async function init() {\n setLoading(true)\n setError(null)\n try {\n const [list, rolesPayload] = await Promise.all([\n loadVariantsList(),\n loadRolesPayload(),\n ])\n setVariants(list)\n setCanApplyToRoles(rolesPayload.canApplyToRoles)\n setAvailableRoleTargets(rolesPayload.roles)\n const active = list.find((v) => v.isActive)\n const initial = active ?? list[0] ?? null\n selectVariantInternal(initial, list)\n setSelectedRoleIds([])\n } catch (err) {\n console.error('Failed to load sidebar variants', err)\n setError(t('appShell.sidebarCustomizationLoadError'))\n } finally {\n setLoading(false)\n }\n }\n void init()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sourceGroups.length])\n\n const toggleRoleSelection = React.useCallback((roleId: string) => {\n setSelectedRoleIds((prev) => (prev.includes(roleId) ? prev.filter((id) => id !== roleId) : [...prev, roleId]))\n setDirty(true)\n }, [])\n\n const createNewVariant = React.useCallback(async (proposedName?: string): Promise<boolean> => {\n if (saving || deleting) return false\n if (dirty && selectedVariantId !== null) {\n const proceed = await confirmDialog({\n title: t('appShell.sidebarCustomizationSwitchConfirmTitle', 'Discard unsaved changes?'),\n text: t('appShell.sidebarCustomizationSwitchConfirmText', 'You have unsaved changes for the current variant. Switching will discard them.'),\n confirmText: t('appShell.sidebarCustomizationSwitchConfirmYes', 'Discard and switch'),\n cancelText: t('common.cancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!proceed) return false\n }\n setSaving(true)\n setError(null)\n try {\n const baseSnapshot = baseSnapshotRef.current ?? buildBaseSnapshot()\n baseSnapshotRef.current = baseSnapshot\n const groupOrder = baseSnapshot.map((g) => resolveGroupKey(g))\n const trimmed = (proposedName ?? '').trim()\n const call = await runMutation({\n operation: () =>\n apiCall<VariantSingleResponse>(variantsApiPath, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n // If name is omitted, server auto-names (\"My preferences\", \"My preferences 2\", \u2026).\n name: trimmed.length > 0 ? trimmed : undefined,\n settings: { groupOrder, groupLabels: {}, itemLabels: {}, hiddenItems: [], itemOrder: {} },\n isActive: true,\n }),\n }),\n context: buildMutationContext('createVariant'),\n mutationPayload: { name: trimmed.length > 0 ? trimmed : null },\n })\n if (!call.ok) {\n setError(formatVariantApiError(call, t))\n return false\n }\n const created = call.result?.variant ?? null\n // Trust POST response as authoritative; refetch in background for any side-effects\n // (e.g. server-side deactivation of previous active variant).\n let nextList: Variant[]\n try {\n nextList = await loadVariantsList()\n } catch {\n nextList = variants\n }\n // Defensive merge: ensure the just-created variant is in the list even if the\n // refetch happened to be served from a stale cache.\n if (created && !nextList.some((v) => v.id === created.id)) {\n nextList = [...nextList, created]\n }\n setVariants(nextList)\n if (created) {\n const fresh = nextList.find((v) => v.id === created.id) ?? created\n selectVariantInternal(fresh, nextList)\n }\n flash(t('appShell.sidebarCustomizationVariantCreated', 'Variant created.'), 'success')\n return true\n } catch (err) {\n console.error('Failed to create sidebar variant', err)\n setError(t('appShell.sidebarCustomizationSaveError'))\n return false\n } finally {\n setSaving(false)\n }\n }, [saving, deleting, dirty, selectedVariantId, confirmDialog, t, buildBaseSnapshot, variantsApiPath, loadVariantsList, selectVariantInternal, variants, runMutation, buildMutationContext])\n\n const handleVariantSwitch = React.useCallback(async (key: string) => {\n if (saving || deleting) return\n if (key === selectedVariantId) return\n if (key === NEW_VARIANT_KEY && isNewVariant) return\n if (dirty) {\n const proceed = await confirmDialog({\n title: t('appShell.sidebarCustomizationSwitchConfirmTitle', 'Discard unsaved changes?'),\n text: t('appShell.sidebarCustomizationSwitchConfirmText', 'You have unsaved changes for the current variant. Switching will discard them.'),\n confirmText: t('appShell.sidebarCustomizationSwitchConfirmYes', 'Discard and switch'),\n cancelText: t('common.cancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!proceed) return\n }\n if (key === NEW_VARIANT_KEY) {\n selectVariantInternal(null, variants)\n return\n }\n const next = variants.find((v) => v.id === key) ?? null\n selectVariantInternal(next, variants)\n }, [saving, deleting, selectedVariantId, isNewVariant, dirty, confirmDialog, t, variants, selectVariantInternal])\n\n const moveGroup = React.useCallback((groupId: string, offset: number) => {\n updateDraft((draft) => {\n const order = [...draft.order]\n const index = order.indexOf(groupId)\n if (index === -1) return draft\n const nextIndex = Math.max(0, Math.min(order.length - 1, index + offset))\n if (nextIndex === index) return draft\n order.splice(index, 1)\n order.splice(nextIndex, 0, groupId)\n return { ...draft, order }\n })\n }, [updateDraft])\n\n const dndSensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor),\n )\n\n const handleItemDragEnd = React.useCallback((groupKey: string, currentItemKeys: string[]) => (event: DragEndEvent) => {\n const { active, over } = event\n if (!over || active.id === over.id) return\n const fromId = String(active.id)\n const toId = String(over.id)\n updateDraft((draft) => {\n const baseOrder = draft.itemOrder?.[groupKey]?.length\n ? [...draft.itemOrder[groupKey]]\n : [...currentItemKeys]\n const fromIndex = baseOrder.indexOf(fromId)\n const toIndex = baseOrder.indexOf(toId)\n if (fromIndex === -1 || toIndex === -1) return draft\n const nextOrder = arrayMove(baseOrder, fromIndex, toIndex)\n return {\n ...draft,\n itemOrder: { ...(draft.itemOrder ?? {}), [groupKey]: nextOrder },\n }\n })\n }, [updateDraft])\n\n const setGroupLabel = React.useCallback((groupId: string, value: string) => {\n updateDraft((draft) => {\n const next = { ...draft.groupLabels }\n if (value.trim().length === 0) delete next[groupId]\n else next[groupId] = value\n return { ...draft, groupLabels: next }\n })\n }, [updateDraft])\n\n const setItemLabel = React.useCallback((itemId: string, value: string) => {\n updateDraft((draft) => {\n const next = { ...draft.itemLabels }\n if (value.trim().length === 0) delete next[itemId]\n else next[itemId] = value\n return { ...draft, itemLabels: next }\n })\n }, [updateDraft])\n\n const setItemHidden = React.useCallback((itemId: string, hidden: boolean) => {\n updateDraft((draft) => {\n const next = { ...draft.hiddenItemIds }\n const apply = (id: string) => {\n if (hidden) next[id] = true\n else delete next[id]\n }\n apply(itemId)\n // Cascade: hiding a parent hides every descendant; showing it reveals them too.\n if (baseSnapshotRef.current) {\n for (const group of baseSnapshotRef.current) {\n const target = findItemByKey(group.items, itemId)\n if (!target) continue\n for (const descendantKey of collectDescendantKeys(target)) apply(descendantKey)\n break\n }\n }\n return { ...draft, hiddenItemIds: next }\n })\n }, [updateDraft])\n\n const reset = React.useCallback(() => {\n if (!baseSnapshotRef.current) return\n if (selectedVariant) {\n const initialDraft = parseDraftFromSettings(selectedVariant.settings, baseSnapshotRef.current)\n setDraft(initialDraft)\n setPreviewGroups(applyCustomizationDraft(baseSnapshotRef.current, initialDraft))\n } else {\n const empty = emptyDraftFor(baseSnapshotRef.current)\n setDraft(empty)\n setPreviewGroups(applyCustomizationDraft(baseSnapshotRef.current, empty))\n }\n setDirty(false)\n }, [selectedVariant])\n\n const cancel = React.useCallback(() => {\n onCanceled?.()\n }, [onCanceled])\n\n const submitAddDialog = React.useCallback(async () => {\n const ok = await createNewVariant(addDialogName)\n if (ok) {\n setAddDialogOpen(false)\n setAddDialogName('')\n }\n }, [createNewVariant, addDialogName])\n\n const sanitizeSettingsPayload = React.useCallback(() => {\n if (!draft || !baseSnapshotRef.current) return null\n const baseGroups = baseSnapshotRef.current\n const { groupDefaults, itemDefaults } = collectSidebarDefaults(baseGroups)\n const sanitizedGroupLabels: Record<string, string> = {}\n for (const [key, value] of Object.entries(draft.groupLabels)) {\n const trimmed = value.trim()\n const base = groupDefaults.get(key)\n if (!trimmed || !base) continue\n if (trimmed !== base) sanitizedGroupLabels[key] = trimmed\n }\n const sanitizedItemLabels: Record<string, string> = {}\n for (const [itemId, value] of Object.entries(draft.itemLabels)) {\n const trimmed = value.trim()\n const base = itemDefaults.get(itemId)\n if (!trimmed || !base) continue\n if (trimmed !== base) sanitizedItemLabels[itemId] = trimmed\n }\n const sanitizedHiddenItems: string[] = []\n for (const [itemId, hidden] of Object.entries(draft.hiddenItemIds)) {\n if (!hidden) continue\n if (!itemDefaults.has(itemId)) continue\n sanitizedHiddenItems.push(itemId)\n }\n // Build a Set of valid group keys to drop stale itemOrder entries.\n const groupKeys = new Set<string>()\n for (const group of baseGroups) groupKeys.add(resolveGroupKey(group))\n const sanitizedItemOrder: Record<string, string[]> = {}\n for (const [groupKey, list] of Object.entries(draft.itemOrder ?? {})) {\n if (!groupKeys.has(groupKey)) continue\n const seen = new Set<string>()\n const values: string[] = []\n for (const itemKey of list) {\n if (seen.has(itemKey)) continue\n if (!itemDefaults.has(itemKey)) continue\n seen.add(itemKey)\n values.push(itemKey)\n }\n if (values.length > 0) sanitizedItemOrder[groupKey] = values\n }\n return {\n groupOrder: draft.order,\n groupLabels: sanitizedGroupLabels,\n itemLabels: sanitizedItemLabels,\n hiddenItems: sanitizedHiddenItems,\n itemOrder: sanitizedItemOrder,\n }\n }, [draft])\n\n const save = React.useCallback(async () => {\n const settings = sanitizeSettingsPayload()\n if (!settings) return\n setSaving(true)\n setError(null)\n try {\n const trimmedName = variantName.trim()\n const isCurrentlyActive = selectedVariant?.isActive ?? false\n let savedVariant: Variant | null = null\n if (isNewVariant) {\n const call = await runMutation({\n operation: () =>\n apiCall<VariantSingleResponse>(variantsApiPath, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n name: trimmedName.length > 0 ? trimmedName : undefined,\n settings,\n // New variants are activated by default \u2014 there's only one active per scope,\n // others get auto-deactivated server-side.\n isActive: true,\n }),\n }),\n context: buildMutationContext('saveVariant'),\n mutationPayload: { name: trimmedName.length > 0 ? trimmedName : null, isActive: true },\n })\n if (!call.ok) {\n setError(formatVariantApiError(call, t))\n return\n }\n savedVariant = call.result?.variant ?? null\n } else if (selectedVariantId) {\n const call = await runMutation({\n operation: () =>\n apiCall<VariantSingleResponse>(`${variantsApiPath}/${encodeURIComponent(selectedVariantId)}`, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n name: trimmedName.length > 0 ? trimmedName : undefined,\n settings,\n isActive: isCurrentlyActive,\n }),\n }),\n context: buildMutationContext('saveVariant', selectedVariantId),\n mutationPayload: {\n id: selectedVariantId,\n name: trimmedName.length > 0 ? trimmedName : null,\n isActive: isCurrentlyActive,\n },\n })\n if (!call.ok) {\n setError(formatVariantApiError(call, t))\n return\n }\n savedVariant = call.result?.variant ?? null\n }\n // Sync user prefs and (optionally) push to roles via the legacy preferences endpoint.\n // The variant entity is the canonical \"saved layout\"; the preferences endpoint is what\n // the AppShell sidebar actually reads. Without this sync, the saved variant wouldn't\n // become the user's live sidebar.\n const preferencesPayload: Record<string, unknown> = {\n groupOrder: settings.groupOrder,\n groupLabels: settings.groupLabels,\n itemLabels: settings.itemLabels,\n hiddenItems: settings.hiddenItems,\n itemOrder: settings.itemOrder,\n }\n if (canApplyToRoles) {\n const applyToRolesPayload = [...selectedRoleIds]\n const clearRoleIdsPayload = availableRoleTargets\n .filter((role) => role.hasPreference && !selectedRoleIds.includes(role.id))\n .map((role) => role.id)\n preferencesPayload.applyToRoles = applyToRolesPayload\n preferencesPayload.clearRoleIds = clearRoleIdsPayload\n }\n const preferencesCall = await runMutation({\n operation: () =>\n apiCall(preferencesApiPath, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(preferencesPayload),\n }),\n context: buildMutationContext('savePreferences', selectedVariantId),\n mutationPayload: preferencesPayload,\n })\n if (!preferencesCall.ok) {\n // The variant entity is the canonical layout; the preferences sync is what the\n // AppShell sidebar actually reads. A failed sync would leave the saved variant\n // not reflected live, so surface it as a save error rather than flashing success.\n setError(formatVariantApiError(preferencesCall, t))\n return\n }\n try { window.dispatchEvent(new Event(REFRESH_SIDEBAR_EVENT)) } catch { /* no listener attached \u2014 fine, AppShell will refresh on next navigation */ }\n // Refresh the list so isActive flags are accurate, plus refresh roles so hasPreference flags update.\n const [list, rolesPayload] = await Promise.all([\n loadVariantsList(),\n loadRolesPayload(),\n ])\n // Defensive: ensure the just-saved variant lands in the list even if the refetch\n // was served stale (browser HTTP cache, etc.).\n const mergedList = savedVariant && !list.some((v) => v.id === savedVariant!.id)\n ? [...list, savedVariant]\n : list\n setVariants(mergedList)\n setCanApplyToRoles(rolesPayload.canApplyToRoles)\n setAvailableRoleTargets(rolesPayload.roles)\n if (savedVariant) {\n const fresh = mergedList.find((v) => v.id === savedVariant!.id) ?? savedVariant\n selectVariantInternal(fresh, mergedList)\n } else {\n const active = mergedList.find((v) => v.isActive) ?? mergedList[0] ?? null\n selectVariantInternal(active, mergedList)\n }\n flash(\n isNewVariant\n ? t('appShell.sidebarCustomizationVariantCreated', 'Variant created.')\n : t('appShell.sidebarCustomizationVariantSaved', 'Variant saved.'),\n 'success',\n )\n onSaved?.()\n } catch (err) {\n console.error('Failed to save sidebar variant', err)\n setError(t('appShell.sidebarCustomizationSaveError'))\n } finally {\n setSaving(false)\n }\n }, [draft, variantName, isNewVariant, selectedVariant, selectedVariantId, variantsApiPath, preferencesApiPath, canApplyToRoles, selectedRoleIds, availableRoleTargets, t, sanitizeSettingsPayload, loadVariantsList, loadRolesPayload, selectVariantInternal, onSaved, runMutation, buildMutationContext])\n\n const toggleActive = React.useCallback(async (next: boolean) => {\n if (!selectedVariant || saving || deleting) return\n setError(null)\n try {\n const call = await runMutation({\n operation: () =>\n apiCall<VariantSingleResponse>(`${variantsApiPath}/${encodeURIComponent(selectedVariant.id)}`, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isActive: next }),\n }),\n context: buildMutationContext('toggleVariantActive', selectedVariant.id),\n mutationPayload: { id: selectedVariant.id, isActive: next },\n })\n if (!call.ok) {\n setError(t('appShell.sidebarCustomizationSaveError'))\n return\n }\n try { window.dispatchEvent(new Event(REFRESH_SIDEBAR_EVENT)) } catch { /* no listener attached \u2014 fine, AppShell will refresh on next navigation */ }\n const list = await loadVariantsList()\n setVariants(list)\n const fresh = list.find((v) => v.id === selectedVariant.id) ?? selectedVariant\n selectVariantInternal(fresh, list)\n } catch (err) {\n console.error('Failed to toggle variant active state', err)\n setError(t('appShell.sidebarCustomizationSaveError'))\n }\n }, [selectedVariant, saving, deleting, variantsApiPath, t, loadVariantsList, selectVariantInternal, runMutation, buildMutationContext])\n\n const deleteVariant = React.useCallback(async () => {\n if (!selectedVariant) return\n const proceed = await confirmDialog({\n title: t('appShell.sidebarCustomizationDeleteVariantTitle', 'Delete variant?'),\n text: t(\n 'appShell.sidebarCustomizationDeleteVariantText',\n 'This variant will be removed from your library.',\n ),\n confirmText: t('appShell.sidebarCustomizationDeleteVariantConfirm', 'Delete variant'),\n cancelText: t('common.cancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!proceed) return\n setDeleting(true)\n setError(null)\n try {\n const call = await runMutation({\n operation: () =>\n apiCall(`${variantsApiPath}/${encodeURIComponent(selectedVariant.id)}`, { method: 'DELETE' }),\n context: buildMutationContext('deleteVariant', selectedVariant.id),\n mutationPayload: { id: selectedVariant.id },\n })\n if (!call.ok) {\n setError(t('appShell.sidebarCustomizationSaveError'))\n return\n }\n try { window.dispatchEvent(new Event(REFRESH_SIDEBAR_EVENT)) } catch { /* no listener attached \u2014 fine, AppShell will refresh on next navigation */ }\n const list = await loadVariantsList()\n setVariants(list)\n const fallback = list[0] ?? null\n selectVariantInternal(fallback, list)\n } catch (err) {\n console.error('Failed to delete variant', err)\n setError(t('appShell.sidebarCustomizationSaveError'))\n } finally {\n setDeleting(false)\n }\n }, [selectedVariant, confirmDialog, t, variantsApiPath, loadVariantsList, selectVariantInternal, runMutation, buildMutationContext])\n\n const isBusy = saving || deleting\n\n if (loading && !draft) {\n return (\n <>\n {ConfirmDialogElement}\n <div className=\"space-y-6\">\n <div className=\"space-y-2\">\n <div className=\"h-7 w-64 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-96 animate-pulse rounded bg-muted/60\" />\n </div>\n <div className=\"h-64 animate-pulse rounded-lg border bg-muted/30\" />\n </div>\n </>\n )\n }\n\n if (!draft || !baseSnapshotRef.current) {\n // While chrome payload streams in or the initial fetch runs, show a neutral loading\n // state instead of the error fallback (otherwise the first visit looks like a crash).\n const stillLoading = loading || chromeIsLoading || sourceGroups.length === 0\n return (\n <>\n {ConfirmDialogElement}\n <div className=\"rounded-lg border border-dashed bg-muted/30 p-6 text-sm text-muted-foreground\">\n {stillLoading\n ? t('appShell.sidebarCustomizationLoading', 'Loading\u2026')\n : (error ?? t('appShell.sidebarCustomizationLoadError'))}\n </div>\n </>\n )\n }\n\n const baseGroupsForDefaults = baseSnapshotRef.current\n const baseGroupMap = new Map<string, SidebarGroup>()\n for (const group of baseGroupsForDefaults) {\n baseGroupMap.set(resolveGroupKey(group), group)\n }\n const orderedGroupIds = mergeGroupOrder(draft.order, Array.from(baseGroupMap.keys()))\n const totalGroups = orderedGroupIds.length\n\n const selectValue = isNewVariant ? NEW_VARIANT_KEY : selectedVariantId ?? NEW_VARIANT_KEY\n const showVariantPicker = variants.length > 0 || isNewVariant\n\n return (\n <>\n {ConfirmDialogElement}\n <Dialog\n open={addDialogOpen}\n onOpenChange={(next) => {\n if (!next) {\n setAddDialogOpen(false)\n setAddDialogName('')\n }\n }}\n >\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {t('appShell.sidebarCustomizationAddDialogTitle', 'Add new variant')}\n </DialogTitle>\n <DialogDescription>\n {t('appShell.sidebarCustomizationAddDialogDescription', 'Choose a name for the new sidebar variant. Leave blank to auto-name it.')}\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-xs font-medium uppercase tracking-wider text-muted-foreground/70\">\n {t('appShell.sidebarCustomizationVariantNameLabel', 'Variant name')}\n </label>\n <Input\n autoFocus\n value={addDialogName}\n onChange={(event) => setAddDialogName(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault()\n void submitAddDialog()\n }\n }}\n placeholder={t('appShell.sidebarCustomizationVariantNamePlaceholder', 'My preferences')}\n disabled={saving}\n />\n </div>\n <DialogFooter className=\"mt-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setAddDialogOpen(false)\n setAddDialogName('')\n }}\n disabled={saving}\n >\n {t('appShell.sidebarCustomizationCancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => { void submitAddDialog() }}\n disabled={saving}\n >\n {saving\n ? t('appShell.sidebarCustomizationCreating', 'Creating\u2026')\n : t('appShell.sidebarCustomizationCreateVariant', 'Create variant')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n <Page>\n <header className=\"space-y-1\">\n <h1 className=\"text-xl sm:text-2xl font-semibold leading-tight\">\n {t('appShell.sidebarCustomizationHeading')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('appShell.sidebarCustomizationHint', { locale: localeLabel })}\n </p>\n </header>\n\n {error ? (\n <div className=\"rounded-lg border border-destructive/40 bg-destructive/5 px-4 py-3 text-sm text-destructive\">\n {error}\n </div>\n ) : null}\n\n {/* Two-column: editor (variant + roles + order) + preview */}\n <PageBody className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(0,360px)]\">\n <div className=\"space-y-6\">\n {(() => {\n const showRolesCard = canApplyToRoles && availableRoleTargets.length > 0\n if (!showVariantPicker && !showRolesCard) return null\n return (\n <Card>\n <CardContent className=\"flex flex-col gap-6\">\n {showVariantPicker ? (\n <div className=\"flex flex-col gap-4\">\n {/* Row: combobox-style name input with chevron picker + DS-compliant add button */}\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-xs font-medium uppercase tracking-wider text-muted-foreground/70\">\n {t('appShell.sidebarCustomizationVariantNameLabel', 'Variant name')}\n </label>\n <div className=\"flex items-stretch gap-2\">\n {/* Combobox group: input + chevron picker share a single visual border. */}\n <div className=\"relative flex flex-1 items-stretch\">\n <Input\n value={variantName}\n onChange={(event) => {\n setVariantName(event.target.value)\n setDirty(true)\n }}\n placeholder={t('appShell.sidebarCustomizationVariantNamePlaceholder', 'My preferences')}\n disabled={isBusy}\n className=\"w-full pr-10\"\n />\n <Select\n value={selectValue}\n onValueChange={(value) => { void handleVariantSwitch(value) }}\n disabled={isBusy || loading}\n >\n <SelectTrigger\n className=\"pointer-events-none absolute inset-0 h-full w-full justify-end border-0 bg-transparent px-3 shadow-none ring-0 focus-visible:ring-0 focus-visible:ring-offset-0 [&>span]:hidden [&>svg]:pointer-events-auto\"\n aria-label={t('appShell.sidebarCustomizationVariantPickerLabel', 'Pick variant')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {variants.length > 0 ? (\n variants.map((variant) => (\n <SelectItem key={variant.id} value={variant.id}>\n {variant.name}\n </SelectItem>\n ))\n ) : (\n <SelectItem value={NEW_VARIANT_KEY} disabled>\n {t('appShell.sidebarCustomizationVariantsEmpty', 'No saved variants yet')}\n </SelectItem>\n )}\n </SelectContent>\n </Select>\n </div>\n <Button\n type=\"button\"\n onClick={() => {\n setAddDialogName('')\n setAddDialogOpen(true)\n }}\n disabled={isBusy}\n title={t('appShell.sidebarCustomizationVariantNew', 'Add new variant')}\n >\n <Plus className=\"size-4\" />\n {t('appShell.sidebarCustomizationCreateNew', 'Create new')}\n </Button>\n </div>\n </div>\n\n {isNewVariant ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('appShell.sidebarCustomizationVariantNewHint', 'Saving will create a new variant. If you leave the name blank, it will be auto-named.')}\n </p>\n ) : null}\n\n {/* Row: active switch */}\n <div className=\"flex items-center gap-2\">\n <Switch\n checked={selectedVariant?.isActive ?? isNewVariant}\n onCheckedChange={(next) => {\n if (isNewVariant) return\n void toggleActive(next === true)\n }}\n disabled={isBusy || isNewVariant}\n aria-label={t('appShell.sidebarCustomizationVariantActiveLabel', 'Active')}\n />\n <span className=\"text-xs font-medium uppercase tracking-wider text-muted-foreground/70\">\n {t('appShell.sidebarCustomizationVariantActiveLabel', 'Active')}\n </span>\n </div>\n </div>\n ) : null}\n\n {showVariantPicker && showRolesCard ? (\n <div className=\"-mx-6 border-t\" aria-hidden />\n ) : null}\n\n {showRolesCard ? (\n <div className=\"flex flex-col gap-3\">\n <div className=\"space-y-1\">\n <h3 className=\"text-base font-semibold leading-none text-foreground\">\n {t('appShell.sidebarApplyToRolesTitle')}\n </h3>\n <p className=\"text-sm text-muted-foreground\">{t('appShell.sidebarApplyToRolesDescription')}</p>\n </div>\n <div className=\"flex flex-col gap-1.5 max-w-sm\">\n {availableRoleTargets.map((role) => {\n const checked = selectedRoleIds.includes(role.id)\n const willClear = role.hasPreference && !checked\n return (\n <label\n key={role.id}\n className=\"flex cursor-pointer items-center gap-3 rounded-lg border bg-background px-3 py-2 text-sm transition-colors hover:bg-muted\"\n >\n <Switch\n checked={checked}\n onCheckedChange={() => toggleRoleSelection(role.id)}\n disabled={isBusy}\n />\n <span className=\"flex-1 truncate font-medium text-foreground\">{role.name}</span>\n {role.hasPreference ? (\n <Tag variant={willClear ? 'error' : 'info'} dot={!willClear}>\n {willClear ? <AlertTriangle className=\"size-3\" aria-hidden /> : null}\n {willClear ? t('appShell.sidebarRoleWillClear') : t('appShell.sidebarRoleHasPreset')}\n </Tag>\n ) : null}\n </label>\n )\n })}\n </div>\n </div>\n ) : null}\n\n {/* Footer: Reset / Cancel / Save (right) + Delete (left). All gated on dirty\n except Delete which acts on the persisted variant regardless of edits. */}\n <div className=\"-mx-6 border-t\" aria-hidden />\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n {selectedVariant ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => { void deleteVariant() }}\n disabled={isBusy}\n className=\"text-destructive hover:text-destructive\"\n >\n <Trash2 className=\"size-4\" />\n {deleting\n ? t('appShell.sidebarCustomizationDeleteVariantInProgress', 'Deleting\u2026')\n : t('appShell.sidebarCustomizationDeleteVariant', 'Delete variant')}\n </Button>\n ) : <span />}\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={reset}\n disabled={isBusy || !dirty}\n >\n {t('appShell.sidebarCustomizationReset')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={cancel}\n disabled={isBusy || !dirty}\n >\n {t('appShell.sidebarCustomizationCancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={save}\n disabled={isBusy || (!isNewVariant && !dirty)}\n >\n {saving\n ? (isNewVariant\n ? t('appShell.sidebarCustomizationCreating', 'Creating\u2026')\n : t('appShell.sidebarCustomizationSaving'))\n : (isNewVariant\n ? t('appShell.sidebarCustomizationCreateVariant', 'Create variant')\n : t('appShell.sidebarCustomizationSave'))}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n )\n })()}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">\n {t('appShell.sidebarCustomizationOrderHeading', 'Order & visibility')}\n </CardTitle>\n <p className=\"text-sm text-muted-foreground\">\n {t('appShell.sidebarCustomizationOrderDescription', 'Reorder groups, rename them, and toggle individual items on or off.')}\n </p>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {orderedGroupIds.map((groupId, index) => {\n const baseGroup = baseGroupMap.get(groupId)\n if (!baseGroup) return null\n const placeholder = baseGroup.defaultName ?? baseGroup.name\n const value = draft.groupLabels[groupId] ?? ''\n const trimmedValue = value.trim()\n const isGroupModified = trimmedValue.length > 0 && trimmedValue !== placeholder\n return (\n <div key={groupId} className=\"rounded-lg border bg-background\">\n <div className=\"flex items-start gap-3 border-b px-4 py-3\">\n <div className=\"flex flex-1 flex-col gap-1.5\">\n <label className=\"text-xs font-medium uppercase tracking-wider text-muted-foreground/70\">\n {t('appShell.sidebarCustomizationGroupLabel')}\n </label>\n <div className=\"flex items-center gap-2\">\n <Input\n value={value}\n onChange={(event) => setGroupLabel(groupId, event.target.value)}\n placeholder={placeholder}\n disabled={isBusy}\n className=\"flex-1\"\n />\n {isGroupModified ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setGroupLabel(groupId, '')}\n disabled={isBusy}\n aria-label={t('appShell.sidebarCustomizationResetField', 'Reset to default')}\n title={t('appShell.sidebarCustomizationResetField', 'Reset to default')}\n >\n <RotateCcw className=\"size-3.5\" />\n </IconButton>\n ) : null}\n </div>\n {isGroupModified ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('appShell.sidebarCustomizationDefault', 'Default:')}{' '}\n <span className=\"font-medium text-foreground/80\">{placeholder}</span>\n </p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-1 mt-[26px]\">\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"text-muted-foreground hover:text-foreground\"\n onClick={() => moveGroup(groupId, -1)}\n disabled={index === 0 || isBusy}\n aria-label={t('appShell.sidebarCustomizationMoveUp')}\n title={t('appShell.sidebarCustomizationMoveUp')}\n >\n <ChevronUp className=\"size-4\" />\n </IconButton>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"text-muted-foreground hover:text-foreground\"\n onClick={() => moveGroup(groupId, 1)}\n disabled={index === totalGroups - 1 || isBusy}\n aria-label={t('appShell.sidebarCustomizationMoveDown')}\n title={t('appShell.sidebarCustomizationMoveDown')}\n >\n <ChevronDown className=\"size-4\" />\n </IconButton>\n </div>\n </div>\n <div className=\"flex flex-col divide-y\">\n <ItemRows\n items={baseGroup.items}\n draft={draft}\n saving={isBusy}\n onLabelChange={setItemLabel}\n onHiddenChange={setItemHidden}\n t={t}\n groupKey={groupId}\n sensors={dndSensors}\n onDragEnd={handleItemDragEnd(groupId, baseGroup.items.map((item) => resolveItemKey(item)))}\n />\n </div>\n </div>\n )\n })}\n </CardContent>\n </Card>\n </div>\n\n <aside className=\"hidden lg:block\">\n <div className=\"sticky top-6\">\n <div className=\"relative\">\n <span className=\"absolute left-1/2 top-0 z-10 -translate-x-1/2 -translate-y-1/2 rounded-md bg-accent-indigo px-3 py-1 text-xs font-semibold uppercase tracking-wider text-accent-indigo-foreground shadow-sm\">\n {t('appShell.sidebarCustomizationPreview', 'Preview')}\n </span>\n <SidebarPreview\n groups={previewGroups}\n productName={t('appShell.productName', 'Open Mercato')}\n pickFirstActive\n />\n </div>\n </div>\n </aside>\n </PageBody>\n </Page>\n </>\n )\n}\n\ntype ItemRowProps = {\n item: SidebarItem\n draft: SidebarCustomizationDraft\n saving: boolean\n onLabelChange: (itemId: string, value: string) => void\n onHiddenChange: (itemId: string, hidden: boolean) => void\n t: ReturnType<typeof useT>\n depth: number\n dragHandle?: React.ReactNode\n /** True when an ancestor in the tree is hidden \u2014 child controls become read-only. */\n ancestorHidden?: boolean\n}\n\nfunction ItemRow({ item, draft, saving, onLabelChange, onHiddenChange, t, depth, dragHandle, ancestorHidden = false }: ItemRowProps) {\n const itemKey = resolveItemKey(item)\n const placeholder = item.defaultTitle ?? item.title\n const value = draft.itemLabels[itemKey] ?? ''\n const trimmedValue = value.trim()\n const isModified = trimmedValue.length > 0 && trimmedValue !== placeholder\n const hidden = draft.hiddenItemIds[itemKey] === true\n const effectivelyDimmed = hidden || ancestorHidden\n return (\n <div\n className=\"flex items-start gap-3 px-4 py-3 transition-colors hover:bg-muted/40\"\n style={depth ? { paddingLeft: 16 + depth * 24 } : undefined}\n >\n {dragHandle ?? (depth > 0 ? <span className=\"w-4 shrink-0\" aria-hidden /> : null)}\n <div className={`min-w-0 flex-1 flex flex-col gap-1.5 ${effectivelyDimmed ? 'opacity-60' : ''}`}>\n <div className=\"flex items-center gap-2\">\n <div className=\"min-w-0 flex-1\">\n <Input\n value={value}\n onChange={(event) => onLabelChange(itemKey, event.target.value)}\n placeholder={placeholder}\n disabled={saving}\n />\n </div>\n {isModified ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onLabelChange(itemKey, '')}\n disabled={saving}\n aria-label={t('appShell.sidebarCustomizationResetField', 'Reset to default')}\n title={t('appShell.sidebarCustomizationResetField', 'Reset to default')}\n >\n <RotateCcw className=\"size-3.5\" />\n </IconButton>\n ) : null}\n </div>\n {isModified ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('appShell.sidebarCustomizationDefault', 'Default:')}{' '}\n <span className=\"font-medium text-foreground/80\">{placeholder}</span>\n </p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-2 pt-1.5\">\n {hidden ? (\n <span className=\"rounded-full border border-border bg-muted px-2 py-0.5 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('appShell.sidebarCustomizationHiddenBadge', 'Hidden')}\n </span>\n ) : null}\n <Switch\n checked={!hidden}\n onCheckedChange={(next) => onHiddenChange(itemKey, next !== true)}\n disabled={saving || ancestorHidden}\n aria-label={t('appShell.sidebarCustomizationShowItem')}\n title={ancestorHidden ? t('appShell.sidebarCustomizationParentHiddenHint', 'Parent is hidden \u2014 show parent first.') : undefined}\n />\n </div>\n </div>\n )\n}\n\ntype SortableItemRowProps = ItemRowProps & { id: string }\n\nfunction SortableItemRow({ id, ...rowProps }: SortableItemRowProps) {\n const t = useT()\n const { attributes, listeners, setNodeRef, transform, transition, isDragging, setActivatorNodeRef } = useSortable({ id })\n const style: React.CSSProperties = {\n transform: CSS.Transform.toString(transform),\n transition,\n opacity: isDragging ? 0.5 : 1,\n }\n const dragHandle = (\n <IconButton\n ref={setActivatorNodeRef}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"shrink-0 mt-1.5 cursor-grab touch-none active:cursor-grabbing\"\n aria-label={t('appShell.sidebarCustomizationDragToReorder', 'Drag to reorder')}\n disabled={rowProps.saving}\n {...attributes}\n {...listeners}\n >\n <GripVertical className=\"size-4\" />\n </IconButton>\n )\n return (\n <div ref={setNodeRef} style={style}>\n <ItemRow {...rowProps} dragHandle={dragHandle} />\n </div>\n )\n}\n\ntype ItemRowsProps = {\n items: SidebarItem[]\n draft: SidebarCustomizationDraft\n saving: boolean\n onLabelChange: (itemId: string, value: string) => void\n onHiddenChange: (itemId: string, hidden: boolean) => void\n t: ReturnType<typeof useT>\n depth?: number\n groupKey?: string\n sensors?: ReturnType<typeof useSensors>\n onDragEnd?: (event: DragEndEvent) => void\n ancestorHidden?: boolean\n}\n\nfunction ItemRows({\n items,\n draft,\n saving,\n onLabelChange,\n onHiddenChange,\n t,\n depth = 0,\n groupKey,\n sensors,\n onDragEnd,\n ancestorHidden = false,\n}: ItemRowsProps) {\n if (items.length === 0) return null\n\n const renderRecursiveChildren = (item: SidebarItem, parentHidden: boolean) =>\n item.children && item.children.length > 0 ? (\n <ItemRows\n items={item.children}\n draft={draft}\n saving={saving}\n onLabelChange={onLabelChange}\n onHiddenChange={onHiddenChange}\n t={t}\n depth={depth + 1}\n ancestorHidden={parentHidden}\n />\n ) : null\n\n // Top-level rows in a group \u2192 enable DnD reordering.\n if (depth === 0 && groupKey && sensors && onDragEnd) {\n const ordered = applyItemOrder(items, resolveItemKey, draft.itemOrder?.[groupKey])\n const ids = ordered.map((item) => resolveItemKey(item))\n return (\n <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={onDragEnd}>\n <SortableContext items={ids} strategy={verticalListSortingStrategy}>\n {ordered.map((item) => {\n const itemKey = resolveItemKey(item)\n const ownHidden = draft.hiddenItemIds[itemKey] === true\n return (\n <React.Fragment key={itemKey}>\n <SortableItemRow\n id={itemKey}\n item={item}\n draft={draft}\n saving={saving}\n onLabelChange={onLabelChange}\n onHiddenChange={onHiddenChange}\n t={t}\n depth={depth}\n ancestorHidden={ancestorHidden}\n />\n {renderRecursiveChildren(item, ancestorHidden || ownHidden)}\n </React.Fragment>\n )\n })}\n </SortableContext>\n </DndContext>\n )\n }\n\n // Nested children \u2192 static rendering, no drag handle.\n return (\n <>\n {items.map((item) => {\n const itemKey = resolveItemKey(item)\n const ownHidden = draft.hiddenItemIds[itemKey] === true\n return (\n <React.Fragment key={itemKey}>\n <ItemRow\n item={item}\n draft={draft}\n saving={saving}\n onLabelChange={onLabelChange}\n onHiddenChange={onHiddenChange}\n t={t}\n depth={depth}\n ancestorHidden={ancestorHidden}\n />\n {renderRecursiveChildren(item, ancestorHidden || ownHidden)}\n </React.Fragment>\n )\n })}\n </>\n )\n}\n\nfunction SidebarPreviewIcon({ item }: { item: SidebarItem }) {\n if (item.icon) return <>{item.icon}</>\n if (item.iconName) {\n const resolved = resolveInjectedIcon(item.iconName)\n if (resolved) return <>{resolved}</>\n }\n if (item.iconMarkup) {\n return <span aria-hidden=\"true\" dangerouslySetInnerHTML={{ __html: item.iconMarkup }} />\n }\n // Fallback default icon \u2014 same shape as AppShell's DefaultIcon\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )\n}\n\nfunction SidebarPreview({\n groups,\n productName,\n pickFirstActive,\n}: {\n groups: SidebarGroup[]\n productName: string\n pickFirstActive: boolean\n}) {\n const t = useT()\n // Pre-compute the first visible item so we can render it as the \"active\" preview state.\n // This shows the user what the active state of their sidebar will look like.\n const activeKey = React.useMemo<string | null>(() => {\n if (!pickFirstActive) return null\n for (const group of groups) {\n for (const item of group.items) {\n if (item.hidden === true) continue\n return resolveItemKey(item)\n }\n }\n return null\n }, [groups, pickFirstActive])\n\n return (\n <div className=\"relative w-[240px] overflow-hidden rounded-xl border bg-background shadow-sm\">\n {/* Match AppShell's outer aside: border-r, py-4, px-3 \u2014 minus border-r since the\n card border already serves that purpose, plus rounded so it reads as a preview tile. */}\n <div className=\"flex flex-col gap-3 px-3 py-4\">\n {/* Brand block \u2014 same classes as AppShell brand tile */}\n <div className=\"mb-2\">\n <div className=\"flex items-center gap-3 rounded-xl p-3\">\n <Image\n src=\"/open-mercato.svg\"\n alt={productName}\n width={40}\n height={40}\n className=\"rounded-full shrink-0\"\n />\n <span className=\"text-sm font-medium text-foreground truncate\">{productName}</span>\n </div>\n </div>\n {/* Search input mock \u2014 same container styling as the real sidebar */}\n <div className=\"mb-2 flex items-center gap-2 rounded-lg border border-border bg-background pl-2.5 pr-2 py-2 shadow-sm\">\n <Search className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 text-sm text-muted-foreground/70 truncate\">\n {t('appShell.sidebarCustomizationPreviewSearchPlaceholder', 'Search...')}\n </span>\n </div>\n {groups.length === 0 ? (\n <p className=\"px-2 text-sm text-muted-foreground\">\n {t('appShell.sidebarCustomizationPreviewEmpty', 'No groups to preview.')}\n </p>\n ) : (\n <nav className=\"flex flex-col gap-2\">\n {groups.map((group, gi) => {\n const visibleItems = group.items.filter((item) => item.hidden !== true)\n if (visibleItems.length === 0) return null\n return (\n <div key={resolveGroupKey(group)}>\n <div className=\"w-full px-1 justify-between flex text-xs font-medium uppercase tracking-wider text-muted-foreground/70 py-1\">\n <span>{group.name}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n {visibleItems.map((item) => {\n const itemKey = resolveItemKey(item)\n const isActive = activeKey === itemKey\n return (\n <div\n key={itemKey}\n className={`relative text-sm font-medium rounded-lg inline-flex items-center w-full px-3 py-2 gap-2 ${\n isActive ? 'bg-muted text-foreground' : 'text-muted-foreground'\n }`}\n >\n {isActive ? (\n <span\n aria-hidden\n className=\"absolute left-[-12px] top-2 w-1 h-5 rounded-r bg-foreground\"\n />\n ) : null}\n <span className=\"flex items-center justify-center shrink-0\">\n <SidebarPreviewIcon item={item} />\n </span>\n <span className=\"truncate\">{item.title}</span>\n </div>\n )\n })}\n </div>\n {gi < groups.length - 1 ? <div className=\"my-2 border-t -ml-3 -mr-4\" /> : null}\n </div>\n )\n })}\n </nav>\n )}\n </div>\n </div>\n )\n}\n\nexport default SidebarCustomizationEditor\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { ChevronUp, ChevronDown, GripVertical, RotateCcw, Trash2, Plus, Search, AlertTriangle } from 'lucide-react'\nimport { DndContext, closestCenter, PointerSensor, KeyboardSensor, useSensor, useSensors, type DragEndEvent } from '@dnd-kit/core'\nimport { SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\nimport Image from 'next/image'\nimport { resolveInjectedIcon } from '../injection/resolveInjectedIcon'\nimport { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport { IconButton } from '../../primitives/icon-button'\nimport { Input } from '../../primitives/input'\nimport { Switch } from '../../primitives/switch'\nimport { Card, CardContent, CardHeader, CardTitle } from '../../primitives/card'\nimport { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../../primitives/dialog'\nimport { Tag } from '../../primitives/tag'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../../primitives/select'\nimport { apiCall } from '../utils/apiCall'\nimport { flash } from '../FlashMessages'\nimport { Page, PageBody } from '../Page'\nimport { useBackendChrome } from '../BackendChromeProvider'\nimport { useConfirmDialog } from '../confirm-dialog'\nimport { useGuardedMutation } from '../injection/useGuardedMutation'\nimport {\n applyCustomizationDraft,\n applyItemOrder,\n cloneSidebarGroups,\n collectSidebarDefaults,\n filterMainSidebarGroups,\n mergeGroupOrder,\n resolveGroupKey,\n resolveItemKey,\n type SidebarCustomizationDraft,\n type SidebarGroup,\n type SidebarItem,\n} from './customization-helpers'\n\nexport type SidebarCustomizationEditorProps = {\n onSaved?: () => void\n onCanceled?: () => void\n variantsApiPath?: string\n preferencesApiPath?: string\n groups?: SidebarGroup[]\n}\n\nconst VARIANTS_API_DEFAULT = '/api/auth/sidebar/variants'\nconst PREFERENCES_API_DEFAULT = '/api/auth/sidebar/preferences'\nconst REFRESH_SIDEBAR_EVENT = 'om:refresh-sidebar'\nconst NEW_VARIANT_KEY = '__new__'\n\n// Surface server-provided error messages directly when present (4xx with `error` field\n// like 409 duplicate-name); fall back to the generic copy + status code for opaque 5xx.\nfunction formatVariantApiError(\n call: { ok: boolean; status: number; result: unknown },\n t: (key: string, fallback?: string) => string,\n): string {\n const detail = (call.result as { error?: unknown } | null)?.error\n if (typeof detail === 'string' && detail.length > 0 && call.status >= 400 && call.status < 500) {\n return detail\n }\n if (typeof detail === 'string' && detail.length > 0) {\n return `${t('appShell.sidebarCustomizationSaveError')} (${call.status}: ${detail})`\n }\n return `${t('appShell.sidebarCustomizationSaveError')} (${call.status})`\n}\n\ntype RoleTarget = {\n id: string\n name: string\n hasPreference: boolean\n}\n\ntype VariantSettings = {\n version: number\n groupOrder: string[]\n groupLabels: Record<string, string>\n itemLabels: Record<string, string>\n hiddenItems: string[]\n itemOrder?: Record<string, string[]>\n}\n\ntype Variant = {\n id: string\n name: string\n isActive: boolean\n settings: VariantSettings\n createdAt: string\n updatedAt: string | null\n}\n\ntype VariantListResponse = { locale: string; variants: Variant[] }\ntype VariantSingleResponse = { locale: string; variant: Variant }\n\nfunction findItemByKey(items: SidebarItem[], targetKey: string): SidebarItem | null {\n for (const item of items) {\n if (resolveItemKey(item) === targetKey) return item\n if (item.children && item.children.length > 0) {\n const found = findItemByKey(item.children, targetKey)\n if (found) return found\n }\n }\n return null\n}\n\nfunction collectDescendantKeys(item: SidebarItem): string[] {\n const out: string[] = []\n const walk = (node: SidebarItem) => {\n if (!node.children) return\n for (const child of node.children) {\n out.push(resolveItemKey(child))\n walk(child)\n }\n }\n walk(item)\n return out\n}\n\nfunction parseDraftFromSettings(\n rawSettings: VariantSettings | null | undefined,\n baseSnapshot: SidebarGroup[],\n): SidebarCustomizationDraft {\n const responseOrder = Array.isArray(rawSettings?.groupOrder)\n ? rawSettings.groupOrder\n .map((id) => (typeof id === 'string' ? id.trim() : ''))\n .filter((id) => id.length > 0)\n : []\n const responseGroupLabels: Record<string, string> = {}\n if (rawSettings?.groupLabels && typeof rawSettings.groupLabels === 'object') {\n for (const [key, value] of Object.entries(rawSettings.groupLabels)) {\n if (typeof value !== 'string') continue\n const trimmedKey = key.trim()\n if (!trimmedKey) continue\n responseGroupLabels[trimmedKey] = value\n }\n }\n const responseItemLabels: Record<string, string> = {}\n if (rawSettings?.itemLabels && typeof rawSettings.itemLabels === 'object') {\n for (const [key, value] of Object.entries(rawSettings.itemLabels)) {\n if (typeof value !== 'string') continue\n const trimmedKey = key.trim()\n if (!trimmedKey) continue\n responseItemLabels[trimmedKey] = value\n }\n }\n const responseHiddenItems = Array.isArray(rawSettings?.hiddenItems)\n ? rawSettings.hiddenItems\n .map((itemId) => (typeof itemId === 'string' ? itemId.trim() : ''))\n .filter((itemId) => itemId.length > 0)\n : []\n const responseItemOrder: Record<string, string[]> = {}\n if (rawSettings?.itemOrder && typeof rawSettings.itemOrder === 'object') {\n for (const [groupKey, list] of Object.entries(rawSettings.itemOrder)) {\n if (!Array.isArray(list)) continue\n const trimmedGroup = groupKey.trim()\n if (!trimmedGroup) continue\n const seen = new Set<string>()\n const values: string[] = []\n for (const itemKey of list) {\n if (typeof itemKey !== 'string') continue\n const trimmedItem = itemKey.trim()\n if (!trimmedItem || seen.has(trimmedItem)) continue\n seen.add(trimmedItem)\n values.push(trimmedItem)\n }\n if (values.length > 0) responseItemOrder[trimmedGroup] = values\n }\n }\n const currentIds = baseSnapshot.map((group) => resolveGroupKey(group))\n const order = mergeGroupOrder(responseOrder, currentIds)\n const { itemDefaults } = collectSidebarDefaults(baseSnapshot)\n const hiddenItemIds: Record<string, boolean> = {}\n for (const itemId of responseHiddenItems) {\n if (!itemDefaults.has(itemId)) continue\n hiddenItemIds[itemId] = true\n }\n return {\n order,\n groupLabels: responseGroupLabels,\n itemLabels: responseItemLabels,\n hiddenItemIds,\n itemOrder: responseItemOrder,\n }\n}\n\nfunction emptyDraftFor(baseSnapshot: SidebarGroup[]): SidebarCustomizationDraft {\n return {\n order: baseSnapshot.map((group) => resolveGroupKey(group)),\n groupLabels: {},\n itemLabels: {},\n hiddenItemIds: {},\n itemOrder: {},\n }\n}\n\nexport function SidebarCustomizationEditor({\n onSaved,\n onCanceled,\n variantsApiPath = VARIANTS_API_DEFAULT,\n preferencesApiPath = PREFERENCES_API_DEFAULT,\n groups: groupsProp,\n}: SidebarCustomizationEditorProps) {\n const t = useT()\n const locale = useLocale()\n const localeLabel = (locale || '').toUpperCase()\n const { payload: chromePayload, isLoading: chromeIsLoading } = useBackendChrome()\n const groupsFromChrome = chromePayload?.groups as SidebarGroup[] | undefined\n const sourceGroups = groupsProp ?? groupsFromChrome ?? []\n const { confirm: confirmDialog, ConfirmDialogElement } = useConfirmDialog()\n\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [deleting, setDeleting] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [variants, setVariants] = React.useState<Variant[]>([])\n const [selectedVariantId, setSelectedVariantId] = React.useState<string | null>(null)\n const [variantName, setVariantName] = React.useState('')\n const [draft, setDraft] = React.useState<SidebarCustomizationDraft | null>(null)\n const [previewGroups, setPreviewGroups] = React.useState<SidebarGroup[]>([])\n const [dirty, setDirty] = React.useState(false)\n const [availableRoleTargets, setAvailableRoleTargets] = React.useState<RoleTarget[]>([])\n const [selectedRoleIds, setSelectedRoleIds] = React.useState<string[]>([])\n const [canApplyToRoles, setCanApplyToRoles] = React.useState(false)\n const [addDialogOpen, setAddDialogOpen] = React.useState(false)\n const [addDialogName, setAddDialogName] = React.useState('')\n const [addDialogError, setAddDialogError] = React.useState<string | null>(null)\n const baseSnapshotRef = React.useRef<SidebarGroup[] | null>(null)\n const hasInitializedRef = React.useRef(false)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n variantId?: string | null\n operation: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: 'sidebar-customization',\n blockedMessage: t('appShell.sidebarCustomizationSaveError'),\n })\n\n const buildMutationContext = React.useCallback(\n (operation: string, variantId?: string | null) => ({\n formId: 'sidebar-customization',\n variantId: variantId ?? null,\n operation,\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n\n const isNewVariant = selectedVariantId === null\n const selectedVariant = React.useMemo(\n () => (selectedVariantId ? variants.find((v) => v.id === selectedVariantId) ?? null : null),\n [selectedVariantId, variants],\n )\n\n const updateDraft = React.useCallback((updater: (draft: SidebarCustomizationDraft) => SidebarCustomizationDraft) => {\n setDraft((prev) => {\n if (!prev) return prev\n const next = updater(prev)\n if (baseSnapshotRef.current) {\n setPreviewGroups(applyCustomizationDraft(baseSnapshotRef.current, next))\n }\n return next\n })\n setDirty(true)\n }, [])\n\n const buildBaseSnapshot = React.useCallback((): SidebarGroup[] => {\n return filterMainSidebarGroups(cloneSidebarGroups(sourceGroups))\n }, [sourceGroups])\n\n const loadVariantsList = React.useCallback(async (): Promise<Variant[]> => {\n // Cache-bust to prevent stale browser/Next caches from masking just-created variants.\n const url = `${variantsApiPath}?_=${Date.now()}`\n const call = await apiCall<VariantListResponse>(url, { cache: 'no-store' })\n if (!call.ok) {\n throw new Error('list-failed')\n }\n return call.result?.variants ?? []\n }, [variantsApiPath])\n\n const loadRolesPayload = React.useCallback(async (): Promise<{ canApplyToRoles: boolean; roles: RoleTarget[] }> => {\n const call = await apiCall<{ canApplyToRoles?: boolean; roles?: Array<{ id?: string; name?: string; hasPreference?: boolean }> }>(preferencesApiPath)\n if (!call.ok) {\n return { canApplyToRoles: false, roles: [] }\n }\n const data = call.result ?? null\n const can = data?.canApplyToRoles === true\n const roles = Array.isArray(data?.roles)\n ? (data!.roles as Array<{ id?: string; name?: string; hasPreference?: boolean }>)\n .filter((r) => typeof r?.id === 'string' && typeof r?.name === 'string')\n .map((r) => ({ id: r.id as string, name: r.name as string, hasPreference: r.hasPreference === true }))\n : []\n return { canApplyToRoles: can, roles }\n }, [preferencesApiPath])\n\n const selectVariantInternal = React.useCallback((variant: Variant | null, list: Variant[]) => {\n const baseSnapshot = baseSnapshotRef.current ?? buildBaseSnapshot()\n baseSnapshotRef.current = baseSnapshot\n if (variant) {\n const initialDraft = parseDraftFromSettings(variant.settings, baseSnapshot)\n setSelectedVariantId(variant.id)\n setVariantName(variant.name)\n setDraft(initialDraft)\n setPreviewGroups(applyCustomizationDraft(baseSnapshot, initialDraft))\n } else {\n const empty = emptyDraftFor(baseSnapshot)\n setSelectedVariantId(null)\n // Suggest a default name based on the existing variants count.\n const usedNumbers = new Set<number>()\n for (const v of list) {\n if (v.name === 'My preferences') usedNumbers.add(1)\n const match = v.name.match(/^My preferences\\s+(\\d+)$/)\n if (match) usedNumbers.add(Number.parseInt(match[1], 10))\n }\n let next = 1\n while (usedNumbers.has(next)) next += 1\n const suggestion = next === 1 ? 'My preferences' : `My preferences ${next}`\n setVariantName(suggestion)\n setDraft(empty)\n setPreviewGroups(applyCustomizationDraft(baseSnapshot, empty))\n }\n setDirty(false)\n }, [buildBaseSnapshot])\n\n // Initial load. No cancelled flag because React Strict Mode in dev runs effects twice\n // and the cleanup-driven cancellation made the only init pass abort silently \u2014 leaving\n // `loading` true forever and the editor stuck on the \"Loading\u2026\" placeholder. The init\n // gate (`hasInitializedRef`) prevents the second Strict-Mode run from doubling work.\n React.useEffect(() => {\n if (hasInitializedRef.current) return\n if (sourceGroups.length === 0) return\n hasInitializedRef.current = true\n async function init() {\n setLoading(true)\n setError(null)\n try {\n const [list, rolesPayload] = await Promise.all([\n loadVariantsList(),\n loadRolesPayload(),\n ])\n setVariants(list)\n setCanApplyToRoles(rolesPayload.canApplyToRoles)\n setAvailableRoleTargets(rolesPayload.roles)\n const active = list.find((v) => v.isActive)\n const initial = active ?? list[0] ?? null\n selectVariantInternal(initial, list)\n setSelectedRoleIds([])\n } catch (err) {\n console.error('Failed to load sidebar variants', err)\n setError(t('appShell.sidebarCustomizationLoadError'))\n } finally {\n setLoading(false)\n }\n }\n void init()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sourceGroups.length])\n\n const toggleRoleSelection = React.useCallback((roleId: string) => {\n setSelectedRoleIds((prev) => (prev.includes(roleId) ? prev.filter((id) => id !== roleId) : [...prev, roleId]))\n setDirty(true)\n }, [])\n\n const createNewVariant = React.useCallback(async (\n proposedName?: string,\n options: { suppressPageError?: boolean } = {},\n ): Promise<{ ok: boolean; error?: string }> => {\n if (saving || deleting) return { ok: false }\n if (dirty && selectedVariantId !== null) {\n const proceed = await confirmDialog({\n title: t('appShell.sidebarCustomizationSwitchConfirmTitle', 'Discard unsaved changes?'),\n text: t('appShell.sidebarCustomizationSwitchConfirmText', 'You have unsaved changes for the current variant. Switching will discard them.'),\n confirmText: t('appShell.sidebarCustomizationSwitchConfirmYes', 'Discard and switch'),\n cancelText: t('common.cancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!proceed) return { ok: false }\n }\n setSaving(true)\n if (!options.suppressPageError) setError(null)\n try {\n const baseSnapshot = baseSnapshotRef.current ?? buildBaseSnapshot()\n baseSnapshotRef.current = baseSnapshot\n const groupOrder = baseSnapshot.map((g) => resolveGroupKey(g))\n const trimmed = (proposedName ?? '').trim()\n const call = await runMutation({\n operation: () =>\n apiCall<VariantSingleResponse>(variantsApiPath, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n // If name is omitted, server auto-names (\"My preferences\", \"My preferences 2\", \u2026).\n name: trimmed.length > 0 ? trimmed : undefined,\n settings: { groupOrder, groupLabels: {}, itemLabels: {}, hiddenItems: [], itemOrder: {} },\n isActive: true,\n }),\n }),\n context: buildMutationContext('createVariant'),\n mutationPayload: { name: trimmed.length > 0 ? trimmed : null },\n })\n if (!call.ok) {\n const message = formatVariantApiError(call, t)\n if (!options.suppressPageError) setError(message)\n return { ok: false, error: message }\n }\n const created = call.result?.variant ?? null\n // Trust POST response as authoritative; refetch in background for any side-effects\n // (e.g. server-side deactivation of previous active variant).\n let nextList: Variant[]\n try {\n nextList = await loadVariantsList()\n } catch {\n nextList = variants\n }\n // Defensive merge: ensure the just-created variant is in the list even if the\n // refetch happened to be served from a stale cache.\n if (created && !nextList.some((v) => v.id === created.id)) {\n nextList = [...nextList, created]\n }\n setVariants(nextList)\n if (created) {\n const fresh = nextList.find((v) => v.id === created.id) ?? created\n selectVariantInternal(fresh, nextList)\n }\n flash(t('appShell.sidebarCustomizationVariantCreated', 'Variant created.'), 'success')\n return { ok: true }\n } catch (err) {\n console.error('Failed to create sidebar variant', err)\n const message = t('appShell.sidebarCustomizationSaveError')\n if (!options.suppressPageError) setError(message)\n return { ok: false, error: message }\n } finally {\n setSaving(false)\n }\n }, [saving, deleting, dirty, selectedVariantId, confirmDialog, t, buildBaseSnapshot, variantsApiPath, loadVariantsList, selectVariantInternal, variants, runMutation, buildMutationContext])\n\n const handleVariantSwitch = React.useCallback(async (key: string) => {\n if (saving || deleting) return\n if (key === selectedVariantId) return\n if (key === NEW_VARIANT_KEY && isNewVariant) return\n if (dirty) {\n const proceed = await confirmDialog({\n title: t('appShell.sidebarCustomizationSwitchConfirmTitle', 'Discard unsaved changes?'),\n text: t('appShell.sidebarCustomizationSwitchConfirmText', 'You have unsaved changes for the current variant. Switching will discard them.'),\n confirmText: t('appShell.sidebarCustomizationSwitchConfirmYes', 'Discard and switch'),\n cancelText: t('common.cancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!proceed) return\n }\n if (key === NEW_VARIANT_KEY) {\n selectVariantInternal(null, variants)\n return\n }\n const next = variants.find((v) => v.id === key) ?? null\n selectVariantInternal(next, variants)\n }, [saving, deleting, selectedVariantId, isNewVariant, dirty, confirmDialog, t, variants, selectVariantInternal])\n\n const moveGroup = React.useCallback((groupId: string, offset: number) => {\n updateDraft((draft) => {\n const order = [...draft.order]\n const index = order.indexOf(groupId)\n if (index === -1) return draft\n const nextIndex = Math.max(0, Math.min(order.length - 1, index + offset))\n if (nextIndex === index) return draft\n order.splice(index, 1)\n order.splice(nextIndex, 0, groupId)\n return { ...draft, order }\n })\n }, [updateDraft])\n\n const dndSensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor),\n )\n\n const handleItemDragEnd = React.useCallback((groupKey: string, currentItemKeys: string[]) => (event: DragEndEvent) => {\n const { active, over } = event\n if (!over || active.id === over.id) return\n const fromId = String(active.id)\n const toId = String(over.id)\n updateDraft((draft) => {\n const baseOrder = draft.itemOrder?.[groupKey]?.length\n ? [...draft.itemOrder[groupKey]]\n : [...currentItemKeys]\n const fromIndex = baseOrder.indexOf(fromId)\n const toIndex = baseOrder.indexOf(toId)\n if (fromIndex === -1 || toIndex === -1) return draft\n const nextOrder = arrayMove(baseOrder, fromIndex, toIndex)\n return {\n ...draft,\n itemOrder: { ...(draft.itemOrder ?? {}), [groupKey]: nextOrder },\n }\n })\n }, [updateDraft])\n\n const setGroupLabel = React.useCallback((groupId: string, value: string) => {\n updateDraft((draft) => {\n const next = { ...draft.groupLabels }\n if (value.trim().length === 0) delete next[groupId]\n else next[groupId] = value\n return { ...draft, groupLabels: next }\n })\n }, [updateDraft])\n\n const setItemLabel = React.useCallback((itemId: string, value: string) => {\n updateDraft((draft) => {\n const next = { ...draft.itemLabels }\n if (value.trim().length === 0) delete next[itemId]\n else next[itemId] = value\n return { ...draft, itemLabels: next }\n })\n }, [updateDraft])\n\n const setItemHidden = React.useCallback((itemId: string, hidden: boolean) => {\n updateDraft((draft) => {\n const next = { ...draft.hiddenItemIds }\n const apply = (id: string) => {\n if (hidden) next[id] = true\n else delete next[id]\n }\n apply(itemId)\n // Cascade: hiding a parent hides every descendant; showing it reveals them too.\n if (baseSnapshotRef.current) {\n for (const group of baseSnapshotRef.current) {\n const target = findItemByKey(group.items, itemId)\n if (!target) continue\n for (const descendantKey of collectDescendantKeys(target)) apply(descendantKey)\n break\n }\n }\n return { ...draft, hiddenItemIds: next }\n })\n }, [updateDraft])\n\n const reset = React.useCallback(() => {\n if (!baseSnapshotRef.current) return\n if (selectedVariant) {\n const initialDraft = parseDraftFromSettings(selectedVariant.settings, baseSnapshotRef.current)\n setDraft(initialDraft)\n setPreviewGroups(applyCustomizationDraft(baseSnapshotRef.current, initialDraft))\n } else {\n const empty = emptyDraftFor(baseSnapshotRef.current)\n setDraft(empty)\n setPreviewGroups(applyCustomizationDraft(baseSnapshotRef.current, empty))\n }\n setDirty(false)\n }, [selectedVariant])\n\n const cancel = React.useCallback(() => {\n onCanceled?.()\n }, [onCanceled])\n\n const submitAddDialog = React.useCallback(async () => {\n setAddDialogError(null)\n const result = await createNewVariant(addDialogName, { suppressPageError: true })\n if (result.ok) {\n setAddDialogOpen(false)\n setAddDialogName('')\n setAddDialogError(null)\n return\n }\n setAddDialogError(result.error ?? t('appShell.sidebarCustomizationSaveError'))\n }, [createNewVariant, addDialogName, t])\n\n const sanitizeSettingsPayload = React.useCallback(() => {\n if (!draft || !baseSnapshotRef.current) return null\n const baseGroups = baseSnapshotRef.current\n const { groupDefaults, itemDefaults } = collectSidebarDefaults(baseGroups)\n const sanitizedGroupLabels: Record<string, string> = {}\n for (const [key, value] of Object.entries(draft.groupLabels)) {\n const trimmed = value.trim()\n const base = groupDefaults.get(key)\n if (!trimmed || !base) continue\n if (trimmed !== base) sanitizedGroupLabels[key] = trimmed\n }\n const sanitizedItemLabels: Record<string, string> = {}\n for (const [itemId, value] of Object.entries(draft.itemLabels)) {\n const trimmed = value.trim()\n const base = itemDefaults.get(itemId)\n if (!trimmed || !base) continue\n if (trimmed !== base) sanitizedItemLabels[itemId] = trimmed\n }\n const sanitizedHiddenItems: string[] = []\n for (const [itemId, hidden] of Object.entries(draft.hiddenItemIds)) {\n if (!hidden) continue\n if (!itemDefaults.has(itemId)) continue\n sanitizedHiddenItems.push(itemId)\n }\n // Build a Set of valid group keys to drop stale itemOrder entries.\n const groupKeys = new Set<string>()\n for (const group of baseGroups) groupKeys.add(resolveGroupKey(group))\n const sanitizedItemOrder: Record<string, string[]> = {}\n for (const [groupKey, list] of Object.entries(draft.itemOrder ?? {})) {\n if (!groupKeys.has(groupKey)) continue\n const seen = new Set<string>()\n const values: string[] = []\n for (const itemKey of list) {\n if (seen.has(itemKey)) continue\n if (!itemDefaults.has(itemKey)) continue\n seen.add(itemKey)\n values.push(itemKey)\n }\n if (values.length > 0) sanitizedItemOrder[groupKey] = values\n }\n return {\n groupOrder: draft.order,\n groupLabels: sanitizedGroupLabels,\n itemLabels: sanitizedItemLabels,\n hiddenItems: sanitizedHiddenItems,\n itemOrder: sanitizedItemOrder,\n }\n }, [draft])\n\n const save = React.useCallback(async () => {\n const settings = sanitizeSettingsPayload()\n if (!settings) return\n setSaving(true)\n setError(null)\n try {\n const trimmedName = variantName.trim()\n const isCurrentlyActive = selectedVariant?.isActive ?? false\n let savedVariant: Variant | null = null\n if (isNewVariant) {\n const call = await runMutation({\n operation: () =>\n apiCall<VariantSingleResponse>(variantsApiPath, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n name: trimmedName.length > 0 ? trimmedName : undefined,\n settings,\n // New variants are activated by default \u2014 there's only one active per scope,\n // others get auto-deactivated server-side.\n isActive: true,\n }),\n }),\n context: buildMutationContext('saveVariant'),\n mutationPayload: { name: trimmedName.length > 0 ? trimmedName : null, isActive: true },\n })\n if (!call.ok) {\n setError(formatVariantApiError(call, t))\n return\n }\n savedVariant = call.result?.variant ?? null\n } else if (selectedVariantId) {\n const call = await runMutation({\n operation: () =>\n apiCall<VariantSingleResponse>(`${variantsApiPath}/${encodeURIComponent(selectedVariantId)}`, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n name: trimmedName.length > 0 ? trimmedName : undefined,\n settings,\n isActive: isCurrentlyActive,\n }),\n }),\n context: buildMutationContext('saveVariant', selectedVariantId),\n mutationPayload: {\n id: selectedVariantId,\n name: trimmedName.length > 0 ? trimmedName : null,\n isActive: isCurrentlyActive,\n },\n })\n if (!call.ok) {\n setError(formatVariantApiError(call, t))\n return\n }\n savedVariant = call.result?.variant ?? null\n }\n // Sync user prefs and (optionally) push to roles via the legacy preferences endpoint.\n // The variant entity is the canonical \"saved layout\"; the preferences endpoint is what\n // the AppShell sidebar actually reads. Without this sync, the saved variant wouldn't\n // become the user's live sidebar.\n const preferencesPayload: Record<string, unknown> = {\n groupOrder: settings.groupOrder,\n groupLabels: settings.groupLabels,\n itemLabels: settings.itemLabels,\n hiddenItems: settings.hiddenItems,\n itemOrder: settings.itemOrder,\n }\n if (canApplyToRoles) {\n const applyToRolesPayload = [...selectedRoleIds]\n const clearRoleIdsPayload = availableRoleTargets\n .filter((role) => role.hasPreference && !selectedRoleIds.includes(role.id))\n .map((role) => role.id)\n preferencesPayload.applyToRoles = applyToRolesPayload\n preferencesPayload.clearRoleIds = clearRoleIdsPayload\n }\n const preferencesCall = await runMutation({\n operation: () =>\n apiCall(preferencesApiPath, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(preferencesPayload),\n }),\n context: buildMutationContext('savePreferences', selectedVariantId),\n mutationPayload: preferencesPayload,\n })\n if (!preferencesCall.ok) {\n // The variant entity is the canonical layout; the preferences sync is what the\n // AppShell sidebar actually reads. A failed sync would leave the saved variant\n // not reflected live, so surface it as a save error rather than flashing success.\n setError(formatVariantApiError(preferencesCall, t))\n return\n }\n try { window.dispatchEvent(new Event(REFRESH_SIDEBAR_EVENT)) } catch { /* no listener attached \u2014 fine, AppShell will refresh on next navigation */ }\n // Refresh the list so isActive flags are accurate, plus refresh roles so hasPreference flags update.\n const [list, rolesPayload] = await Promise.all([\n loadVariantsList(),\n loadRolesPayload(),\n ])\n // Defensive: ensure the just-saved variant lands in the list even if the refetch\n // was served stale (browser HTTP cache, etc.).\n const mergedList = savedVariant && !list.some((v) => v.id === savedVariant!.id)\n ? [...list, savedVariant]\n : list\n setVariants(mergedList)\n setCanApplyToRoles(rolesPayload.canApplyToRoles)\n setAvailableRoleTargets(rolesPayload.roles)\n if (savedVariant) {\n const fresh = mergedList.find((v) => v.id === savedVariant!.id) ?? savedVariant\n selectVariantInternal(fresh, mergedList)\n } else {\n const active = mergedList.find((v) => v.isActive) ?? mergedList[0] ?? null\n selectVariantInternal(active, mergedList)\n }\n flash(\n isNewVariant\n ? t('appShell.sidebarCustomizationVariantCreated', 'Variant created.')\n : t('appShell.sidebarCustomizationVariantSaved', 'Variant saved.'),\n 'success',\n )\n onSaved?.()\n } catch (err) {\n console.error('Failed to save sidebar variant', err)\n setError(t('appShell.sidebarCustomizationSaveError'))\n } finally {\n setSaving(false)\n }\n }, [draft, variantName, isNewVariant, selectedVariant, selectedVariantId, variantsApiPath, preferencesApiPath, canApplyToRoles, selectedRoleIds, availableRoleTargets, t, sanitizeSettingsPayload, loadVariantsList, loadRolesPayload, selectVariantInternal, onSaved, runMutation, buildMutationContext])\n\n const toggleActive = React.useCallback(async (next: boolean) => {\n if (!selectedVariant || saving || deleting) return\n setError(null)\n try {\n const call = await runMutation({\n operation: () =>\n apiCall<VariantSingleResponse>(`${variantsApiPath}/${encodeURIComponent(selectedVariant.id)}`, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isActive: next }),\n }),\n context: buildMutationContext('toggleVariantActive', selectedVariant.id),\n mutationPayload: { id: selectedVariant.id, isActive: next },\n })\n if (!call.ok) {\n setError(t('appShell.sidebarCustomizationSaveError'))\n return\n }\n try { window.dispatchEvent(new Event(REFRESH_SIDEBAR_EVENT)) } catch { /* no listener attached \u2014 fine, AppShell will refresh on next navigation */ }\n const list = await loadVariantsList()\n setVariants(list)\n const fresh = list.find((v) => v.id === selectedVariant.id) ?? selectedVariant\n selectVariantInternal(fresh, list)\n } catch (err) {\n console.error('Failed to toggle variant active state', err)\n setError(t('appShell.sidebarCustomizationSaveError'))\n }\n }, [selectedVariant, saving, deleting, variantsApiPath, t, loadVariantsList, selectVariantInternal, runMutation, buildMutationContext])\n\n const deleteVariant = React.useCallback(async () => {\n if (!selectedVariant) return\n const proceed = await confirmDialog({\n title: t('appShell.sidebarCustomizationDeleteVariantTitle', 'Delete variant?'),\n text: t(\n 'appShell.sidebarCustomizationDeleteVariantText',\n 'This variant will be removed from your library.',\n ),\n confirmText: t('appShell.sidebarCustomizationDeleteVariantConfirm', 'Delete variant'),\n cancelText: t('common.cancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!proceed) return\n setDeleting(true)\n setError(null)\n try {\n const call = await runMutation({\n operation: () =>\n apiCall(`${variantsApiPath}/${encodeURIComponent(selectedVariant.id)}`, { method: 'DELETE' }),\n context: buildMutationContext('deleteVariant', selectedVariant.id),\n mutationPayload: { id: selectedVariant.id },\n })\n if (!call.ok) {\n setError(t('appShell.sidebarCustomizationSaveError'))\n return\n }\n try { window.dispatchEvent(new Event(REFRESH_SIDEBAR_EVENT)) } catch { /* no listener attached \u2014 fine, AppShell will refresh on next navigation */ }\n const list = await loadVariantsList()\n setVariants(list)\n const fallback = list[0] ?? null\n selectVariantInternal(fallback, list)\n } catch (err) {\n console.error('Failed to delete variant', err)\n setError(t('appShell.sidebarCustomizationSaveError'))\n } finally {\n setDeleting(false)\n }\n }, [selectedVariant, confirmDialog, t, variantsApiPath, loadVariantsList, selectVariantInternal, runMutation, buildMutationContext])\n\n const isBusy = saving || deleting\n\n if (loading && !draft) {\n return (\n <>\n {ConfirmDialogElement}\n <div className=\"space-y-6\">\n <div className=\"space-y-2\">\n <div className=\"h-7 w-64 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-96 animate-pulse rounded bg-muted/60\" />\n </div>\n <div className=\"h-64 animate-pulse rounded-lg border bg-muted/30\" />\n </div>\n </>\n )\n }\n\n if (!draft || !baseSnapshotRef.current) {\n // While chrome payload streams in or the initial fetch runs, show a neutral loading\n // state instead of the error fallback (otherwise the first visit looks like a crash).\n const stillLoading = loading || chromeIsLoading || sourceGroups.length === 0\n return (\n <>\n {ConfirmDialogElement}\n <div className=\"rounded-lg border border-dashed bg-muted/30 p-6 text-sm text-muted-foreground\">\n {stillLoading\n ? t('appShell.sidebarCustomizationLoading', 'Loading\u2026')\n : (error ?? t('appShell.sidebarCustomizationLoadError'))}\n </div>\n </>\n )\n }\n\n const baseGroupsForDefaults = baseSnapshotRef.current\n const baseGroupMap = new Map<string, SidebarGroup>()\n for (const group of baseGroupsForDefaults) {\n baseGroupMap.set(resolveGroupKey(group), group)\n }\n const orderedGroupIds = mergeGroupOrder(draft.order, Array.from(baseGroupMap.keys()))\n const totalGroups = orderedGroupIds.length\n\n const selectValue = isNewVariant ? NEW_VARIANT_KEY : selectedVariantId ?? NEW_VARIANT_KEY\n const showVariantPicker = variants.length > 0 || isNewVariant\n\n return (\n <>\n {ConfirmDialogElement}\n <Dialog\n open={addDialogOpen}\n onOpenChange={(next) => {\n if (!next) {\n setAddDialogOpen(false)\n setAddDialogName('')\n setAddDialogError(null)\n }\n }}\n >\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {t('appShell.sidebarCustomizationAddDialogTitle', 'Add new variant')}\n </DialogTitle>\n <DialogDescription>\n {t('appShell.sidebarCustomizationAddDialogDescription', 'Choose a name for the new sidebar variant. Leave blank to auto-name it.')}\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-xs font-medium uppercase tracking-wider text-muted-foreground/70\">\n {t('appShell.sidebarCustomizationVariantNameLabel', 'Variant name')}\n </label>\n <Input\n autoFocus\n value={addDialogName}\n onChange={(event) => {\n setAddDialogName(event.target.value)\n if (addDialogError) setAddDialogError(null)\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault()\n void submitAddDialog()\n }\n }}\n placeholder={t('appShell.sidebarCustomizationVariantNamePlaceholder', 'My preferences')}\n disabled={saving}\n aria-invalid={addDialogError ? true : undefined}\n aria-describedby={addDialogError ? 'sidebar-add-variant-error' : undefined}\n />\n {addDialogError ? (\n <p\n id=\"sidebar-add-variant-error\"\n role=\"alert\"\n className=\"text-xs text-destructive\"\n >\n {addDialogError}\n </p>\n ) : null}\n </div>\n <DialogFooter className=\"mt-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setAddDialogOpen(false)\n setAddDialogName('')\n setAddDialogError(null)\n }}\n disabled={saving}\n >\n {t('appShell.sidebarCustomizationCancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => { void submitAddDialog() }}\n disabled={saving}\n >\n {saving\n ? t('appShell.sidebarCustomizationCreating', 'Creating\u2026')\n : t('appShell.sidebarCustomizationCreateVariant', 'Create variant')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n <Page>\n <header className=\"space-y-1\">\n <h1 className=\"text-xl sm:text-2xl font-semibold leading-tight\">\n {t('appShell.sidebarCustomizationHeading')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('appShell.sidebarCustomizationHint', { locale: localeLabel })}\n </p>\n </header>\n\n {error ? (\n <div className=\"rounded-lg border border-destructive/40 bg-destructive/5 px-4 py-3 text-sm text-destructive\">\n {error}\n </div>\n ) : null}\n\n {/* Two-column: editor (variant + roles + order) + preview */}\n <PageBody className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(0,360px)]\">\n <div className=\"space-y-6\">\n {(() => {\n const showRolesCard = canApplyToRoles && availableRoleTargets.length > 0\n if (!showVariantPicker && !showRolesCard) return null\n return (\n <Card>\n <CardContent className=\"flex flex-col gap-6\">\n {showVariantPicker ? (\n <div className=\"flex flex-col gap-4\">\n {/* Row: combobox-style name input with chevron picker + DS-compliant add button */}\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-xs font-medium uppercase tracking-wider text-muted-foreground/70\">\n {t('appShell.sidebarCustomizationVariantNameLabel', 'Variant name')}\n </label>\n <div className=\"flex items-stretch gap-2\">\n {/* Combobox group: input + chevron picker share a single visual border. */}\n <div className=\"relative flex flex-1 items-stretch\">\n <Input\n value={variantName}\n onChange={(event) => {\n setVariantName(event.target.value)\n setDirty(true)\n }}\n placeholder={t('appShell.sidebarCustomizationVariantNamePlaceholder', 'My preferences')}\n disabled={isBusy}\n className=\"w-full pr-10\"\n />\n <Select\n value={selectValue}\n onValueChange={(value) => { void handleVariantSwitch(value) }}\n disabled={isBusy || loading}\n >\n <SelectTrigger\n className=\"pointer-events-none absolute inset-0 h-full w-full justify-end border-0 bg-transparent px-3 shadow-none ring-0 focus-visible:ring-0 focus-visible:ring-offset-0 [&>span]:hidden [&>svg]:pointer-events-auto\"\n aria-label={t('appShell.sidebarCustomizationVariantPickerLabel', 'Pick variant')}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {variants.length > 0 ? (\n variants.map((variant) => (\n <SelectItem key={variant.id} value={variant.id}>\n {variant.name}\n </SelectItem>\n ))\n ) : (\n <SelectItem value={NEW_VARIANT_KEY} disabled>\n {t('appShell.sidebarCustomizationVariantsEmpty', 'No saved variants yet')}\n </SelectItem>\n )}\n </SelectContent>\n </Select>\n </div>\n <Button\n type=\"button\"\n onClick={() => {\n setAddDialogName('')\n setAddDialogError(null)\n setAddDialogOpen(true)\n }}\n disabled={isBusy}\n title={t('appShell.sidebarCustomizationVariantNew', 'Add new variant')}\n >\n <Plus className=\"size-4\" />\n {t('appShell.sidebarCustomizationCreateNew', 'Create new')}\n </Button>\n </div>\n </div>\n\n {isNewVariant ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('appShell.sidebarCustomizationVariantNewHint', 'Saving will create a new variant. If you leave the name blank, it will be auto-named.')}\n </p>\n ) : null}\n\n {/* Row: active switch */}\n <div className=\"flex items-center gap-2\">\n <Switch\n checked={selectedVariant?.isActive ?? isNewVariant}\n onCheckedChange={(next) => {\n if (isNewVariant) return\n void toggleActive(next === true)\n }}\n disabled={isBusy || isNewVariant}\n aria-label={t('appShell.sidebarCustomizationVariantActiveLabel', 'Active')}\n />\n <span className=\"text-xs font-medium uppercase tracking-wider text-muted-foreground/70\">\n {t('appShell.sidebarCustomizationVariantActiveLabel', 'Active')}\n </span>\n </div>\n </div>\n ) : null}\n\n {showVariantPicker && showRolesCard ? (\n <div className=\"-mx-6 border-t\" aria-hidden />\n ) : null}\n\n {showRolesCard ? (\n <div className=\"flex flex-col gap-3\">\n <div className=\"space-y-1\">\n <h3 className=\"text-base font-semibold leading-none text-foreground\">\n {t('appShell.sidebarApplyToRolesTitle')}\n </h3>\n <p className=\"text-sm text-muted-foreground\">{t('appShell.sidebarApplyToRolesDescription')}</p>\n </div>\n <div className=\"flex flex-col gap-1.5 max-w-sm\">\n {availableRoleTargets.map((role) => {\n const checked = selectedRoleIds.includes(role.id)\n const willClear = role.hasPreference && !checked\n return (\n <label\n key={role.id}\n className=\"flex cursor-pointer items-center gap-3 rounded-lg border bg-background px-3 py-2 text-sm transition-colors hover:bg-muted\"\n >\n <Switch\n checked={checked}\n onCheckedChange={() => toggleRoleSelection(role.id)}\n disabled={isBusy}\n />\n <span className=\"flex-1 truncate font-medium text-foreground\">{role.name}</span>\n {role.hasPreference ? (\n <Tag variant={willClear ? 'error' : 'info'} dot={!willClear}>\n {willClear ? <AlertTriangle className=\"size-3\" aria-hidden /> : null}\n {willClear ? t('appShell.sidebarRoleWillClear') : t('appShell.sidebarRoleHasPreset')}\n </Tag>\n ) : null}\n </label>\n )\n })}\n </div>\n </div>\n ) : null}\n\n {/* Footer: Reset / Cancel / Save (right) + Delete (left). All gated on dirty\n except Delete which acts on the persisted variant regardless of edits. */}\n <div className=\"-mx-6 border-t\" aria-hidden />\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n {selectedVariant ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => { void deleteVariant() }}\n disabled={isBusy}\n className=\"text-destructive hover:text-destructive\"\n >\n <Trash2 className=\"size-4\" />\n {deleting\n ? t('appShell.sidebarCustomizationDeleteVariantInProgress', 'Deleting\u2026')\n : t('appShell.sidebarCustomizationDeleteVariant', 'Delete variant')}\n </Button>\n ) : <span />}\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={reset}\n disabled={isBusy || !dirty}\n >\n {t('appShell.sidebarCustomizationReset')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={cancel}\n disabled={isBusy || !dirty}\n >\n {t('appShell.sidebarCustomizationCancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={save}\n disabled={isBusy || (!isNewVariant && !dirty)}\n >\n {saving\n ? (isNewVariant\n ? t('appShell.sidebarCustomizationCreating', 'Creating\u2026')\n : t('appShell.sidebarCustomizationSaving'))\n : (isNewVariant\n ? t('appShell.sidebarCustomizationCreateVariant', 'Create variant')\n : t('appShell.sidebarCustomizationSave'))}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n )\n })()}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">\n {t('appShell.sidebarCustomizationOrderHeading', 'Order & visibility')}\n </CardTitle>\n <p className=\"text-sm text-muted-foreground\">\n {t('appShell.sidebarCustomizationOrderDescription', 'Reorder groups, rename them, and toggle individual items on or off.')}\n </p>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {orderedGroupIds.map((groupId, index) => {\n const baseGroup = baseGroupMap.get(groupId)\n if (!baseGroup) return null\n const placeholder = baseGroup.defaultName ?? baseGroup.name\n const value = draft.groupLabels[groupId] ?? ''\n const trimmedValue = value.trim()\n const isGroupModified = trimmedValue.length > 0 && trimmedValue !== placeholder\n return (\n <div key={groupId} className=\"rounded-lg border bg-background\">\n <div className=\"flex items-start gap-3 border-b px-4 py-3\">\n <div className=\"flex flex-1 flex-col gap-1.5\">\n <label className=\"text-xs font-medium uppercase tracking-wider text-muted-foreground/70\">\n {t('appShell.sidebarCustomizationGroupLabel')}\n </label>\n <div className=\"flex items-center gap-2\">\n <Input\n value={value}\n onChange={(event) => setGroupLabel(groupId, event.target.value)}\n placeholder={placeholder}\n disabled={isBusy}\n className=\"flex-1\"\n />\n {isGroupModified ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setGroupLabel(groupId, '')}\n disabled={isBusy}\n aria-label={t('appShell.sidebarCustomizationResetField', 'Reset to default')}\n title={t('appShell.sidebarCustomizationResetField', 'Reset to default')}\n >\n <RotateCcw className=\"size-3.5\" />\n </IconButton>\n ) : null}\n </div>\n {isGroupModified ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('appShell.sidebarCustomizationDefault', 'Default:')}{' '}\n <span className=\"font-medium text-foreground/80\">{placeholder}</span>\n </p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-1 mt-[26px]\">\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"text-muted-foreground hover:text-foreground\"\n onClick={() => moveGroup(groupId, -1)}\n disabled={index === 0 || isBusy}\n aria-label={t('appShell.sidebarCustomizationMoveUp')}\n title={t('appShell.sidebarCustomizationMoveUp')}\n >\n <ChevronUp className=\"size-4\" />\n </IconButton>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"text-muted-foreground hover:text-foreground\"\n onClick={() => moveGroup(groupId, 1)}\n disabled={index === totalGroups - 1 || isBusy}\n aria-label={t('appShell.sidebarCustomizationMoveDown')}\n title={t('appShell.sidebarCustomizationMoveDown')}\n >\n <ChevronDown className=\"size-4\" />\n </IconButton>\n </div>\n </div>\n <div className=\"flex flex-col divide-y\">\n <ItemRows\n items={baseGroup.items}\n draft={draft}\n saving={isBusy}\n onLabelChange={setItemLabel}\n onHiddenChange={setItemHidden}\n t={t}\n groupKey={groupId}\n sensors={dndSensors}\n onDragEnd={handleItemDragEnd(groupId, baseGroup.items.map((item) => resolveItemKey(item)))}\n />\n </div>\n </div>\n )\n })}\n </CardContent>\n </Card>\n </div>\n\n <aside className=\"hidden lg:block\">\n <div className=\"sticky top-6\">\n <div className=\"relative\">\n <span className=\"absolute left-1/2 top-0 z-10 -translate-x-1/2 -translate-y-1/2 rounded-md bg-accent-indigo px-3 py-1 text-xs font-semibold uppercase tracking-wider text-accent-indigo-foreground shadow-sm\">\n {t('appShell.sidebarCustomizationPreview', 'Preview')}\n </span>\n <SidebarPreview\n groups={previewGroups}\n productName={t('appShell.productName', 'Open Mercato')}\n pickFirstActive\n />\n </div>\n </div>\n </aside>\n </PageBody>\n </Page>\n </>\n )\n}\n\ntype ItemRowProps = {\n item: SidebarItem\n draft: SidebarCustomizationDraft\n saving: boolean\n onLabelChange: (itemId: string, value: string) => void\n onHiddenChange: (itemId: string, hidden: boolean) => void\n t: ReturnType<typeof useT>\n depth: number\n dragHandle?: React.ReactNode\n /** True when an ancestor in the tree is hidden \u2014 child controls become read-only. */\n ancestorHidden?: boolean\n}\n\nfunction ItemRow({ item, draft, saving, onLabelChange, onHiddenChange, t, depth, dragHandle, ancestorHidden = false }: ItemRowProps) {\n const itemKey = resolveItemKey(item)\n const placeholder = item.defaultTitle ?? item.title\n const value = draft.itemLabels[itemKey] ?? ''\n const trimmedValue = value.trim()\n const isModified = trimmedValue.length > 0 && trimmedValue !== placeholder\n const hidden = draft.hiddenItemIds[itemKey] === true\n const effectivelyDimmed = hidden || ancestorHidden\n return (\n <div\n className=\"flex items-start gap-3 px-4 py-3 transition-colors hover:bg-muted/40\"\n style={depth ? { paddingLeft: 16 + depth * 24 } : undefined}\n >\n {dragHandle ?? (depth > 0 ? <span className=\"w-4 shrink-0\" aria-hidden /> : null)}\n <div className={`min-w-0 flex-1 flex flex-col gap-1.5 ${effectivelyDimmed ? 'opacity-60' : ''}`}>\n <div className=\"flex items-center gap-2\">\n <div className=\"min-w-0 flex-1\">\n <Input\n value={value}\n onChange={(event) => onLabelChange(itemKey, event.target.value)}\n placeholder={placeholder}\n disabled={saving}\n />\n </div>\n {isModified ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onLabelChange(itemKey, '')}\n disabled={saving}\n aria-label={t('appShell.sidebarCustomizationResetField', 'Reset to default')}\n title={t('appShell.sidebarCustomizationResetField', 'Reset to default')}\n >\n <RotateCcw className=\"size-3.5\" />\n </IconButton>\n ) : null}\n </div>\n {isModified ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('appShell.sidebarCustomizationDefault', 'Default:')}{' '}\n <span className=\"font-medium text-foreground/80\">{placeholder}</span>\n </p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-2 pt-1.5\">\n {hidden ? (\n <span className=\"rounded-full border border-border bg-muted px-2 py-0.5 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('appShell.sidebarCustomizationHiddenBadge', 'Hidden')}\n </span>\n ) : null}\n <Switch\n checked={!hidden}\n onCheckedChange={(next) => onHiddenChange(itemKey, next !== true)}\n disabled={saving || ancestorHidden}\n aria-label={t('appShell.sidebarCustomizationShowItem')}\n title={ancestorHidden ? t('appShell.sidebarCustomizationParentHiddenHint', 'Parent is hidden \u2014 show parent first.') : undefined}\n />\n </div>\n </div>\n )\n}\n\ntype SortableItemRowProps = ItemRowProps & { id: string }\n\nfunction SortableItemRow({ id, ...rowProps }: SortableItemRowProps) {\n const t = useT()\n const { attributes, listeners, setNodeRef, transform, transition, isDragging, setActivatorNodeRef } = useSortable({ id })\n const style: React.CSSProperties = {\n transform: CSS.Transform.toString(transform),\n transition,\n opacity: isDragging ? 0.5 : 1,\n }\n const dragHandle = (\n <IconButton\n ref={setActivatorNodeRef}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"shrink-0 mt-1.5 cursor-grab touch-none active:cursor-grabbing\"\n aria-label={t('appShell.sidebarCustomizationDragToReorder', 'Drag to reorder')}\n disabled={rowProps.saving}\n {...attributes}\n {...listeners}\n >\n <GripVertical className=\"size-4\" />\n </IconButton>\n )\n return (\n <div ref={setNodeRef} style={style}>\n <ItemRow {...rowProps} dragHandle={dragHandle} />\n </div>\n )\n}\n\ntype ItemRowsProps = {\n items: SidebarItem[]\n draft: SidebarCustomizationDraft\n saving: boolean\n onLabelChange: (itemId: string, value: string) => void\n onHiddenChange: (itemId: string, hidden: boolean) => void\n t: ReturnType<typeof useT>\n depth?: number\n groupKey?: string\n sensors?: ReturnType<typeof useSensors>\n onDragEnd?: (event: DragEndEvent) => void\n ancestorHidden?: boolean\n}\n\nfunction ItemRows({\n items,\n draft,\n saving,\n onLabelChange,\n onHiddenChange,\n t,\n depth = 0,\n groupKey,\n sensors,\n onDragEnd,\n ancestorHidden = false,\n}: ItemRowsProps) {\n if (items.length === 0) return null\n\n const renderRecursiveChildren = (item: SidebarItem, parentHidden: boolean) =>\n item.children && item.children.length > 0 ? (\n <ItemRows\n items={item.children}\n draft={draft}\n saving={saving}\n onLabelChange={onLabelChange}\n onHiddenChange={onHiddenChange}\n t={t}\n depth={depth + 1}\n ancestorHidden={parentHidden}\n />\n ) : null\n\n // Top-level rows in a group \u2192 enable DnD reordering.\n if (depth === 0 && groupKey && sensors && onDragEnd) {\n const ordered = applyItemOrder(items, resolveItemKey, draft.itemOrder?.[groupKey])\n const ids = ordered.map((item) => resolveItemKey(item))\n return (\n <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={onDragEnd}>\n <SortableContext items={ids} strategy={verticalListSortingStrategy}>\n {ordered.map((item) => {\n const itemKey = resolveItemKey(item)\n const ownHidden = draft.hiddenItemIds[itemKey] === true\n return (\n <React.Fragment key={itemKey}>\n <SortableItemRow\n id={itemKey}\n item={item}\n draft={draft}\n saving={saving}\n onLabelChange={onLabelChange}\n onHiddenChange={onHiddenChange}\n t={t}\n depth={depth}\n ancestorHidden={ancestorHidden}\n />\n {renderRecursiveChildren(item, ancestorHidden || ownHidden)}\n </React.Fragment>\n )\n })}\n </SortableContext>\n </DndContext>\n )\n }\n\n // Nested children \u2192 static rendering, no drag handle.\n return (\n <>\n {items.map((item) => {\n const itemKey = resolveItemKey(item)\n const ownHidden = draft.hiddenItemIds[itemKey] === true\n return (\n <React.Fragment key={itemKey}>\n <ItemRow\n item={item}\n draft={draft}\n saving={saving}\n onLabelChange={onLabelChange}\n onHiddenChange={onHiddenChange}\n t={t}\n depth={depth}\n ancestorHidden={ancestorHidden}\n />\n {renderRecursiveChildren(item, ancestorHidden || ownHidden)}\n </React.Fragment>\n )\n })}\n </>\n )\n}\n\nfunction SidebarPreviewIcon({ item }: { item: SidebarItem }) {\n if (item.icon) return <>{item.icon}</>\n if (item.iconName) {\n const resolved = resolveInjectedIcon(item.iconName)\n if (resolved) return <>{resolved}</>\n }\n if (item.iconMarkup) {\n return <span aria-hidden=\"true\" dangerouslySetInnerHTML={{ __html: item.iconMarkup }} />\n }\n // Fallback default icon \u2014 same shape as AppShell's DefaultIcon\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )\n}\n\nfunction SidebarPreview({\n groups,\n productName,\n pickFirstActive,\n}: {\n groups: SidebarGroup[]\n productName: string\n pickFirstActive: boolean\n}) {\n const t = useT()\n // Pre-compute the first visible item so we can render it as the \"active\" preview state.\n // This shows the user what the active state of their sidebar will look like.\n const activeKey = React.useMemo<string | null>(() => {\n if (!pickFirstActive) return null\n for (const group of groups) {\n for (const item of group.items) {\n if (item.hidden === true) continue\n return resolveItemKey(item)\n }\n }\n return null\n }, [groups, pickFirstActive])\n\n return (\n <div className=\"relative w-[240px] overflow-hidden rounded-xl border bg-background shadow-sm\">\n {/* Match AppShell's outer aside: border-r, py-4, px-3 \u2014 minus border-r since the\n card border already serves that purpose, plus rounded so it reads as a preview tile. */}\n <div className=\"flex flex-col gap-3 px-3 py-4\">\n {/* Brand block \u2014 same classes as AppShell brand tile */}\n <div className=\"mb-2\">\n <div className=\"flex items-center gap-3 rounded-xl p-3\">\n <Image\n src=\"/open-mercato.svg\"\n alt={productName}\n width={40}\n height={40}\n className=\"rounded-full shrink-0\"\n />\n <span className=\"text-sm font-medium text-foreground truncate\">{productName}</span>\n </div>\n </div>\n {/* Search input mock \u2014 same container styling as the real sidebar */}\n <div className=\"mb-2 flex items-center gap-2 rounded-lg border border-border bg-background pl-2.5 pr-2 py-2 shadow-sm\">\n <Search className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 text-sm text-muted-foreground/70 truncate\">\n {t('appShell.sidebarCustomizationPreviewSearchPlaceholder', 'Search...')}\n </span>\n </div>\n {groups.length === 0 ? (\n <p className=\"px-2 text-sm text-muted-foreground\">\n {t('appShell.sidebarCustomizationPreviewEmpty', 'No groups to preview.')}\n </p>\n ) : (\n <nav className=\"flex flex-col gap-2\">\n {groups.map((group, gi) => {\n const visibleItems = group.items.filter((item) => item.hidden !== true)\n if (visibleItems.length === 0) return null\n return (\n <div key={resolveGroupKey(group)}>\n <div className=\"w-full px-1 justify-between flex text-xs font-medium uppercase tracking-wider text-muted-foreground/70 py-1\">\n <span>{group.name}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n {visibleItems.map((item) => {\n const itemKey = resolveItemKey(item)\n const isActive = activeKey === itemKey\n return (\n <div\n key={itemKey}\n className={`relative text-sm font-medium rounded-lg inline-flex items-center w-full px-3 py-2 gap-2 ${\n isActive ? 'bg-muted text-foreground' : 'text-muted-foreground'\n }`}\n >\n {isActive ? (\n <span\n aria-hidden\n className=\"absolute left-[-12px] top-2 w-1 h-5 rounded-r bg-foreground\"\n />\n ) : null}\n <span className=\"flex items-center justify-center shrink-0\">\n <SidebarPreviewIcon item={item} />\n </span>\n <span className=\"truncate\">{item.title}</span>\n </div>\n )\n })}\n </div>\n {gi < groups.length - 1 ? <div className=\"my-2 border-t -ml-3 -mr-4\" /> : null}\n </div>\n )\n })}\n </nav>\n )}\n </div>\n </div>\n )\n}\n\nexport default SidebarCustomizationEditor\n"],
5
+ "mappings": "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",
6
6
  "names": ["draft"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ui",
3
- "version": "0.6.1-develop.3054.g0367114684",
3
+ "version": "0.6.1-develop.3060.d6cca7ade1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -140,12 +140,12 @@
140
140
  "remark-gfm": "^4.0.1"
141
141
  },
142
142
  "peerDependencies": {
143
- "@open-mercato/shared": "0.6.1-develop.3054.g0367114684",
143
+ "@open-mercato/shared": "0.6.1-develop.3060.d6cca7ade1",
144
144
  "react": ">=18.0.0",
145
145
  "react-dom": ">=18.0.0"
146
146
  },
147
147
  "devDependencies": {
148
- "@open-mercato/shared": "0.6.1-develop.3054.g0367114684",
148
+ "@open-mercato/shared": "0.6.1-develop.3060.d6cca7ade1",
149
149
  "@testing-library/dom": "^10.4.1",
150
150
  "@testing-library/jest-dom": "^6.9.1",
151
151
  "@testing-library/react": "^16.3.1",
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import * as React from 'react'
6
- import { screen, waitFor } from '@testing-library/react'
6
+ import { fireEvent, screen, waitFor, within } from '@testing-library/react'
7
7
  import { SidebarCustomizationEditor } from '../sidebar/SidebarCustomizationEditor'
8
8
  import { renderWithProviders } from '@open-mercato/shared/lib/testing/renderWithProviders'
9
9
 
@@ -178,6 +178,86 @@ describe('SidebarCustomizationEditor', () => {
178
178
  expect(screen.getByText('Admin')).toBeInTheDocument()
179
179
  })
180
180
 
181
+ it('shows duplicate-name error inline inside the add-variant dialog (not on the page behind it)', async () => {
182
+ const duplicateMessage = 'A variant with this name already exists. Choose a different name.'
183
+
184
+ apiCallMock.mockImplementation((url: string, init?: RequestInit) => {
185
+ if (init?.method === 'POST' && /\/api\/auth\/sidebar\/variants$/.test(url)) {
186
+ return Promise.resolve({
187
+ ok: false,
188
+ status: 409,
189
+ result: { error: duplicateMessage, code: 'duplicate_name' },
190
+ response: {},
191
+ cacheStatus: null,
192
+ } as ApiCallResult<unknown>)
193
+ }
194
+ if (/\/api\/auth\/sidebar\/variants/.test(url)) {
195
+ return Promise.resolve(okResult({ locale: 'en', variants: [] }))
196
+ }
197
+ if (/\/api\/auth\/sidebar\/preferences/.test(url)) {
198
+ return Promise.resolve(okResult({ canApplyToRoles: false, roles: [] }))
199
+ }
200
+ throw new Error(`apiCall mock: no response configured for ${url}`)
201
+ })
202
+
203
+ renderWithProviders(<SidebarCustomizationEditor groups={fakeGroups} />, { dict })
204
+
205
+ const openDialogButton = await screen.findByRole('button', { name: /Create new/ })
206
+ fireEvent.click(openDialogButton)
207
+
208
+ const dialog = await screen.findByRole('dialog')
209
+ const nameInput = within(dialog).getByPlaceholderText('My preferences')
210
+ fireEvent.change(nameInput, { target: { value: 'My Variant' } })
211
+
212
+ fireEvent.click(within(dialog).getByRole('button', { name: /Create variant/ }))
213
+
214
+ const dialogAlert = await within(dialog).findByRole('alert')
215
+ expect(dialogAlert).toHaveTextContent(duplicateMessage)
216
+ expect(within(dialog).getByRole('textbox')).toHaveAttribute('aria-invalid', 'true')
217
+
218
+ expect(screen.getByRole('dialog')).toBeInTheDocument()
219
+
220
+ expect(screen.getAllByText(duplicateMessage)).toHaveLength(1)
221
+ })
222
+
223
+ it('clears the inline dialog error when the user edits the name', async () => {
224
+ const duplicateMessage = 'A variant with this name already exists. Choose a different name.'
225
+
226
+ apiCallMock.mockImplementation((url: string, init?: RequestInit) => {
227
+ if (init?.method === 'POST' && /\/api\/auth\/sidebar\/variants$/.test(url)) {
228
+ return Promise.resolve({
229
+ ok: false,
230
+ status: 409,
231
+ result: { error: duplicateMessage, code: 'duplicate_name' },
232
+ response: {},
233
+ cacheStatus: null,
234
+ } as ApiCallResult<unknown>)
235
+ }
236
+ if (/\/api\/auth\/sidebar\/variants/.test(url)) {
237
+ return Promise.resolve(okResult({ locale: 'en', variants: [] }))
238
+ }
239
+ if (/\/api\/auth\/sidebar\/preferences/.test(url)) {
240
+ return Promise.resolve(okResult({ canApplyToRoles: false, roles: [] }))
241
+ }
242
+ throw new Error(`apiCall mock: no response configured for ${url}`)
243
+ })
244
+
245
+ renderWithProviders(<SidebarCustomizationEditor groups={fakeGroups} />, { dict })
246
+
247
+ const openDialogButton = await screen.findByRole('button', { name: /Create new/ })
248
+ fireEvent.click(openDialogButton)
249
+ const dialog = await screen.findByRole('dialog')
250
+ const nameInput = within(dialog).getByPlaceholderText('My preferences')
251
+ fireEvent.change(nameInput, { target: { value: 'My Variant' } })
252
+ fireEvent.click(within(dialog).getByRole('button', { name: /Create variant/ }))
253
+
254
+ await within(dialog).findByRole('alert')
255
+
256
+ fireEvent.change(nameInput, { target: { value: 'My Variant 2' } })
257
+
258
+ expect(within(dialog).queryByRole('alert')).not.toBeInTheDocument()
259
+ })
260
+
181
261
  it('does not render the role-apply target list when canApplyToRoles is false', async () => {
182
262
  setApiCallSequence([
183
263
  { url: /\/api\/auth\/sidebar\/variants/, response: okResult({ locale: 'en', variants: [] }) },
@@ -228,6 +228,7 @@ export function SidebarCustomizationEditor({
228
228
  const [canApplyToRoles, setCanApplyToRoles] = React.useState(false)
229
229
  const [addDialogOpen, setAddDialogOpen] = React.useState(false)
230
230
  const [addDialogName, setAddDialogName] = React.useState('')
231
+ const [addDialogError, setAddDialogError] = React.useState<string | null>(null)
231
232
  const baseSnapshotRef = React.useRef<SidebarGroup[] | null>(null)
232
233
  const hasInitializedRef = React.useRef(false)
233
234
 
@@ -366,8 +367,11 @@ export function SidebarCustomizationEditor({
366
367
  setDirty(true)
367
368
  }, [])
368
369
 
369
- const createNewVariant = React.useCallback(async (proposedName?: string): Promise<boolean> => {
370
- if (saving || deleting) return false
370
+ const createNewVariant = React.useCallback(async (
371
+ proposedName?: string,
372
+ options: { suppressPageError?: boolean } = {},
373
+ ): Promise<{ ok: boolean; error?: string }> => {
374
+ if (saving || deleting) return { ok: false }
371
375
  if (dirty && selectedVariantId !== null) {
372
376
  const proceed = await confirmDialog({
373
377
  title: t('appShell.sidebarCustomizationSwitchConfirmTitle', 'Discard unsaved changes?'),
@@ -376,10 +380,10 @@ export function SidebarCustomizationEditor({
376
380
  cancelText: t('common.cancel', 'Cancel'),
377
381
  variant: 'destructive',
378
382
  })
379
- if (!proceed) return false
383
+ if (!proceed) return { ok: false }
380
384
  }
381
385
  setSaving(true)
382
- setError(null)
386
+ if (!options.suppressPageError) setError(null)
383
387
  try {
384
388
  const baseSnapshot = baseSnapshotRef.current ?? buildBaseSnapshot()
385
389
  baseSnapshotRef.current = baseSnapshot
@@ -401,8 +405,9 @@ export function SidebarCustomizationEditor({
401
405
  mutationPayload: { name: trimmed.length > 0 ? trimmed : null },
402
406
  })
403
407
  if (!call.ok) {
404
- setError(formatVariantApiError(call, t))
405
- return false
408
+ const message = formatVariantApiError(call, t)
409
+ if (!options.suppressPageError) setError(message)
410
+ return { ok: false, error: message }
406
411
  }
407
412
  const created = call.result?.variant ?? null
408
413
  // Trust POST response as authoritative; refetch in background for any side-effects
@@ -424,11 +429,12 @@ export function SidebarCustomizationEditor({
424
429
  selectVariantInternal(fresh, nextList)
425
430
  }
426
431
  flash(t('appShell.sidebarCustomizationVariantCreated', 'Variant created.'), 'success')
427
- return true
432
+ return { ok: true }
428
433
  } catch (err) {
429
434
  console.error('Failed to create sidebar variant', err)
430
- setError(t('appShell.sidebarCustomizationSaveError'))
431
- return false
435
+ const message = t('appShell.sidebarCustomizationSaveError')
436
+ if (!options.suppressPageError) setError(message)
437
+ return { ok: false, error: message }
432
438
  } finally {
433
439
  setSaving(false)
434
440
  }
@@ -552,12 +558,16 @@ export function SidebarCustomizationEditor({
552
558
  }, [onCanceled])
553
559
 
554
560
  const submitAddDialog = React.useCallback(async () => {
555
- const ok = await createNewVariant(addDialogName)
556
- if (ok) {
561
+ setAddDialogError(null)
562
+ const result = await createNewVariant(addDialogName, { suppressPageError: true })
563
+ if (result.ok) {
557
564
  setAddDialogOpen(false)
558
565
  setAddDialogName('')
566
+ setAddDialogError(null)
567
+ return
559
568
  }
560
- }, [createNewVariant, addDialogName])
569
+ setAddDialogError(result.error ?? t('appShell.sidebarCustomizationSaveError'))
570
+ }, [createNewVariant, addDialogName, t])
561
571
 
562
572
  const sanitizeSettingsPayload = React.useCallback(() => {
563
573
  if (!draft || !baseSnapshotRef.current) return null
@@ -857,6 +867,7 @@ export function SidebarCustomizationEditor({
857
867
  if (!next) {
858
868
  setAddDialogOpen(false)
859
869
  setAddDialogName('')
870
+ setAddDialogError(null)
860
871
  }
861
872
  }}
862
873
  >
@@ -876,7 +887,10 @@ export function SidebarCustomizationEditor({
876
887
  <Input
877
888
  autoFocus
878
889
  value={addDialogName}
879
- onChange={(event) => setAddDialogName(event.target.value)}
890
+ onChange={(event) => {
891
+ setAddDialogName(event.target.value)
892
+ if (addDialogError) setAddDialogError(null)
893
+ }}
880
894
  onKeyDown={(event) => {
881
895
  if (event.key === 'Enter' && !event.shiftKey) {
882
896
  event.preventDefault()
@@ -885,7 +899,18 @@ export function SidebarCustomizationEditor({
885
899
  }}
886
900
  placeholder={t('appShell.sidebarCustomizationVariantNamePlaceholder', 'My preferences')}
887
901
  disabled={saving}
902
+ aria-invalid={addDialogError ? true : undefined}
903
+ aria-describedby={addDialogError ? 'sidebar-add-variant-error' : undefined}
888
904
  />
905
+ {addDialogError ? (
906
+ <p
907
+ id="sidebar-add-variant-error"
908
+ role="alert"
909
+ className="text-xs text-destructive"
910
+ >
911
+ {addDialogError}
912
+ </p>
913
+ ) : null}
889
914
  </div>
890
915
  <DialogFooter className="mt-2">
891
916
  <Button
@@ -894,6 +919,7 @@ export function SidebarCustomizationEditor({
894
919
  onClick={() => {
895
920
  setAddDialogOpen(false)
896
921
  setAddDialogName('')
922
+ setAddDialogError(null)
897
923
  }}
898
924
  disabled={saving}
899
925
  >
@@ -986,6 +1012,7 @@ export function SidebarCustomizationEditor({
986
1012
  type="button"
987
1013
  onClick={() => {
988
1014
  setAddDialogName('')
1015
+ setAddDialogError(null)
989
1016
  setAddDialogOpen(true)
990
1017
  }}
991
1018
  disabled={isBusy}