@asteby/metacore-runtime-react 18.13.1 → 18.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 18.13.3
4
+
5
+ ### Patch Changes
6
+
7
+ - b45b5b1: ActivityDiff: created/deleted events render as a true two-column grid (Campo + Valor) — the old layout emitted a placeholder cell plus a col-span-2 value into a 3-column grid, overflowing the row so the value wrapped below its label. RecordHistory gains `moduleLabel` so the event-header badge can show the localized model title (e.g. "Clientes") instead of the raw addon_key.
8
+
9
+ ## 18.13.2
10
+
11
+ ### Patch Changes
12
+
13
+ - b37e1d7: RecordHistory: event headers show the actor's photo, not just initials — `ActivityEvent` gains `actor_avatar` and the component renders it via the new `resolveAvatarUrl` prop (host resolves the storage path, e.g. ops' `getStorageUrl(path, 'avatars')`; identity fallback for absolute paths).
14
+
3
15
  ## 18.13.1
4
16
 
5
17
  ### Patch Changes
@@ -25,6 +25,8 @@ export interface ActivityEvent {
25
25
  correlation_id?: string | null;
26
26
  actor_id?: string | null;
27
27
  actor_label?: string | null;
28
+ /** Storage path of the actor's avatar image, when the backend resolves one. */
29
+ actor_avatar?: string | null;
28
30
  addon_key: string;
29
31
  model: string;
30
32
  record_id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"activity-diff.d.ts","sourceRoot":"","sources":["../src/activity-diff.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAO/C;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,IAAI,CAAA;IAC/D,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,iBAAiB;IAC9B,oCAAoC;IACpC,KAAK,EAAE,aAAa,CAAA;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAuGD;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAmJpD,CAAA"}
1
+ {"version":3,"file":"activity-diff.d.ts","sourceRoot":"","sources":["../src/activity-diff.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAO/C;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,IAAI,CAAA;IAC/D,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,iBAAiB;IAC9B,oCAAoC;IACpC,KAAK,EAAE,aAAa,CAAA;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAuGD;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA4KpD,CAAA"}
@@ -137,7 +137,7 @@ export const ActivityDiff = ({ event, columns, timeZone, currency, locale = 'es'
137
137
  if (allKeys.length === 0 && !event.summary) {
138
138
  return (_jsx("div", { className: cn('text-sm text-muted-foreground italic py-1', className), children: "Sin campos registrados." }));
139
139
  }
140
- return (_jsxs("div", { className: cn('space-y-2', className), children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Badge, { variant: "outline", className: cn('text-xs font-medium px-2 py-0.5', variantBadge.className), children: variantBadge.label || event.action }), changed.size > 0 && variant === 'updated' && (_jsxs("span", { className: "text-xs text-muted-foreground", children: [changed.size, " campo", changed.size !== 1 ? 's' : '', " modificado", changed.size !== 1 ? 's' : ''] })), allKeys.length > 0 && variant === 'updated' && (_jsx("button", { type: "button", onClick: () => setShowOnlyChanged((v) => !v), className: "ml-auto text-xs text-primary hover:underline", children: showOnlyChanged ? `Ver todos (${allKeys.length})` : 'Solo cambios' }))] }), event.summary && (_jsx("p", { className: "text-sm text-muted-foreground italic", children: event.summary })), displayedKeys.length > 0 && (_jsxs("div", { className: "rounded-lg border border-border/60 overflow-hidden text-sm", children: [_jsxs("div", { className: "grid grid-cols-[1fr_1fr_1fr] border-b border-border/40 bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [_jsx("span", { children: "Campo" }), !isCreated && _jsx("span", { children: isDeleted ? 'Valor' : 'Antes' }), isCreated && _jsx("span", {}), !isDeleted && _jsx("span", { children: isCreated ? 'Valor' : 'Después' }), isDeleted && _jsx("span", {})] }), displayedKeys.map((key, idx) => {
140
+ return (_jsxs("div", { className: cn('space-y-2', className), children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Badge, { variant: "outline", className: cn('text-xs font-medium px-2 py-0.5', variantBadge.className), children: variantBadge.label || event.action }), changed.size > 0 && variant === 'updated' && (_jsxs("span", { className: "text-xs text-muted-foreground", children: [changed.size, " campo", changed.size !== 1 ? 's' : '', " modificado", changed.size !== 1 ? 's' : ''] })), allKeys.length > 0 && variant === 'updated' && (_jsx("button", { type: "button", onClick: () => setShowOnlyChanged((v) => !v), className: "ml-auto text-xs text-primary hover:underline", children: showOnlyChanged ? `Ver todos (${allKeys.length})` : 'Solo cambios' }))] }), event.summary && (_jsx("p", { className: "text-sm text-muted-foreground italic", children: event.summary })), displayedKeys.length > 0 && (_jsxs("div", { className: "rounded-lg border border-border/60 overflow-hidden text-sm", children: [isCreated || isDeleted ? (_jsxs("div", { className: "grid grid-cols-[1fr_2fr] border-b border-border/40 bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground gap-x-2", children: [_jsx("span", { children: "Campo" }), _jsx("span", { children: isDeleted ? 'Valor anterior' : 'Valor' })] })) : (_jsxs("div", { className: "grid grid-cols-[1fr_1fr_1fr] border-b border-border/40 bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground gap-x-2", children: [_jsx("span", { children: "Campo" }), _jsx("span", { children: "Antes" }), _jsx("span", { children: "Despu\u00E9s" })] })), displayedKeys.map((key, idx) => {
141
141
  const col = resolveColumn(key, columns);
142
142
  const label = resolveLabel(key, columns);
143
143
  const isChanged = changed.has(key);
@@ -155,9 +155,12 @@ export const ActivityDiff = ({ event, columns, timeZone, currency, locale = 'es'
155
155
  ? ROW_STYLE.created
156
156
  : isDeleted
157
157
  ? ROW_STYLE.deleted
158
- : isChanged
159
- ? {}
160
- : {};
161
- return (_jsxs("div", { style: rowStyle, className: cn('grid grid-cols-[1fr_1fr_1fr] items-start px-3 py-2 gap-x-2', idx !== displayedKeys.length - 1 && 'border-b border-border/30', isChanged && variant === 'updated' && 'bg-yellow-50/40 dark:bg-yellow-950/10'), children: [_jsx("span", { className: "text-xs font-medium text-foreground/70 pt-0.5 truncate", title: label, children: label }), !isCreated ? (_jsx("span", { className: cn(isDeleted ? 'col-span-2' : ''), children: _jsx(ActivityValueRenderer, { value: isDeleted ? fromVal : fromVal, col: col, timeZone: timeZone, currency: currency, locale: locale }) })) : (_jsx("span", {})), !isDeleted ? (_jsxs("span", { className: cn(isCreated ? 'col-span-2' : ''), children: [isChanged && variant === 'updated' && (_jsx("span", { className: "inline-flex items-center gap-1 align-middle mr-1", children: _jsx(ArrowRight, { className: "h-3 w-3 text-muted-foreground/50 shrink-0" }) })), _jsx(ActivityValueRenderer, { value: isCreated ? toVal : toVal, col: col, timeZone: timeZone, currency: currency, locale: locale })] })) : (_jsx("span", {}))] }, key));
158
+ : {};
159
+ // Single-snapshot row: label + one value cell, aligned
160
+ // with the two-column header above.
161
+ if (isCreated || isDeleted) {
162
+ return (_jsxs("div", { style: rowStyle, className: cn('grid grid-cols-[1fr_2fr] items-start px-3 py-2 gap-x-2', idx !== displayedKeys.length - 1 && 'border-b border-border/30'), children: [_jsx("span", { className: "text-xs font-medium text-foreground/70 pt-0.5 truncate", title: label, children: label }), _jsx("span", { className: "min-w-0", children: _jsx(ActivityValueRenderer, { value: isDeleted ? fromVal : toVal, col: col, timeZone: timeZone, currency: currency, locale: locale }) })] }, key));
163
+ }
164
+ return (_jsxs("div", { className: cn('grid grid-cols-[1fr_1fr_1fr] items-start px-3 py-2 gap-x-2', idx !== displayedKeys.length - 1 && 'border-b border-border/30', isChanged && variant === 'updated' && 'bg-yellow-50/40 dark:bg-yellow-950/10'), children: [_jsx("span", { className: "text-xs font-medium text-foreground/70 pt-0.5 truncate", title: label, children: label }), _jsx("span", { className: "min-w-0", children: _jsx(ActivityValueRenderer, { value: fromVal, col: col, timeZone: timeZone, currency: currency, locale: locale }) }), _jsxs("span", { className: "min-w-0", children: [isChanged && variant === 'updated' && (_jsx("span", { className: "inline-flex items-center gap-1 align-middle mr-1", children: _jsx(ArrowRight, { className: "h-3 w-3 text-muted-foreground/50 shrink-0" }) })), _jsx(ActivityValueRenderer, { value: toVal, col: col, timeZone: timeZone, currency: currency, locale: locale })] })] }, key));
162
165
  })] }))] }));
163
166
  };
@@ -37,6 +37,17 @@ export interface RecordHistoryProps {
37
37
  * detail page (e.g. `/activity/:id`). Omitted → no button.
38
38
  */
39
39
  onOpenEvent?: (event: ActivityEvent) => void;
40
+ /**
41
+ * Resolves an event's `actor_avatar` storage path to a fetchable URL
42
+ * (e.g. ops' `getStorageUrl(path, 'avatars')`). Identity when omitted —
43
+ * fine for absolute same-origin paths.
44
+ */
45
+ resolveAvatarUrl?: (path: string) => string;
46
+ /**
47
+ * Localized label for the badge on each event header (e.g. "Clientes").
48
+ * Falls back to the event's raw `addon_key` when omitted.
49
+ */
50
+ moduleLabel?: string;
40
51
  }
41
52
  /**
42
53
  * Shows the full activity history of a single record as a vertical timeline.
@@ -1 +1 @@
1
- {"version":3,"file":"record-history.d.ts","sourceRoot":"","sources":["../src/record-history.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAOpD,MAAM,WAAW,kBAAkB;IAC/B;;;OAGG;IACH,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;CAC/C;AAoCD;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA6KtD,CAAA"}
1
+ {"version":3,"file":"record-history.d.ts","sourceRoot":"","sources":["../src/record-history.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAe9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAOpD,MAAM,WAAW,kBAAkB;IAC/B;;;OAGG;IACH,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC5C;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IAC3C;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACvB;AAoCD;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAsLtD,CAAA"}
@@ -15,7 +15,7 @@ import { formatDistanceToNow } from 'date-fns';
15
15
  import { es, enUS } from 'date-fns/locale';
16
16
  import { ChevronDown, ChevronRight, Clock, ExternalLink } from 'lucide-react';
17
17
  import { cn } from '@asteby/metacore-ui/lib';
18
- import { Avatar, AvatarFallback, Badge, Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@asteby/metacore-ui/primitives';
18
+ import { Avatar, AvatarFallback, AvatarImage, Badge, Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@asteby/metacore-ui/primitives';
19
19
  import { getInitials } from '@asteby/metacore-ui/lib';
20
20
  import { ActivityDiff } from './activity-diff';
21
21
  // ---------------------------------------------------------------------------
@@ -51,7 +51,7 @@ function actionDotColor(action) {
51
51
  * Each event is collapsible — the header shows actor + time; expanding reveals
52
52
  * the <ActivityDiff> with field-level changes.
53
53
  */
54
- export const RecordHistory = ({ events, columns, timeZone, currency, locale = 'es', className, onOpenEvent, }) => {
54
+ export const RecordHistory = ({ events, columns, timeZone, currency, locale = 'es', className, onOpenEvent, resolveAvatarUrl, moduleLabel, }) => {
55
55
  const dateLocale = locale === 'en' ? enUS : es;
56
56
  // Sort: most recent first
57
57
  const sorted = React.useMemo(() => [...events].sort((a, b) => new Date(b.occurred_at).getTime() - new Date(a.occurred_at).getTime()), [events]);
@@ -94,7 +94,7 @@ export const RecordHistory = ({ events, columns, timeZone, currency, locale = 'e
94
94
  return event.occurred_at;
95
95
  }
96
96
  })();
97
- return (_jsx(Collapsible, { open: isOpen, onOpenChange: () => toggle(event.id), children: _jsxs("div", { className: "relative", children: [_jsx("span", { className: "absolute -left-5 top-3.5 h-2.5 w-2.5 rounded-full border-2 border-background -translate-x-[4px]", style: { background: dotColor }, "aria-hidden": "true" }), _jsxs("div", { className: "rounded-lg border border-border/60 bg-card overflow-hidden", children: [_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-muted/30 transition-colors", children: [_jsx(Avatar, { className: "h-7 w-7 rounded-full shrink-0", children: _jsx(AvatarFallback, { className: "text-[9px] font-bold bg-primary/10 text-primary", children: getInitials(actor) }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "text-sm font-semibold text-foreground truncate", children: actor }), _jsx("span", { className: "text-sm text-muted-foreground", children: actionLabel(event.action) })] }), _jsxs("div", { className: "flex items-center gap-1.5 mt-0.5", children: [_jsx(Clock, { className: "h-3 w-3 text-muted-foreground/60 shrink-0" }), _jsx("span", { className: "text-xs text-muted-foreground", title: fullDate, children: timeAgo }), event.addon_key && (_jsx(Badge, { variant: "outline", className: "text-[10px] px-1.5 py-0 h-4 ml-1", children: event.addon_key }))] })] }), onOpenEvent && (_jsx("span", { role: "button", tabIndex: 0, "aria-label": "Ver en registro de actividad", title: "Ver en registro de actividad", className: "shrink-0 rounded-md p-1.5 text-muted-foreground hover:text-foreground hover:bg-muted/60 transition-colors cursor-pointer", onClick: (e) => {
97
+ return (_jsx(Collapsible, { open: isOpen, onOpenChange: () => toggle(event.id), children: _jsxs("div", { className: "relative", children: [_jsx("span", { className: "absolute -left-5 top-3.5 h-2.5 w-2.5 rounded-full border-2 border-background -translate-x-[4px]", style: { background: dotColor }, "aria-hidden": "true" }), _jsxs("div", { className: "rounded-lg border border-border/60 bg-card overflow-hidden", children: [_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-muted/30 transition-colors", children: [_jsxs(Avatar, { className: "h-7 w-7 rounded-full shrink-0", children: [event.actor_avatar ? (_jsx(AvatarImage, { src: resolveAvatarUrl ? resolveAvatarUrl(event.actor_avatar) : event.actor_avatar, alt: actor })) : null, _jsx(AvatarFallback, { className: "text-[9px] font-bold bg-primary/10 text-primary", children: getInitials(actor) })] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "text-sm font-semibold text-foreground truncate", children: actor }), _jsx("span", { className: "text-sm text-muted-foreground", children: actionLabel(event.action) })] }), _jsxs("div", { className: "flex items-center gap-1.5 mt-0.5", children: [_jsx(Clock, { className: "h-3 w-3 text-muted-foreground/60 shrink-0" }), _jsx("span", { className: "text-xs text-muted-foreground", title: fullDate, children: timeAgo }), (moduleLabel || event.addon_key) && (_jsx(Badge, { variant: "outline", className: "text-[10px] px-1.5 py-0 h-4 ml-1", children: moduleLabel || event.addon_key }))] })] }), onOpenEvent && (_jsx("span", { role: "button", tabIndex: 0, "aria-label": "Ver en registro de actividad", title: "Ver en registro de actividad", className: "shrink-0 rounded-md p-1.5 text-muted-foreground hover:text-foreground hover:bg-muted/60 transition-colors cursor-pointer", onClick: (e) => {
98
98
  e.stopPropagation();
99
99
  onOpenEvent(event);
100
100
  }, onKeyDown: (e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "18.13.1",
3
+ "version": "18.13.3",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -61,8 +61,8 @@
61
61
  "typescript": "^6.0.0",
62
62
  "vitest": "^4.0.0",
63
63
  "zustand": "^5.0.0",
64
- "@asteby/metacore-sdk": "3.2.0",
65
- "@asteby/metacore-ui": "2.5.1"
64
+ "@asteby/metacore-ui": "2.5.1",
65
+ "@asteby/metacore-sdk": "3.2.0"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsc -p tsconfig.json",
@@ -35,6 +35,8 @@ export interface ActivityEvent {
35
35
  correlation_id?: string | null
36
36
  actor_id?: string | null
37
37
  actor_label?: string | null
38
+ /** Storage path of the actor's avatar image, when the backend resolves one. */
39
+ actor_avatar?: string | null
38
40
  addon_key: string
39
41
  model: string
40
42
  record_id: string
@@ -231,17 +233,25 @@ export const ActivityDiff: React.FC<ActivityDiffProps> = ({
231
233
  <p className="text-sm text-muted-foreground italic">{event.summary}</p>
232
234
  )}
233
235
 
234
- {/* Diff table */}
236
+ {/* Diff table. Created/deleted events carry a single snapshot — no
237
+ before/after pair — so they collapse to two columns (Campo +
238
+ Valor); a third placeholder column would force the value cell
239
+ to wrap onto its own row. Updated keeps Campo/Antes/Después. */}
235
240
  {displayedKeys.length > 0 && (
236
241
  <div className="rounded-lg border border-border/60 overflow-hidden text-sm">
237
242
  {/* Column headers */}
238
- <div className="grid grid-cols-[1fr_1fr_1fr] border-b border-border/40 bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground">
239
- <span>Campo</span>
240
- {!isCreated && <span>{isDeleted ? 'Valor' : 'Antes'}</span>}
241
- {isCreated && <span></span>}
242
- {!isDeleted && <span>{isCreated ? 'Valor' : 'Después'}</span>}
243
- {isDeleted && <span></span>}
244
- </div>
243
+ {isCreated || isDeleted ? (
244
+ <div className="grid grid-cols-[1fr_2fr] border-b border-border/40 bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground gap-x-2">
245
+ <span>Campo</span>
246
+ <span>{isDeleted ? 'Valor anterior' : 'Valor'}</span>
247
+ </div>
248
+ ) : (
249
+ <div className="grid grid-cols-[1fr_1fr_1fr] border-b border-border/40 bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground gap-x-2">
250
+ <span>Campo</span>
251
+ <span>Antes</span>
252
+ <span>Después</span>
253
+ </div>
254
+ )}
245
255
 
246
256
  {displayedKeys.map((key, idx) => {
247
257
  const col = resolveColumn(key, columns)
@@ -263,14 +273,39 @@ export const ActivityDiff: React.FC<ActivityDiffProps> = ({
263
273
  ? ROW_STYLE.created
264
274
  : isDeleted
265
275
  ? ROW_STYLE.deleted
266
- : isChanged
267
- ? {}
268
- : {}
276
+ : {}
277
+
278
+ // Single-snapshot row: label + one value cell, aligned
279
+ // with the two-column header above.
280
+ if (isCreated || isDeleted) {
281
+ return (
282
+ <div
283
+ key={key}
284
+ style={rowStyle}
285
+ className={cn(
286
+ 'grid grid-cols-[1fr_2fr] items-start px-3 py-2 gap-x-2',
287
+ idx !== displayedKeys.length - 1 && 'border-b border-border/30',
288
+ )}
289
+ >
290
+ <span className="text-xs font-medium text-foreground/70 pt-0.5 truncate" title={label}>
291
+ {label}
292
+ </span>
293
+ <span className="min-w-0">
294
+ <ActivityValueRenderer
295
+ value={isDeleted ? fromVal : toVal}
296
+ col={col}
297
+ timeZone={timeZone}
298
+ currency={currency}
299
+ locale={locale}
300
+ />
301
+ </span>
302
+ </div>
303
+ )
304
+ }
269
305
 
270
306
  return (
271
307
  <div
272
308
  key={key}
273
- style={rowStyle}
274
309
  className={cn(
275
310
  'grid grid-cols-[1fr_1fr_1fr] items-start px-3 py-2 gap-x-2',
276
311
  idx !== displayedKeys.length - 1 && 'border-b border-border/30',
@@ -282,40 +317,32 @@ export const ActivityDiff: React.FC<ActivityDiffProps> = ({
282
317
  {label}
283
318
  </span>
284
319
 
285
- {/* Before value (or value for deleted/created) */}
286
- {!isCreated ? (
287
- <span className={cn(isDeleted ? 'col-span-2' : '')}>
288
- <ActivityValueRenderer
289
- value={isDeleted ? fromVal : fromVal}
290
- col={col}
291
- timeZone={timeZone}
292
- currency={currency}
293
- locale={locale}
294
- />
295
- </span>
296
- ) : (
297
- <span />
298
- )}
320
+ {/* Before value */}
321
+ <span className="min-w-0">
322
+ <ActivityValueRenderer
323
+ value={fromVal}
324
+ col={col}
325
+ timeZone={timeZone}
326
+ currency={currency}
327
+ locale={locale}
328
+ />
329
+ </span>
299
330
 
300
331
  {/* After value */}
301
- {!isDeleted ? (
302
- <span className={cn(isCreated ? 'col-span-2' : '')}>
303
- {isChanged && variant === 'updated' && (
304
- <span className="inline-flex items-center gap-1 align-middle mr-1">
305
- <ArrowRight className="h-3 w-3 text-muted-foreground/50 shrink-0" />
306
- </span>
307
- )}
308
- <ActivityValueRenderer
309
- value={isCreated ? toVal : toVal}
310
- col={col}
311
- timeZone={timeZone}
312
- currency={currency}
313
- locale={locale}
314
- />
315
- </span>
316
- ) : (
317
- <span />
318
- )}
332
+ <span className="min-w-0">
333
+ {isChanged && variant === 'updated' && (
334
+ <span className="inline-flex items-center gap-1 align-middle mr-1">
335
+ <ArrowRight className="h-3 w-3 text-muted-foreground/50 shrink-0" />
336
+ </span>
337
+ )}
338
+ <ActivityValueRenderer
339
+ value={toVal}
340
+ col={col}
341
+ timeZone={timeZone}
342
+ currency={currency}
343
+ locale={locale}
344
+ />
345
+ </span>
319
346
  </div>
320
347
  )
321
348
  })}
@@ -18,6 +18,7 @@ import { cn } from '@asteby/metacore-ui/lib'
18
18
  import {
19
19
  Avatar,
20
20
  AvatarFallback,
21
+ AvatarImage,
21
22
  Badge,
22
23
  Collapsible,
23
24
  CollapsibleContent,
@@ -57,6 +58,17 @@ export interface RecordHistoryProps {
57
58
  * detail page (e.g. `/activity/:id`). Omitted → no button.
58
59
  */
59
60
  onOpenEvent?: (event: ActivityEvent) => void
61
+ /**
62
+ * Resolves an event's `actor_avatar` storage path to a fetchable URL
63
+ * (e.g. ops' `getStorageUrl(path, 'avatars')`). Identity when omitted —
64
+ * fine for absolute same-origin paths.
65
+ */
66
+ resolveAvatarUrl?: (path: string) => string
67
+ /**
68
+ * Localized label for the badge on each event header (e.g. "Clientes").
69
+ * Falls back to the event's raw `addon_key` when omitted.
70
+ */
71
+ moduleLabel?: string
60
72
  }
61
73
 
62
74
  // ---------------------------------------------------------------------------
@@ -106,6 +118,8 @@ export const RecordHistory: React.FC<RecordHistoryProps> = ({
106
118
  locale = 'es',
107
119
  className,
108
120
  onOpenEvent,
121
+ resolveAvatarUrl,
122
+ moduleLabel,
109
123
  }) => {
110
124
  const dateLocale = locale === 'en' ? enUS : es
111
125
 
@@ -187,8 +201,15 @@ export const RecordHistory: React.FC<RecordHistoryProps> = ({
187
201
  type="button"
188
202
  className="w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-muted/30 transition-colors"
189
203
  >
190
- {/* Actor avatar */}
204
+ {/* Actor avatar — the photo when the event carries one,
205
+ initials otherwise */}
191
206
  <Avatar className="h-7 w-7 rounded-full shrink-0">
207
+ {event.actor_avatar ? (
208
+ <AvatarImage
209
+ src={resolveAvatarUrl ? resolveAvatarUrl(event.actor_avatar) : event.actor_avatar}
210
+ alt={actor}
211
+ />
212
+ ) : null}
192
213
  <AvatarFallback className="text-[9px] font-bold bg-primary/10 text-primary">
193
214
  {getInitials(actor)}
194
215
  </AvatarFallback>
@@ -208,9 +229,9 @@ export const RecordHistory: React.FC<RecordHistoryProps> = ({
208
229
  <span className="text-xs text-muted-foreground" title={fullDate}>
209
230
  {timeAgo}
210
231
  </span>
211
- {event.addon_key && (
232
+ {(moduleLabel || event.addon_key) && (
212
233
  <Badge variant="outline" className="text-[10px] px-1.5 py-0 h-4 ml-1">
213
- {event.addon_key}
234
+ {moduleLabel || event.addon_key}
214
235
  </Badge>
215
236
  )}
216
237
  </div>