@proveanything/smartlinks-utils-ui 0.3.0 → 0.3.1

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.
@@ -1,9 +1,47 @@
1
1
  import { cn } from '../../chunk-L7FQ52F5.js';
2
- import { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext } from 'react';
3
- import { Package, Layers, Tag, Box, Rows3, Image, LayoutGrid, List, ChevronRight, ChevronDown, Trash2, Eye, X, HelpCircle, Search, Plus, CornerDownLeft, Circle } from 'lucide-react';
2
+ import { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext, createElement, isValidElement } from 'react';
3
+ import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, Globe, Tag, Boxes, Layers, Package, Rows3, Image, List, ChevronRight, Eraser, Box, AlertTriangle, Info, X, HelpCircle, Search, CornerDownLeft, Circle } from 'lucide-react';
4
4
  import { useQueryClient, useInfiniteQuery, useQuery } from '@tanstack/react-query';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
 
7
+ var DEFAULT_ICONS = {
8
+ scope: { product: Package, variant: Layers, batch: Boxes, facet: Tag, universal: Globe },
9
+ status: { own: CheckCircle2, inherited: ArrowDownLeft, missing: CircleDashed },
10
+ action: {
11
+ add: Plus,
12
+ edit: Pencil,
13
+ duplicate: Copy,
14
+ delete: Trash2,
15
+ import: Upload,
16
+ export: Download,
17
+ more: MoreHorizontal
18
+ },
19
+ preview: { eye: Eye, switch: LayoutGrid },
20
+ empty: { default: Inbox, search: SearchX },
21
+ intro: { info: Lightbulb },
22
+ header: { default: Database, byRecordType: {} },
23
+ group: { chevron: ChevronDown }
24
+ };
25
+ function mergeIcons(override) {
26
+ if (!override) return DEFAULT_ICONS;
27
+ return {
28
+ scope: { ...DEFAULT_ICONS.scope, ...override.scope ?? {} },
29
+ status: { ...DEFAULT_ICONS.status, ...override.status ?? {} },
30
+ action: { ...DEFAULT_ICONS.action, ...override.action ?? {} },
31
+ preview: { ...DEFAULT_ICONS.preview, ...override.preview ?? {} },
32
+ empty: { ...DEFAULT_ICONS.empty, ...override.empty ?? {} },
33
+ intro: { ...DEFAULT_ICONS.intro, ...override.intro ?? {} },
34
+ header: {
35
+ default: override.header?.default ?? DEFAULT_ICONS.header.default,
36
+ byRecordType: { ...DEFAULT_ICONS.header.byRecordType, ...override.header?.byRecordType ?? {} }
37
+ },
38
+ group: { ...DEFAULT_ICONS.group, ...override.group ?? {} }
39
+ };
40
+ }
41
+ function pickHeaderIcon(icons, recordType) {
42
+ return icons.header.byRecordType[recordType] ?? icons.header.default;
43
+ }
44
+
7
45
  // src/components/RecordsAdmin/types/i18n.ts
