@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 +12 -0
- package/dist/activity-diff.d.ts +2 -0
- package/dist/activity-diff.d.ts.map +1 -1
- package/dist/activity-diff.js +8 -5
- package/dist/record-history.d.ts +11 -0
- package/dist/record-history.d.ts.map +1 -1
- package/dist/record-history.js +3 -3
- package/package.json +3 -3
- package/src/activity-diff.tsx +71 -44
- package/src/record-history.tsx +24 -3
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
|
package/dist/activity-diff.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/activity-diff.js
CHANGED
|
@@ -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-[
|
|
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
|
-
:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
};
|
package/dist/record-history.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/record-history.js
CHANGED
|
@@ -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: [
|
|
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.
|
|
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-
|
|
65
|
-
"@asteby/metacore-
|
|
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",
|
package/src/activity-diff.tsx
CHANGED
|
@@ -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
|
-
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
:
|
|
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
|
|
286
|
-
|
|
287
|
-
<
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
<
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
})}
|
package/src/record-history.tsx
CHANGED
|
@@ -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>
|