diff --git a/.changeset/activity-popover-field-mapping.md b/.changeset/activity-popover-field-mapping.md new file mode 100644 index 000000000..3f0f1c808 --- /dev/null +++ b/.changeset/activity-popover-field-mapping.md @@ -0,0 +1,16 @@ +--- +"@object-ui/app-shell": patch +--- + +fix(app-shell): map raw `sys_activity` rows before rendering the inbox Activity tab + +The top-bar inbox bell's Activity tab (`InboxPopover`) rendered blank rows — +only the relative time showed (`47m ·`), with the actor, summary, and object +name all missing. `AppHeader.fetchPresenceAndActivities` cast the raw +`sys_activity` rows straight to `ActivityItem` without renaming their fields, +so the popover read `a.user` / `a.description` / `a.objectName` while the rows +only carry plugin-audit's `actor_name` / `summary` / `object_name`. + +The rows are now mapped onto `ActivityItem` (with `type` normalization, a +`timestamp` fallback, and an empty-`summary` filter), mirroring the mapping in +`useHomeInbox` so the bell and the Home dashboard stay in sync. diff --git a/packages/app-shell/src/layout/AppHeader.tsx b/packages/app-shell/src/layout/AppHeader.tsx index eaf4c58c2..e9be8112d 100644 --- a/packages/app-shell/src/layout/AppHeader.tsx +++ b/packages/app-shell/src/layout/AppHeader.tsx @@ -274,9 +274,35 @@ export function AppHeader({ return { data: [] as Record[] }; }); if (activityResult.data?.length) { - const items = (activityResult.data as Record[]).filter( - (a): a is ActivityItem & Record => typeof a.type === 'string' - ); + // Raw sys_activity rows use plugin-audit's column names + // (summary / actor_name / object_name / timestamp). Map them onto + // ActivityItem's shape (description / user / objectName) — casting the + // raw row straight through left every field undefined, so the popover + // Activity tab rendered blank rows (only the relative time showed). + // Mirrors the mapping in `useHomeInbox` so the bell and Home never diverge. + const items: ActivityItem[] = (activityResult.data as Record[]) + .filter((r) => typeof r.type === 'string' && String(r.summary ?? '').trim()) + .map((r) => { + let when = r.timestamp as string | undefined; + if (!when || when === 'NOW()' || Number.isNaN(Date.parse(when))) { + when = r.created_at as string | undefined; + } + const raw = String(r.type); + const type: ActivityItem['type'] = + raw === 'commented' || raw === 'mentioned' ? 'comment' + : raw === 'deleted' ? 'delete' + : raw === 'created' ? 'create' + : 'update'; + return { + id: String(r.id), + type, + objectName: (r.object_name as string) ?? '', + recordId: (r.record_id as string) ?? undefined, + user: (r.actor_name as string) ?? '', + description: (r.summary as string) ?? '', + timestamp: when ?? '', + }; + }); if (items.length) setApiActivities(items); } } catch { /* fallback below */ } finally {