8
46
  var DEFAULT_I18N = {
9
47
  emptyTitle: "Nothing here yet",
@@ -900,48 +938,44 @@ var useDirtyNavigation = ({
900
938
  );
901
939
  return { runWithGuard };
902
940
  };
903
- var ICONS = {
904
- product: Box,
905
- facet: Tag,
906
- variant: Layers,
907
- batch: Package
908
- };
909
941
  var LABELS = {
910
942
  product: "Products",
911
943
  facet: "Shared",
912
- // Variant/Batch are drill-downs under a product, but kept here for hosts
913
- // that still pass them as top-level scopes. The shell omits them by default.
914
944
  variant: "Variants",
915
945
  batch: "Batches"
916
946
  };
917
- var ScopeTabs = ({ scopes, active, onChange, loading = false }) => /* @__PURE__ */ jsx("div", { role: "tablist", className: "flex gap-1 border-b", style: { borderColor: "hsl(var(--ra-border))" }, children: scopes.map((s) => {
918
- const Icon = ICONS[s];
919
- const isActive = active === s;
920
- return /* @__PURE__ */ jsxs(
921
- "button",
922
- {
923
- type: "button",
924
- role: "tab",
925
- "aria-selected": isActive,
926
- onClick: () => onChange(s),
927
- disabled: loading,
928
- className: cn(
929
- "flex items-center gap-1.5 px-3 py-2 text-xs border-b-2 -mb-px transition-colors",
930
- isActive ? "font-medium" : "opacity-60 hover:opacity-100",
931
- loading && "cursor-wait opacity-40"
932
- ),
933
- style: {
934
- borderColor: isActive ? "hsl(var(--ra-accent))" : "transparent",
935
- color: "hsl(var(--ra-text))"
947
+ var ScopeTabs = ({
948
+ scopes,
949
+ active,
950
+ onChange,
951
+ loading = false,
952
+ counts,
953
+ icons
954
+ }) => {
955
+ const iconMap = icons ?? DEFAULT_ICONS.scope;
956
+ return /* @__PURE__ */ jsx("div", { role: "tablist", className: "ra-tabs", "aria-label": "Record scope", children: scopes.map((s) => {
957
+ const Icon = iconMap[s] ?? DEFAULT_ICONS.scope[s];
958
+ const isActive = active === s;
959
+ const count = counts?.[s];
960
+ return /* @__PURE__ */ jsxs(
961
+ "button",
962
+ {
963
+ type: "button",
964
+ role: "tab",
965
+ "aria-selected": isActive,
966
+ onClick: () => onChange(s),
967
+ disabled: loading,
968
+ className: "ra-tab",
969
+ children: [
970
+ /* @__PURE__ */ jsx(Icon, { className: cn("ra-tab-icon w-3.5 h-3.5", loading && "animate-pulse") }),
971
+ /* @__PURE__ */ jsx("span", { children: LABELS[s] }),
972
+ typeof count === "number" && count > 0 && /* @__PURE__ */ jsx("span", { className: "ra-tab-count", children: count })
973
+ ]
936
974
  },
937
- children: [
938
- /* @__PURE__ */ jsx(Icon, { className: cn("w-3.5 h-3.5", loading && "animate-pulse") }),
939
- LABELS[s]
940
- ]
941
- },
942
- s
943
- );
944
- }) });
975
+ s
976
+ );
977
+ }) });
978
+ };
945
979
  var StatusFilterPills = ({ value, onChange, counts, i18n }) => {
946
980
  const opts = [
947
981
  { key: "all", label: i18n.filterAll, count: counts.all },
@@ -982,34 +1016,23 @@ var StatusDot = ({ source, status, className }) => {
982
1016
  };
983
1017
  var DefaultRecordRow = ({ record, ctx, compact = false }) => {
984
1018
  const { selected, onSelect, isDirty } = ctx;
1019
+ const ScopeIcon = record.scope.kind && record.scope.kind !== "collection" ? DEFAULT_ICONS.scope[record.scope.kind] : DEFAULT_ICONS.scope.product;
985
1020
  return /* @__PURE__ */ jsxs(
986
1021
  "button",
987
1022
  {
988
1023
  type: "button",
989
1024
  onClick: onSelect,
990
- className: cn(
991
- "w-full text-left flex items-center gap-2.5 px-3 transition-colors hover:bg-[hsl(var(--ra-muted))]",
992
- compact ? "py-1.5" : "py-2.5",
993
- selected && "ra-row-active"
994
- ),
1025
+ "data-selected": selected ? "true" : "false",
1026
+ className: cn("ra-row", compact && "ra-row-compact"),
1027
+ style: compact ? { paddingTop: "0.4rem", paddingBottom: "0.4rem" } : void 0,
995
1028
  children: [
996
- !compact && /* @__PURE__ */ jsx(StatusDot, { status: record.status }),
997
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
998
- /* @__PURE__ */ jsx("div", { className: "text-sm truncate", style: { color: "hsl(var(--ra-text))" }, children: record.label }),
999
- !compact && record.subtitle && /* @__PURE__ */ jsx("div", { className: "text-xs truncate", style: { color: "hsl(var(--ra-muted-text))" }, children: record.subtitle })
1029
+ !compact && /* @__PURE__ */ jsx("span", { className: "ra-row-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ScopeIcon, { className: "w-3.5 h-3.5" }) }),
1030
+ /* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
1031
+ /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: record.label }),
1032
+ !compact && record.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: record.subtitle })
1000
1033
  ] }),
1001
- record.badges?.slice(0, 2).map((b, i) => /* @__PURE__ */ jsx(
1002
- "span",
1003
- {
1004
- className: "text-[10px] px-1.5 py-0.5 rounded-sm shrink-0",
1005
- style: {
1006
- background: "hsl(var(--ra-muted))",
1007
- color: "hsl(var(--ra-muted-text))"
1008
- },
1009
- children: b.label
1010
- },
1011
- `${b.label}-${i}`
1012
- )),
1034
+ /* @__PURE__ */ jsx(StatusDot, { status: record.status }),
1035
+ record.badges?.slice(0, 1).map((b, i) => /* @__PURE__ */ jsx("span", { className: "ra-chip", "data-tone": "muted", children: b.label }, `${b.label}-${i}`)),
1013
1036
  isDirty && /* @__PURE__ */ jsx(
1014
1037
  "span",
1015
1038
  {
@@ -1032,14 +1055,13 @@ var DefaultRecordCard = ({ record, ctx, variant = "grid" }) => {
1032
1055
  type: "button",
1033
1056
  onClick: onSelect,
1034
1057
  className: cn(
1035
- "group flex flex-col text-left rounded-md overflow-hidden border transition-colors",
1036
- "hover:bg-[hsl(var(--ra-muted))]",
1058
+ "group flex flex-col text-left rounded-md overflow-hidden border ra-card-hover",
1037
1059
  selected && "ring-2"
1038
1060
  ),
1039
1061
  style: {
1040
- borderColor: "hsl(var(--ra-border))",
1041
- // selected ring
1042
- ...selected ? { boxShadow: "0 0 0 2px hsl(var(--ra-accent, var(--ra-text)))" } : {}
1062
+ background: "hsl(var(--ra-surface))",
1063
+ borderColor: selected ? "var(--ra-row-active-bd)" : "hsl(var(--ra-border))",
1064
+ boxShadow: selected ? `0 0 0 2px hsl(var(--ra-accent) / 0.45), var(--ra-card-shadow)` : "var(--ra-card-shadow)"
1043
1065
  },
1044
1066
  children: [
1045
1067
  /* @__PURE__ */ jsxs(
@@ -1059,8 +1081,8 @@ var DefaultRecordCard = ({ record, ctx, variant = "grid" }) => {
1059
1081
  ) : /* @__PURE__ */ jsx(
1060
1082
  "span",
1061
1083
  {
1062
- className: "text-2xl font-medium opacity-60",
1063
- style: { color: "hsl(var(--ra-text))" },
1084
+ className: "text-2xl ra-display",
1085
+ style: { color: "hsl(var(--ra-muted-text))" },
1064
1086
  children: initials(record.label)
1065
1087
  }
1066
1088
  ),
@@ -1076,22 +1098,10 @@ var DefaultRecordCard = ({ record, ctx, variant = "grid" }) => {
1076
1098
  ]
1077
1099
  }
1078
1100
  ),
1079
- /* @__PURE__ */ jsxs("div", { className: "p-2 min-w-0", children: [
1080
- /* @__PURE__ */ jsx("div", { className: "text-sm truncate", style: { color: "hsl(var(--ra-text))" }, children: record.label }),
1081
- variant === "gallery" && record.subtitle && /* @__PURE__ */ jsx("div", { className: "text-xs truncate", style: { color: "hsl(var(--ra-muted-text))" }, children: record.subtitle }),
1082
- record.badges && record.badges.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex gap-1 mt-1.5 flex-wrap", children: record.badges.slice(0, 3).map((b, i) => /* @__PURE__ */ jsx(
1083
- "span",
1084
- {
1085
- className: "text-[10px] px-1.5 py-0.5 rounded-sm",
1086
- style: {
1087
- background: "hsl(var(--ra-surface))",
1088
- border: "1px solid hsl(var(--ra-border))",
1089
- color: "hsl(var(--ra-muted-text))"
1090
- },
1091
- children: b.label
1092
- },
1093
- `${b.label}-${i}`
1094
- )) })
1101
+ /* @__PURE__ */ jsxs("div", { className: "p-2.5 min-w-0", children: [
1102
+ /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: record.label }),
1103
+ variant === "gallery" && record.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: record.subtitle }),
1104
+ record.badges && record.badges.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex gap-1 mt-1.5 flex-wrap", children: record.badges.slice(0, 3).map((b, i) => /* @__PURE__ */ jsx("span", { className: "ra-chip", "data-tone": "muted", children: b.label }, `${b.label}-${i}`)) })
1095
1105
  ] })
1096
1106
  ]
1097
1107
  }
@@ -1104,45 +1114,124 @@ var RecordList = ({
1104
1114
  dirtyRef,
1105
1115
  presentation = "list",
1106
1116
  renderListRow,
1107
- renderCard
1117
+ renderCard,
1118
+ groupBy
1108
1119
  }) => {
1109
1120
  const buildCtx = (item) => ({
1110
1121
  selected: item.ref === selectedRef,
1111
1122
  onSelect: () => onSelect(item),
1112
1123
  isDirty: !!dirtyRef && item.ref === dirtyRef
1113
1124
  });
1114
- if (presentation === "grid" || presentation === "gallery") {
1115
- const minColPx = presentation === "gallery" ? 200 : 120;
1116
- return /* @__PURE__ */ jsx(
1117
- "div",
1118
- {
1119
- className: "grid gap-2 p-2",
1120
- style: { gridTemplateColumns: `repeat(auto-fill, minmax(${minColPx}px, 1fr))` },
1121
- children: items.map((item) => {
1122
- const ctx = buildCtx(item);
1123
- return /* @__PURE__ */ jsx("div", { children: renderCard ? renderCard(item, ctx) : /* @__PURE__ */ jsx(DefaultRecordCard, { record: item, ctx, variant: presentation }) }, item.ref);
1124
- })
1125
+ const groups = useMemo(() => {
1126
+ if (!groupBy) return null;
1127
+ const buckets = /* @__PURE__ */ new Map();
1128
+ const orderedKeys = [];
1129
+ for (const item of items) {
1130
+ const g = groupBy(item) ?? { key: "__other", label: "Other" };
1131
+ if (!buckets.has(g.key)) {
1132
+ buckets.set(g.key, { ...g, items: [] });
1133
+ orderedKeys.push(g.key);
1125
1134
  }
1126
- );
1135
+ buckets.get(g.key).items.push(item);
1136
+ }
1137
+ return orderedKeys.map((k) => buckets.get(k));
1138
+ }, [items, groupBy]);
1139
+ const renderItems = (rows) => {
1140
+ if (presentation === "grid" || presentation === "gallery") {
1141
+ const minColPx = presentation === "gallery" ? 200 : 120;
1142
+ return /* @__PURE__ */ jsx(
1143
+ "div",
1144
+ {
1145
+ className: "grid gap-2 p-2",
1146
+ style: { gridTemplateColumns: `repeat(auto-fill, minmax(${minColPx}px, 1fr))` },
1147
+ children: rows.map((item) => {
1148
+ const ctx = buildCtx(item);
1149
+ return /* @__PURE__ */ jsx("div", { children: renderCard ? renderCard(item, ctx) : /* @__PURE__ */ jsx(DefaultRecordCard, { record: item, ctx, variant: presentation }) }, item.ref);
1150
+ })
1151
+ }
1152
+ );
1153
+ }
1154
+ const compact = presentation === "compact";
1155
+ return /* @__PURE__ */ jsx("ul", { children: rows.map((item) => {
1156
+ const ctx = buildCtx(item);
1157
+ return /* @__PURE__ */ jsx("li", { children: renderListRow ? renderListRow(item, ctx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: item, ctx, compact }) }, item.ref);
1158
+ }) });
1159
+ };
1160
+ if (groups) {
1161
+ return /* @__PURE__ */ jsx(GroupedList, { groups, renderItems });
1127
1162
  }
1128
- const compact = presentation === "compact";
1129
- return /* @__PURE__ */ jsx("ul", { className: "divide-y", style: { borderColor: "hsl(var(--ra-border))" }, children: items.map((item) => {
1130
- const ctx = buildCtx(item);
1131
- return /* @__PURE__ */ jsx("li", { children: renderListRow ? renderListRow(item, ctx) : /* @__PURE__ */ jsx(DefaultRecordRow, { record: item, ctx, compact }) }, item.ref);
1163
+ return renderItems(items);
1164
+ };
1165
+ var GroupedList = ({
1166
+ groups,
1167
+ renderItems
1168
+ }) => {
1169
+ const Chevron = DEFAULT_ICONS.group.chevron;
1170
+ const [open, setOpen] = useState(
1171
+ () => Object.fromEntries(groups.map((g) => [g.key, true]))
1172
+ );
1173
+ return /* @__PURE__ */ jsx(Fragment, { children: groups.map((g) => {
1174
+ const isOpen = open[g.key] !== false;
1175
+ return /* @__PURE__ */ jsxs("section", { className: "ra-group", "data-open": isOpen ? "true" : "false", children: [
1176
+ /* @__PURE__ */ jsxs(
1177
+ "button",
1178
+ {
1179
+ type: "button",
1180
+ className: "ra-group-summary",
1181
+ onClick: () => setOpen((s) => ({ ...s, [g.key]: !isOpen })),
1182
+ "aria-expanded": isOpen,
1183
+ children: [
1184
+ /* @__PURE__ */ jsx(Chevron, { className: "ra-group-chevron w-3 h-3" }),
1185
+ g.icon,
1186
+ /* @__PURE__ */ jsx("span", { className: "ra-group-name", children: g.label }),
1187
+ /* @__PURE__ */ jsx("span", { className: "ra-group-count", children: g.items.length })
1188
+ ]
1189
+ }
1190
+ ),
1191
+ /* @__PURE__ */ jsx("div", { className: "ra-group-body", children: renderItems(g.items) })
1192
+ ] }, g.key);
1132
1193
  }) });
1133
1194
  };
1134
1195
  var ProductList = RecordList;
1135
1196
  var FacetList = RecordList;
1136
1197
  var VariantList = RecordList;
1137
1198
  var BatchList = RecordList;
1138
- var EmptyState = ({ title, body, action }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-center p-8 gap-2", children: [
1139
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", style: { color: "hsl(var(--ra-text))" }, children: title }),
1140
- body && /* @__PURE__ */ jsx("div", { className: "text-xs", style: { color: "hsl(var(--ra-muted-text))" }, children: body }),
1141
- action
1199
+ var EmptyState = ({
1200
+ title,
1201
+ body,
1202
+ action,
1203
+ secondaryAction,
1204
+ icon: Icon = Inbox
1205
+ }) => /* @__PURE__ */ jsxs("div", { className: "ra-empty", children: [
1206
+ /* @__PURE__ */ jsx("div", { className: "ra-empty-icon", children: /* @__PURE__ */ jsx(Icon, { className: "w-6 h-6" }) }),
1207
+ /* @__PURE__ */ jsx("h3", { className: "ra-empty-title", children: title }),
1208
+ body && /* @__PURE__ */ jsx("p", { className: "ra-empty-body", children: body }),
1209
+ (action || secondaryAction) && /* @__PURE__ */ jsxs("div", { className: "ra-empty-actions", children: [
1210
+ action,
1211
+ secondaryAction
1212
+ ] })
1142
1213
  ] });
1143
- var LoadingState = () => /* @__PURE__ */ jsx("div", { className: "p-4 space-y-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { className: "h-10 rounded animate-pulse", style: { background: "hsl(var(--ra-muted))" } }, i)) });
1144
- var ErrorState = ({ error }) => /* @__PURE__ */ jsx("div", { className: "p-4 text-xs", style: { color: "hsl(0 70% 45%)" }, children: error.message || "Something went wrong." });
1145
- var ICONS2 = {
1214
+ var LoadingState = () => /* @__PURE__ */ jsx("div", { className: "p-3 space-y-2", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsx(
1215
+ "div",
1216
+ {
1217
+ className: "h-11 rounded-md animate-pulse",
1218
+ style: { background: "hsl(var(--ra-muted))" }
1219
+ },
1220
+ i
1221
+ )) });
1222
+ var ErrorState = ({ error }) => /* @__PURE__ */ jsxs("div", { className: "ra-empty", children: [
1223
+ /* @__PURE__ */ jsx(
1224
+ "div",
1225
+ {
1226
+ className: "ra-empty-icon",
1227
+ style: { background: "hsl(var(--ra-danger) / 0.10)", color: "hsl(var(--ra-danger))" },
1228
+ children: "!"
1229
+ }
1230
+ ),
1231
+ /* @__PURE__ */ jsx("h3", { className: "ra-empty-title", children: "Something went wrong" }),
1232
+ /* @__PURE__ */ jsx("p", { className: "ra-empty-body", children: error.message || "Please try again." })
1233
+ ] });
1234
+ var ICONS = {
1146
1235
  list: List,
1147
1236
  grid: LayoutGrid,
1148
1237
  gallery: Image,
@@ -1170,7 +1259,7 @@ var PresentationSwitcher = ({ options, value, onChange, i18n }) => {
1170
1259
  role: "tablist",
1171
1260
  "aria-label": "View",
1172
1261
  children: options.map((opt) => {
1173
- const Icon = ICONS2[opt];
1262
+ const Icon = ICONS[opt];
1174
1263
  const active = opt === value;
1175
1264
  return /* @__PURE__ */ jsx(
1176
1265
  "button",
@@ -1260,11 +1349,11 @@ var BulkActionsMenu = ({
1260
1349
  return () => document.removeEventListener("mousedown", onDocClick);
1261
1350
  }, [open]);
1262
1351
  const items = [
1263
- onApplyToMany && { label: i18n.applyToMany, onClick: onApplyToMany },
1264
- onCopyFrom && { label: i18n.copyFrom, onClick: onCopyFrom },
1265
- onClearMany && { label: i18n.clear, onClick: onClearMany },
1266
- onImportCsv && { label: i18n.importCsv, onClick: onImportCsv },
1267
- onExportCsv && { label: i18n.exportCsv, onClick: onExportCsv }
1352
+ onApplyToMany && { key: "apply", label: i18n.applyToMany, onClick: onApplyToMany, icon: Layers },
1353
+ onCopyFrom && { key: "copy", label: i18n.copyFrom, onClick: onCopyFrom, icon: Copy },
1354
+ onImportCsv && { key: "imp", label: i18n.importCsv, onClick: onImportCsv, icon: Upload, divider: true },
1355
+ onExportCsv && { key: "exp", label: i18n.exportCsv, onClick: onExportCsv, icon: Download },
1356
+ onClearMany && { key: "clear", label: i18n.clear, onClick: onClearMany, icon: Eraser, tone: "danger", divider: true }
1268
1357
  ].filter(Boolean);
1269
1358
  if (items.length === 0) return null;
1270
1359
  return /* @__PURE__ */ jsxs("div", { className: "relative", ref, children: [
@@ -1273,35 +1362,39 @@ var BulkActionsMenu = ({
1273
1362
  {
1274
1363
  type: "button",
1275
1364
  onClick: () => setOpen((v) => !v),
1276
- className: "flex items-center gap-1 px-3 py-1.5 text-xs rounded-md border hover:bg-[hsl(var(--ra-muted))]",
1277
- style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
1365
+ className: "ra-btn",
1366
+ "aria-haspopup": "menu",
1367
+ "aria-expanded": open,
1278
1368
  children: [
1279
1369
  i18n.bulkActions,
1280
- /* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3" })
1370
+ /* @__PURE__ */ jsx(ChevronDown, { className: "w-3.5 h-3.5 opacity-70" })
1281
1371
  ]
1282
1372
  }
1283
1373
  ),
1284
- open && /* @__PURE__ */ jsx(
1285
- "div",
1286
- {
1287
- className: "absolute right-0 mt-1 min-w-[180px] rounded-md border shadow-md py-1 z-10",
1288
- style: { background: "hsl(var(--ra-surface))", borderColor: "hsl(var(--ra-border))" },
1289
- children: items.map((it) => /* @__PURE__ */ jsx(
1374
+ open && /* @__PURE__ */ jsx("div", { className: "absolute right-0 mt-1.5 ra-bulk-menu", role: "menu", children: items.map((it, i) => {
1375
+ const Icon = it.icon;
1376
+ return /* @__PURE__ */ jsxs("div", { children: [
1377
+ it.divider && i > 0 && /* @__PURE__ */ jsx("div", { className: "ra-bulk-divider" }),
1378
+ /* @__PURE__ */ jsxs(
1290
1379
  "button",
1291
1380
  {
1292
1381
  type: "button",
1382
+ role: "menuitem",
1383
+ "data-tone": it.tone,
1293
1384
  onClick: () => {
1294
1385
  setOpen(false);
1295
1386
  it.onClick();
1296
1387
  },
1297
- className: "w-full text-left px-3 py-1.5 text-xs hover:bg-[hsl(var(--ra-muted))]",
1298
- style: { color: "hsl(var(--ra-text))" },
1299
- children: it.label
1300
- },
1301
- it.label
1302
- ))
1303
- }
1304
- )
1388
+ className: "ra-bulk-item",
1389
+ children: [
1390
+ /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5 opacity-80" }),
1391
+ it.label,
1392
+ it.tone === "danger" && /* @__PURE__ */ jsx(Trash2, { className: "w-3 h-3 ml-auto opacity-50" })
1393
+ ]
1394
+ }
1395
+ )
1396
+ ] }, it.key);
1397
+ }) })
1305
1398
  ] });
1306
1399
  };
1307
1400
  var DeleteButton = ({
@@ -1628,17 +1721,16 @@ var InlinePreview = ({ children, scopePicker, label = "Preview" }) => /* @__PURE
1628
1721
  ] }),
1629
1722
  children
1630
1723
  ] });
1631
- var SidePreview = ({ children, scopePicker, label = "Preview" }) => /* @__PURE__ */ jsxs(
1632
- "div",
1633
- {
1634
- className: "flex flex-col h-full border-l overflow-hidden",
1635
- style: { borderColor: "hsl(var(--ra-border))" },
1636
- children: [
1637
- /* @__PURE__ */ jsx(PreviewHeader, { label, scopePicker }),
1638
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-4", children })
1639
- ]
1640
- }
1641
- );
1724
+ var SidePreview = ({ children, scopePicker, label = "Preview" }) => /* @__PURE__ */ jsxs("div", { className: "ra-preview-rail", children: [
1725
+ /* @__PURE__ */ jsxs("header", { className: "ra-preview-rail-header", children: [
1726
+ /* @__PURE__ */ jsxs("div", { className: "ra-preview-rail-title", children: [
1727
+ /* @__PURE__ */ jsx(Eye, { className: "w-3 h-3" }),
1728
+ label
1729
+ ] }),
1730
+ scopePicker && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: scopePicker })
1731
+ ] }),
1732
+ /* @__PURE__ */ jsx("div", { className: "ra-preview-rail-body", children })
1733
+ ] });
1642
1734
  var TabbedPreview = ({
1643
1735
  editor,
1644
1736
  preview,
@@ -1861,48 +1953,99 @@ var PreviewScopePicker = ({
1861
1953
  )
1862
1954
  ] });
1863
1955
  };
1864
- var IntroCard = ({ title, body, onDismiss }) => /* @__PURE__ */ jsxs(
1865
- "div",
1866
- {
1867
- className: "relative rounded-lg border p-4 mb-4",
1868
- style: {
1869
- borderColor: "hsl(var(--ra-accent) / 0.3)",
1870
- background: "hsl(var(--ra-accent) / 0.04)"
1871
- },
1872
- children: [
1873
- /* @__PURE__ */ jsx(
1956
+ var TONE_ICON = {
1957
+ info: Lightbulb,
1958
+ success: CheckCircle2,
1959
+ warning: AlertTriangle
1960
+ };
1961
+ var IntroCard = ({ title, body, onDismiss, tone = "info", action }) => {
1962
+ const Icon = TONE_ICON[tone] ?? Info;
1963
+ return /* @__PURE__ */ jsxs("div", { className: "ra-intro", "data-tone": tone, role: "note", children: [
1964
+ /* @__PURE__ */ jsx("div", { className: "ra-intro-icon", children: /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" }) }),
1965
+ /* @__PURE__ */ jsxs("div", { className: "ra-intro-body", children: [
1966
+ /* @__PURE__ */ jsx("h4", { className: "ra-intro-title", children: title }),
1967
+ /* @__PURE__ */ jsxs("div", { className: "ra-intro-text", children: [
1968
+ body,
1969
+ action && /* @__PURE__ */ jsx("span", { className: "ml-1.5", children: action })
1970
+ ] })
1971
+ ] }),
1972
+ /* @__PURE__ */ jsx(
1973
+ "button",
1974
+ {
1975
+ type: "button",
1976
+ onClick: onDismiss,
1977
+ "aria-label": "Dismiss",
1978
+ className: "ra-intro-dismiss",
1979
+ children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
1980
+ }
1981
+ )
1982
+ ] });
1983
+ };
1984
+ var UtilityRow = ({ label, introHidden, onShowIntro }) => {
1985
+ if (!introHidden || !onShowIntro) return null;
1986
+ return /* @__PURE__ */ jsx(
1987
+ "div",
1988
+ {
1989
+ className: "flex items-center justify-end gap-2 text-xs",
1990
+ style: { color: "hsl(var(--ra-muted-text))" },
1991
+ children: /* @__PURE__ */ jsxs(
1874
1992
  "button",
1875
1993
  {
1876
1994
  type: "button",
1877
- onClick: onDismiss,
1878
- "aria-label": "Dismiss",
1879
- className: "absolute top-2 right-2 p-1 rounded hover:bg-black/5",
1880
- children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5 opacity-60" })
1995
+ onClick: onShowIntro,
1996
+ className: "ra-btn",
1997
+ "data-variant": "ghost",
1998
+ style: { padding: "0.25rem 0.55rem", fontSize: "0.7rem" },
1999
+ children: [
2000
+ /* @__PURE__ */ jsx(HelpCircle, { className: "w-3 h-3" }),
2001
+ "How ",
2002
+ label.toLowerCase(),
2003
+ " works"
2004
+ ]
1881
2005
  }
1882
- ),
1883
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium mb-1.5", style: { color: "hsl(var(--ra-text))" }, children: title }),
1884
- /* @__PURE__ */ jsx("div", { className: "text-xs", style: { color: "hsl(var(--ra-muted-text))" }, children: body })
1885
- ]
1886
- }
1887
- );
1888
- var UtilityRow = ({ label, recordType, introHidden, onShowIntro }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 mb-3 text-xs", style: { color: "hsl(var(--ra-muted-text))" }, children: [
1889
- /* @__PURE__ */ jsx("span", { className: "font-mono opacity-70", children: recordType }),
1890
- introHidden && onShowIntro && /* @__PURE__ */ jsxs(
1891
- "button",
1892
- {
1893
- type: "button",
1894
- onClick: onShowIntro,
1895
- className: "flex items-center gap-1 px-2 py-1 rounded-full border hover:bg-[hsl(var(--ra-muted))]",
1896
- style: { borderColor: "hsl(var(--ra-border))" },
1897
- children: [
1898
- /* @__PURE__ */ jsx(HelpCircle, { className: "w-3 h-3" }),
1899
- "How ",
1900
- label.toLowerCase(),
1901
- " works"
1902
- ]
2006
+ )
1903
2007
  }
1904
- )
1905
- ] });
2008
+ );
2009
+ };
2010
+ function ShellHeader({
2011
+ title,
2012
+ subtitle,
2013
+ headerIcon,
2014
+ headerActions,
2015
+ showStats = true,
2016
+ recordType,
2017
+ icons,
2018
+ stats
2019
+ }) {
2020
+ let iconNode = headerIcon;
2021
+ if (!iconNode) {
2022
+ const Icon = pickHeaderIcon(icons, recordType);
2023
+ iconNode = createElement(Icon, { className: "w-5 h-5", "aria-hidden": true });
2024
+ } else if (isValidElement(iconNode)) ;
2025
+ const hasStats = showStats && stats && (typeof stats.products === "number" || typeof stats.shared === "number");
2026
+ return /* @__PURE__ */ jsxs("header", { className: "ra-header", children: [
2027
+ /* @__PURE__ */ jsxs("div", { className: "ra-header__main", children: [
2028
+ /* @__PURE__ */ jsx("div", { className: "ra-header__icon", "aria-hidden": "true", children: iconNode }),
2029
+ /* @__PURE__ */ jsxs("div", { className: "ra-header__text", children: [
2030
+ /* @__PURE__ */ jsx("h1", { className: "ra-header__title", children: title }),
2031
+ subtitle ? /* @__PURE__ */ jsx("p", { className: "ra-header__subtitle", children: subtitle }) : null
2032
+ ] })
2033
+ ] }),
2034
+ /* @__PURE__ */ jsxs("div", { className: "ra-header__aside", children: [
2035
+ hasStats ? /* @__PURE__ */ jsxs("div", { className: "ra-header__stats", role: "group", "aria-label": "Record counts", children: [
2036
+ typeof stats.products === "number" && /* @__PURE__ */ jsxs("span", { className: "ra-header__stat", children: [
2037
+ /* @__PURE__ */ jsx("span", { className: "ra-header__stat-value", children: stats.products }),
2038
+ /* @__PURE__ */ jsx("span", { className: "ra-header__stat-label", children: "Products" })
2039
+ ] }),
2040
+ typeof stats.shared === "number" && /* @__PURE__ */ jsxs("span", { className: "ra-header__stat", children: [
2041
+ /* @__PURE__ */ jsx("span", { className: "ra-header__stat-value", children: stats.shared }),
2042
+ /* @__PURE__ */ jsx("span", { className: "ra-header__stat-label", children: "Shared" })
2043
+ ] })
2044
+ ] }) : null,
2045
+ headerActions ? /* @__PURE__ */ jsx("div", { className: "ra-header__actions", children: headerActions }) : null
2046
+ ] })
2047
+ ] });
2048
+ }
1906
2049
 
1907
2050
  // src/components/RecordsAdmin/data/csv.ts
1908
2051
  var escapeCell = (s) => {
@@ -2056,9 +2199,19 @@ function RecordsAdminShell(props) {
2056
2199
  renderEmpty,
2057
2200
  cardinality = "singleton",
2058
2201
  itemNoun = "item",
2059
- generateItemId
2202
+ generateItemId,
2203
+ title,
2204
+ subtitle,
2205
+ headerIcon,
2206
+ headerActions,
2207
+ showStats = true,
2208
+ icons: iconsOverride,
2209
+ groupBy,
2210
+ renderEmptyState,
2211
+ density = "comfortable"
2060
2212
  } = props;
2061
2213
  const i18n = { ...DEFAULT_I18N, ...i18nOverride ?? {} };
2214
+ const icons = useMemo(() => mergeIcons(iconsOverride), [iconsOverride]);
2062
2215
  const [presentation, setPresentation] = usePresentationPref({
2063
2216
  appId,
2064
2217
  recordType,
@@ -2375,177 +2528,212 @@ function RecordsAdminShell(props) {
2375
2528
  }
2376
2529
  });
2377
2530
  };
2378
- return /* @__PURE__ */ jsxs("div", { className: `ra-shell flex flex-col h-full ${className ?? ""}`, children: [
2379
- /* @__PURE__ */ jsxs("div", { className: "px-4 pt-4", children: [
2380
- intro && !dismissed && /* @__PURE__ */ jsx(
2381
- IntroCard,
2382
- {
2383
- title: intro.title,
2384
- body: intro.body,
2385
- onDismiss: () => {
2386
- dismiss();
2387
- onTelemetry?.({ type: "intro.dismiss", recordType });
2388
- }
2389
- }
2390
- ),
2391
- /* @__PURE__ */ jsx(
2392
- UtilityRow,
2393
- {
2394
- label,
2395
- recordType,
2396
- introHidden: dismissed && !!intro,
2397
- onShowIntro: undismiss
2398
- }
2399
- )
2400
- ] }),
2401
- /* @__PURE__ */ jsxs(
2402
- "div",
2403
- {
2404
- className: "flex-1 grid border-t overflow-hidden",
2405
- style: { gridTemplateColumns: "minmax(260px, 320px) 1fr", borderColor: "hsl(var(--ra-border))" },
2406
- children: [
2407
- /* @__PURE__ */ jsxs("aside", { className: "border-r overflow-hidden flex flex-col", style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" }, children: [
2408
- /* @__PURE__ */ jsx(
2409
- ScopeTabs,
2410
- {
2411
- scopes: topLevelScopes,
2412
- active: activeScope,
2413
- onChange: (s) => {
2414
- void runWithGuard(() => {
2415
- onTelemetry?.({ type: "scope.change", recordType, from: activeScope, to: s });
2416
- setActiveScope(s);
2417
- });
2418
- },
2419
- loading: probe.isLoading
2531
+ return /* @__PURE__ */ jsxs(
2532
+ "div",
2533
+ {
2534
+ className: `ra-shell flex flex-col h-full ${className ?? ""}`,
2535
+ "data-density": density,
2536
+ children: [
2537
+ /* @__PURE__ */ jsxs("div", { className: "px-4 pt-4 space-y-3", children: [
2538
+ intro && !dismissed && /* @__PURE__ */ jsx(
2539
+ IntroCard,
2540
+ {
2541
+ title: intro.title,
2542
+ body: intro.body,
2543
+ onDismiss: () => {
2544
+ dismiss();
2545
+ onTelemetry?.({ type: "intro.dismiss", recordType });
2420
2546
  }
2421
- ),
2422
- /* @__PURE__ */ jsxs("div", { className: "p-3 space-y-2.5 border-b", style: { borderColor: "hsl(var(--ra-border))" }, children: [
2423
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2424
- /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-w-0", children: [
2425
- /* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 opacity-50" }),
2426
- /* @__PURE__ */ jsx(
2427
- "input",
2547
+ }
2548
+ ),
2549
+ /* @__PURE__ */ jsx(
2550
+ ShellHeader,
2551
+ {
2552
+ title: title ?? label ?? recordType,
2553
+ subtitle,
2554
+ headerIcon,
2555
+ headerActions,
2556
+ showStats,
2557
+ recordType,
2558
+ icons,
2559
+ stats: {
2560
+ products: productBrowse.items.length,
2561
+ shared: recordList.items.length
2562
+ }
2563
+ }
2564
+ ),
2565
+ /* @__PURE__ */ jsx(
2566
+ UtilityRow,
2567
+ {
2568
+ label,
2569
+ introHidden: dismissed && !!intro,
2570
+ onShowIntro: undismiss
2571
+ }
2572
+ )
2573
+ ] }),
2574
+ /* @__PURE__ */ jsxs(
2575
+ "div",
2576
+ {
2577
+ className: "flex-1 grid border-t overflow-hidden",
2578
+ style: { gridTemplateColumns: "minmax(260px, 320px) 1fr", borderColor: "hsl(var(--ra-border))", marginTop: "0.75rem" },
2579
+ children: [
2580
+ /* @__PURE__ */ jsxs("aside", { className: "border-r overflow-hidden flex flex-col", style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" }, children: [
2581
+ /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
2582
+ ScopeTabs,
2583
+ {
2584
+ scopes: topLevelScopes,
2585
+ active: activeScope,
2586
+ onChange: (s) => {
2587
+ void runWithGuard(() => {
2588
+ onTelemetry?.({ type: "scope.change", recordType, from: activeScope, to: s });
2589
+ setActiveScope(s);
2590
+ });
2591
+ },
2592
+ loading: probe.isLoading,
2593
+ counts: {
2594
+ product: productBrowse.items.length,
2595
+ facet: recordList.items.length
2596
+ },
2597
+ icons: icons.scope
2598
+ }
2599
+ ) }),
2600
+ /* @__PURE__ */ jsxs("div", { className: "p-3 space-y-2.5 border-b", style: { borderColor: "hsl(var(--ra-border))" }, children: [
2601
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2602
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-w-0", children: [
2603
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 opacity-50" }),
2604
+ /* @__PURE__ */ jsx(
2605
+ "input",
2606
+ {
2607
+ type: "text",
2608
+ value: search,
2609
+ onChange: (e) => setSearch(e.target.value),
2610
+ placeholder: i18n.searchPlaceholder,
2611
+ className: "w-full pl-8 pr-3 py-1.5 text-xs rounded-md border bg-transparent focus:outline-none focus:ring-1",
2612
+ style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" }
2613
+ }
2614
+ )
2615
+ ] }),
2616
+ /* @__PURE__ */ jsx(
2617
+ PresentationSwitcher,
2618
+ {
2619
+ options: presentations,
2620
+ value: presentation,
2621
+ onChange: onPresentationChange,
2622
+ i18n
2623
+ }
2624
+ )
2625
+ ] }),
2626
+ !isProductTab && /* @__PURE__ */ jsx(StatusFilterPills, { value: filter, onChange: setFilter, counts: recordList.counts, i18n }),
2627
+ cardinality === "collection" && !isProductTab && editingScope && /* @__PURE__ */ jsxs(
2628
+ "button",
2428
2629
  {
2429
- type: "text",
2430
- value: search,
2431
- onChange: (e) => setSearch(e.target.value),
2432
- placeholder: i18n.searchPlaceholder,
2433
- className: "w-full pl-8 pr-3 py-1.5 text-xs rounded-md border bg-transparent focus:outline-none focus:ring-1",
2434
- style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" }
2630
+ type: "button",
2631
+ onClick: () => {
2632
+ void runWithGuard(() => {
2633
+ const id = generateItemId ? generateItemId() : defaultItemId();
2634
+ const baseRef = editingScope.raw;
2635
+ const itemRef = baseRef ? `${baseRef}/item:${id}` : `item:${id}`;
2636
+ setSelectedFacetRef(itemRef);
2637
+ onTelemetry?.({ type: "item.create", recordType, scopeRef: baseRef });
2638
+ });
2639
+ },
2640
+ className: "w-full inline-flex items-center justify-center gap-1.5 text-xs py-1.5 rounded-md border transition-colors hover:bg-[hsl(var(--ra-muted))]",
2641
+ style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
2642
+ children: [
2643
+ /* @__PURE__ */ jsx(Plus, { className: "w-3.5 h-3.5" }),
2644
+ i18n.newItem || `New ${itemNoun}`
2645
+ ]
2435
2646
  }
2436
2647
  )
2437
2648
  ] }),
2438
- /* @__PURE__ */ jsx(
2439
- PresentationSwitcher,
2440
- {
2441
- options: presentations,
2442
- value: presentation,
2443
- onChange: onPresentationChange,
2444
- i18n
2445
- }
2446
- )
2649
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
2650
+ leftLoading && /* @__PURE__ */ jsx(LoadingState, {}),
2651
+ !leftLoading && leftError && /* @__PURE__ */ jsx(ErrorState, { error: leftError }),
2652
+ !leftLoading && !leftError && leftItems.length === 0 && (renderEmpty ? renderEmpty({ scope: editingScope }) : renderEmptyState ? renderEmptyState({ scope: activeScope }) : /* @__PURE__ */ jsx(
2653
+ EmptyState,
2654
+ {
2655
+ icon: search ? icons.empty.search : icons.empty.default,
2656
+ title: search ? i18n.noResults : i18n.emptyTitle,
2657
+ body: search ? void 0 : i18n.emptyBody
2658
+ }
2659
+ )),
2660
+ !leftLoading && !leftError && leftItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2661
+ /* @__PURE__ */ jsx(
2662
+ RecordList,
2663
+ {
2664
+ items: leftItems,
2665
+ selectedRef: leftSelectedRef,
2666
+ onSelect: onLeftSelect,
2667
+ dirtyRef: editorCtx.isDirty ? editingScope?.raw : void 0,
2668
+ presentation,
2669
+ renderListRow,
2670
+ renderCard,
2671
+ groupBy
2672
+ }
2673
+ ),
2674
+ isProductTab && !productPinned && productBrowse.hasNextPage && /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
2675
+ "button",
2676
+ {
2677
+ type: "button",
2678
+ onClick: () => {
2679
+ void productBrowse.fetchNextPage();
2680
+ },
2681
+ disabled: productBrowse.isFetchingNextPage,
2682
+ className: "w-full text-xs py-2 rounded-md border transition-opacity disabled:opacity-50 hover:bg-[hsl(var(--ra-muted))]",
2683
+ style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
2684
+ children: productBrowse.isFetchingNextPage ? "Loading\u2026" : `Load more (${leftItems.length} shown)`
2685
+ }
2686
+ ) })
2687
+ ] })
2688
+ ] })
2447
2689
  ] }),
2448
- !isProductTab && /* @__PURE__ */ jsx(StatusFilterPills, { value: filter, onChange: setFilter, counts: recordList.counts, i18n }),
2449
- cardinality === "collection" && !isProductTab && editingScope && /* @__PURE__ */ jsxs(
2450
- "button",
2451
- {
2452
- type: "button",
2453
- onClick: () => {
2454
- void runWithGuard(() => {
2455
- const id = generateItemId ? generateItemId() : defaultItemId();
2456
- const baseRef = editingScope.raw;
2457
- const itemRef = baseRef ? `${baseRef}/item:${id}` : `item:${id}`;
2458
- setSelectedFacetRef(itemRef);
2459
- onTelemetry?.({ type: "item.create", recordType, scopeRef: baseRef });
2460
- });
2461
- },
2462
- className: "w-full inline-flex items-center justify-center gap-1.5 text-xs py-1.5 rounded-md border transition-colors hover:bg-[hsl(var(--ra-muted))]",
2463
- style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
2464
- children: [
2465
- /* @__PURE__ */ jsx(Plus, { className: "w-3.5 h-3.5" }),
2466
- i18n.newItem || `New ${itemNoun}`
2467
- ]
2468
- }
2469
- )
2470
- ] }),
2471
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
2472
- leftLoading && /* @__PURE__ */ jsx(LoadingState, {}),
2473
- !leftLoading && leftError && /* @__PURE__ */ jsx(ErrorState, { error: leftError }),
2474
- !leftLoading && !leftError && leftItems.length === 0 && (renderEmpty ? renderEmpty({ scope: editingScope }) : /* @__PURE__ */ jsx(EmptyState, { title: i18n.noResults, body: search ? void 0 : i18n.emptyBody })),
2475
- !leftLoading && !leftError && leftItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2476
- /* @__PURE__ */ jsx(
2477
- RecordList,
2690
+ /* @__PURE__ */ jsxs("main", { className: "overflow-hidden", children: [
2691
+ !editingScope && activeScope === "product" && !selectedProductId && /* @__PURE__ */ jsx(EmptyState, { title: i18n.emptyTitle, body: i18n.emptyBody }),
2692
+ !editingScope && activeScope === "facet" && /* @__PURE__ */ jsx(EmptyState, { title: i18n.emptyTitle, body: i18n.emptyBody }),
2693
+ isProductTab && selectedProductId && /* @__PURE__ */ jsx(
2694
+ ProductDrillDown,
2478
2695
  {
2479
- items: leftItems,
2480
- selectedRef: leftSelectedRef,
2481
- onSelect: onLeftSelect,
2482
- dirtyRef: editorCtx.isDirty ? editingScope?.raw : void 0,
2483
- presentation,
2484
- renderListRow,
2485
- renderCard
2486
- }
2487
- ),
2488
- isProductTab && !productPinned && productBrowse.hasNextPage && /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
2489
- "button",
2490
- {
2491
- type: "button",
2492
- onClick: () => {
2493
- void productBrowse.fetchNextPage();
2696
+ productLabel: productBrowse.items.find((p) => p.id === selectedProductId)?.name ?? selectedProductId,
2697
+ showVariants: drillVariantsAllowed,
2698
+ showBatches: drillBatchesAllowed,
2699
+ active: drillTab,
2700
+ onChange: (t) => {
2701
+ void runWithGuard(() => {
2702
+ setDrillTab(t);
2703
+ if (t === "product") {
2704
+ setSelectedVariantId(void 0);
2705
+ setSelectedBatchId(void 0);
2706
+ }
2707
+ });
2708
+ },
2709
+ selectedChildId: drillTab === "variant" ? selectedVariantId : drillTab === "batch" ? selectedBatchId : void 0,
2710
+ onSelectChild: (id) => {
2711
+ void runWithGuard(() => {
2712
+ if (drillTab === "variant") setSelectedVariantId(id);
2713
+ else if (drillTab === "batch") setSelectedBatchId(id);
2714
+ });
2494
2715
  },
2495
- disabled: productBrowse.isFetchingNextPage,
2496
- className: "w-full text-xs py-2 rounded-md border transition-opacity disabled:opacity-50 hover:bg-[hsl(var(--ra-muted))]",
2497
- style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
2498
- children: productBrowse.isFetchingNextPage ? "Loading\u2026" : `Load more (${leftItems.length} shown)`
2716
+ variants: variantChildren.items,
2717
+ batches: batchChildren.items,
2718
+ variantsLoading: variantChildren.isLoading,
2719
+ batchesLoading: batchChildren.isLoading,
2720
+ children: editingScope ? renderEditorWithPreview() : /* @__PURE__ */ jsx(
2721
+ EmptyState,
2722
+ {
2723
+ title: drillTab === "variant" ? "Pick a variant" : "Pick a batch",
2724
+ body: `Select a ${drillTab} on the left to edit its ${recordType}.`
2725
+ }
2726
+ )
2499
2727
  }
2500
- ) })
2728
+ ),
2729
+ !isProductTab && editingScope && renderEditorWithPreview()
2501
2730
  ] })
2502
- ] })
2503
- ] }),
2504
- /* @__PURE__ */ jsxs("main", { className: "overflow-hidden", children: [
2505
- !editingScope && activeScope === "product" && !selectedProductId && /* @__PURE__ */ jsx(EmptyState, { title: i18n.emptyTitle, body: i18n.emptyBody }),
2506
- !editingScope && activeScope === "facet" && /* @__PURE__ */ jsx(EmptyState, { title: i18n.emptyTitle, body: i18n.emptyBody }),
2507
- isProductTab && selectedProductId && /* @__PURE__ */ jsx(
2508
- ProductDrillDown,
2509
- {
2510
- productLabel: productBrowse.items.find((p) => p.id === selectedProductId)?.name ?? selectedProductId,
2511
- showVariants: drillVariantsAllowed,
2512
- showBatches: drillBatchesAllowed,
2513
- active: drillTab,
2514
- onChange: (t) => {
2515
- void runWithGuard(() => {
2516
- setDrillTab(t);
2517
- if (t === "product") {
2518
- setSelectedVariantId(void 0);
2519
- setSelectedBatchId(void 0);
2520
- }
2521
- });
2522
- },
2523
- selectedChildId: drillTab === "variant" ? selectedVariantId : drillTab === "batch" ? selectedBatchId : void 0,
2524
- onSelectChild: (id) => {
2525
- void runWithGuard(() => {
2526
- if (drillTab === "variant") setSelectedVariantId(id);
2527
- else if (drillTab === "batch") setSelectedBatchId(id);
2528
- });
2529
- },
2530
- variants: variantChildren.items,
2531
- batches: batchChildren.items,
2532
- variantsLoading: variantChildren.isLoading,
2533
- batchesLoading: batchChildren.isLoading,
2534
- children: editingScope ? renderEditorWithPreview() : /* @__PURE__ */ jsx(
2535
- EmptyState,
2536
- {
2537
- title: drillTab === "variant" ? "Pick a variant" : "Pick a batch",
2538
- body: `Select a ${drillTab} on the left to edit its ${recordType}.`
2539
- }
2540
- )
2541
- }
2542
- ),
2543
- !isProductTab && editingScope && renderEditorWithPreview()
2544
- ] })
2545
- ]
2546
- }
2547
- )
2548
- ] });
2731
+ ]
2732
+ }
2733
+ )
2734
+ ]
2735
+ }
2736
+ );
2549
2737
  }
2550
2738
  var RecordBrowser = ({
2551
2739
  scopes,
@@ -2828,6 +3016,6 @@ function useMergedRecord(args) {
2828
3016
  };
2829
3017
  }
2830
3018
 
2831
- export { ALL_PRESENTATIONS, BatchList, BulkActionsMenu, DEFAULT_I18N, DefaultRecordCard, DefaultRecordRow, DeleteButton, DrawerPreview, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, LoadingState, PresentationSwitcher, PreviewScopePicker, PreviewToggleButton, ProductDrillDown, ProductList, RecordBrowser, RecordEditor, RecordList, RecordsAdminShell, ResolvedPreview, ScopeBreadcrumb, ScopeTabs, SidePreview, StatusDot, StatusFilterPills, TabbedPreview, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, parseRef, parsedRefToScope, parsedRefToTarget, resolutionChain, resolveRecord, restoreRecord, scopesEqual, upsertRecord, useCollectedRecords, useDirtyNavigation, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordEditor, useRecordList, useResolvedRecord, useScopeProbe, useUnsavedGuard };
3019
+ export { ALL_PRESENTATIONS, BatchList, BulkActionsMenu, DEFAULT_I18N, DEFAULT_ICONS, DefaultRecordCard, DefaultRecordRow, DeleteButton, DrawerPreview, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, LoadingState, PresentationSwitcher, PreviewScopePicker, PreviewToggleButton, ProductDrillDown, ProductList, RecordBrowser, RecordEditor, RecordList, RecordsAdminShell, ResolvedPreview, ScopeBreadcrumb, ScopeTabs, SidePreview, StatusDot, StatusFilterPills, TabbedPreview, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, mergeIcons, parseRef, parsedRefToScope, parsedRefToTarget, pickHeaderIcon, resolutionChain, resolveRecord, restoreRecord, scopesEqual, upsertRecord, useCollectedRecords, useDirtyNavigation, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordEditor, useRecordList, useResolvedRecord, useScopeProbe, useUnsavedGuard };
2832
3020
  //# sourceMappingURL=index.js.map
2833
3021
  //# sourceMappingURL=index.js.map