Skip to content

feat(ui): display names in activities + refactor feeds to tables/timelines on datum-ui#192

Closed
mattdjenkinson wants to merge 3 commits into
mainfrom
feat/activity-display-names
Closed

feat(ui): display names in activities + refactor feeds to tables/timelines on datum-ui#192
mattdjenkinson wants to merge 3 commits into
mainfrom
feat/activity-display-names

Conversation

@mattdjenkinson
Copy link
Copy Markdown
Contributor

@mattdjenkinson mattdjenkinson commented May 1, 2026

Summary

Two large pieces of work landed on this branch:

  1. Backend display-name enrichmentActivityActor and ActivityLink carry a server-resolved DisplayName (and Email for links) so the UI can render human-readable user names instead of raw emails / UIDs.
  2. UI refactor onto datum-ui — the activity, events, and audit-log feeds are now table/timeline-shaped, route every primitive through @datum-cloud/datum-ui, and share a single set of detail-panel primitives.

Backend changes (Go)

  • pkg/apis/activity/v1alpha1/types_activity.go — added ActivityActor.DisplayName and ActivityLink.DisplayName / ActivityLink.Email (all omitempty). Marker fields preserved for backwards compatibility.
  • internal/processor/userlookup.go (new)UserResolver interface + NoopUserResolver + CachedUserResolver with TTL cache, single-flight collapse, and negative caching for misses.
  • internal/processor/classifier.goResolveActorWithResolver(ctx, user, resolver) populates DisplayName from the iam User's givenName + familyName. Resolver errors are silently ignored; the activity is still emitted with whatever data is available.
  • internal/processor/activity.goActivityBuilder gains a UserResolver. New enrichSummaryWithDisplayNames rewrites the summary to substitute actor.Name (typically email) with actor.DisplayName, upgrades existing actor link() entries in place (or appends a synthetic actor link for templates that didn't wrap the actor), and hydrates User-typed link targets via the resolver.
  • internal/processor/evaluate.go + internal/activityprocessor/policycache.go — added *WithResolver overloads alongside the existing functions so callers can opt into enrichment without changing the public API.
  • internal/activityprocessor/userresolver.go (new)IAMUserResolver queries iam.miloapis.com/v1alpha1/User resources via a controller-runtime client, exposed as Unstructured so we don't pull in milo's Go types as a dependency.
  • internal/activityprocessor/processor.go — wires NewCachedUserResolver(NewIAMUserResolver(k8sClient), 0, 0) into the Processor at startup.
  • Testsinternal/processor/userlookup_test.go (cache hit, neg-cache, TTL expiry, single-flight, error-not-cached, empty-key short-circuit) and internal/processor/enrichment_test.go (actor replacement, link upgrade in place, actor-not-in-summary, user link hydration, non-user link untouched, ResolveActorWithResolver paths). Full Go test suite green; go vet + go build clean.

UI refactor (ui/)

Datum-ui adoption + primitive sweep

  • Added @datum-cloud/datum-ui ^0.8.0 as a peer dep (and devDep so the rollup build resolves locally). Externalised @datum-cloud/datum-ui/* in rollup.config.mjs — consumers bring their own pinned copy.
  • Bulk-migrated 11 drop-in primitives across all 42 consumer files to direct imports (alert, card, checkbox, input, label, select, separator, sheet, skeleton, tabs, textarea).
  • Adapter shims kept in src/components/ui/ for the four datum-ui APIs that don't match the legacy shadcn shape:
    • badgevariant=…type=… + theme=….
    • dialog — flat Dialog{Header,Title,…} over datum-ui's compound Dialog.Header/.Title/….
    • tooltip — re-built directly on @radix-ui/react-tooltip (datum-ui's <Tooltip message=…> wraps the trigger in relative inline-flex which broke truncation downstream); styling mirrors datum-ui's secondary popover with arrow.
  • Button shim deleted entirely. All ~60 <Button variant=… size=…> call sites rewritten to use datum-ui's type=… theme=… size=… props (Python script that walked each opening tag); native HTML type="button|submit|reset" renamed to htmlType.
  • Hardcoded brand-pink CTAs (#BF9595/#0C1D31/#A88080/#E6F59F) across policy + reindex views replaced by theme tokens (bg-primary, text-primary-foreground, etc.).
  • Dropped the local rollup PostCSS / Tailwind pipeline (no CSS shipped — host app's Tailwind compiles utility classes from dist/). Removed tailwindcss, @tailwindcss/postcss, autoprefixer, rollup-plugin-postcss, tailwindcss-animate from deps.
  • Removed unused VerbToggle.tsx and EventFeedItemSkeleton.tsx.

Feed refactor (table + timeline variants)

  • ActivityFeed — Card-row list → <Table> with columns Actor (avatar w/ tooltip) | Summary (truncated, full on hover) | Tenant (TenantBadge) | When (<Timestamp>) | expand. Row click toggles expansion; expanded panel spans all columns. Manual "Load more" pagination footer (count + button); IntersectionObserver path retained behind infiniteScroll={true}.
  • AuditLogQueryComponent + AuditLogFeedItem — same treatment with columns Verb (badge) | Summary | Status (✓/✗ + code) | When | expand.
  • EventsFeed + EventFeedItem — Type (Normal/Warning) | Reason | Note | Object | When | expand.
  • All three feeds also accept variant: 'feed' | 'timeline'. The 'timeline' variant renders a flat icon-list (action-keyed icon square + summary + status/object + timestamp + expand) matching the existing ActivityFeedItem timeline shape so all three activity-hub tabs can present consistently.

Timestamp component

  • New <Timestamp> (uses Intl.DateTimeFormat — no extra deps). Three visible variants: relative (default), utc, iso-utc, local. Hover always reveals a four-row tooltip (UTC / browser-tz / Relative / microsecond timestamp), matching staff-portal's <DateTime variant="detailed"> pattern. Used everywhere a time is shown — feed rows, expanded panels, timeline variants.

Detail panels

  • New shared primitives in src/components/details.tsx: Section, Field, CopyButton, DetailGrid, DetailPanelShell. Each Field renders label-above-value; values truncate with a hover tooltip (disableTooltip opt-out for cells whose value already provides its own, e.g. <Timestamp>); copy button fades in on row hover.
  • Activity / AuditLog / Event expanded panels rebuilt onto these primitives. Sections grouped by responsibility (e.g. When / Actor / Resource / Origin for activity).

Pause/Resume + pagination correctness

  • Added userPaused to useActivityFeed and useEventsFeed. The auto-restart effects that previously re-opened the watch immediately after stopStreaming now skip while userPaused is true. Clicking Pause stays paused; tab switching while paused fetches but doesn't silently resume the stream.
  • Manual pagination is the new default in staff-portal's feed.tsx. The IntersectionObserver dependency cycle (re-creating on every isLoading toggle, refiring loadMore() until items filled the viewport) is fixed by mirroring hasMore / isLoading into refs.

TenantBadge / toolbars

  • ChangeSourceToggle / EventTypeToggle / ActionToggle now use datum-ui's type=primary theme=solid for the active state (so Button picks up the matching foreground colour) and force borderRadius: 0 + border: 0 inline on the active button so the segmented control fills its wrapper edge-to-edge with no internal seam.
  • All three filter toolbars share the same wrapper (border-b border-border py-4); search inputs match (Search icon + Input + clear-X button).
  • Streaming header (Pause/Resume) standardised across activity + events: same outlined Button, same icon sizes, same tooltipped streaming-dot.

Notable verification

  • pnpm run type-check
  • pnpm run build (rollup) ✓ (no warnings)
  • Backend: go build ./..., go vet ./..., go test ./... — all green; new tests cover cache + enrichment paths.
Screenshot 2026-05-01 at 13 55 32 Screenshot 2026-05-01 at 13 55 40 Screenshot 2026-05-01 at 13 55 54 Screenshot 2026-05-01 at 13 56 07 Screenshot 2026-05-01 at 14 09 47

Switch the activity, events, and audit-log feeds from card-row
lists to data-table layouts. Add a `timeline` variant on each so
all three views can also render as a flat icon-list when the host
prefers a denser presentation.

Key changes:
- Activity / Events / Audit-log feeds render via datum-ui Table
  with consistent columns, expand-row details, and a manual
  "Load more" pagination footer (no infinite-scroll observer
  loops).
- New `Timestamp` component shows UTC / browser-local / Relative
  / microsecond timestamp on hover; used everywhere a time is
  rendered.
- New shared `details` primitives (Section, Field, CopyButton,
  DetailGrid, DetailPanelShell) standardise the look of every
  expanded detail panel; values truncate with hover tooltips and
  copy buttons that fade in.
- Drop-in primitives (alert, card, input, label, separator,
  skeleton, tabs, textarea, checkbox, sheet, select, button)
  are imported directly from @datum-cloud/datum-ui across the
  codebase; the local src/components/ui shadcn copies are gone.
- Tooltip shim is now a thin wrapper around Radix directly so
  truncation downstream isn't broken by datum-ui's inline-flex
  trigger wrapper; styling mirrors datum-ui's bg-secondary
  popover with arrow.
- Pause/Resume on the activity + events feeds now respects user
  intent (auto-restart effects skip while userPaused is set).
- Pagination Intersection Observer no longer rebuilds on
  isLoading toggles; manual pagination is the default.
- ChangeSourceToggle / EventTypeToggle / ActionToggle use
  datum-ui's primary/quaternary variants and force borderRadius:0
  inline so the segmented control fills its wrapper edge-to-edge.
- Hardcoded brand-pink CTAs (#BF9595) across policy / reindex
  views replaced by bg-primary / theme tokens.
- Backend changes (DisplayName/Email on Actor/Link, iam User
  resolver, summary enrichment, CEL paths) included from the
  earlier display-name PR and tested locally.

Drops the rollup PostCSS/Tailwind pipeline (no CSS shipped),
removes VerbToggle and EventFeedItemSkeleton as unused, and
deletes src/styles.css + tailwind.config.js + postcss.config.js.
The library no longer ships its own tailwind/postcss pipeline (host
app provides Tailwind via @source). The example app's configs
remain in place at example/postcss.config.js and
example/tailwind.config.js for the Remix demo.
@scotwells
Copy link
Copy Markdown
Contributor

@mattdjenkinson thoughts on separating the user enrichment stuff into a separate PR? I have some concerns around the pressure this could put on the control plane that I need to think through and don't want to hold up the UI improvements.

@mattdjenkinson
Copy link
Copy Markdown
Contributor Author

@scotwells yeah i probs should have done that to begin with. Will sort.

@mattdjenkinson
Copy link
Copy Markdown
Contributor Author

Splitting this into two focused PRs for easier review:

The UI PR can merge independently — the new displayName/email fields are optional and will simply stay undefined until the backend PR lands.

mattdjenkinson added a commit that referenced this pull request May 1, 2026
## Summary

Rebuilds the activity UI to consume `@datum-cloud/datum-ui` as a peer
dependency and converts the activity feed, events, and audit views to
table + timeline layouts with manual pagination. This is the **UI half**
of the work previously bundled in #192; the matching backend
display-name enrichment ships in #193.

## What changes

**Design system migration**
- Promote `@datum-cloud/datum-ui` to a peer dependency
- Replace local shadcn primitives with datum-ui exports where possible
- Drop Tailwind/PostCSS configs and unused local components
- Externalize peer deps in `rollup.config.mjs`
- Update `Dockerfile` to drop postcss/tailwind copies

**Layout refactor**
- Convert `ActivityFeed`, `EventsFeed`, `AuditLogQueryComponent` to
table/timeline views
- Manual "Load more" pagination (replaces auto-scroll observer that was
misfiring on tab switches)
- Add `Timestamp` component using `Intl.DateTimeFormat` for
timezone-aware rendering with a single tooltip
- Add reusable details-panel primitives in `components/details.tsx`;
apply across activity/audit/event expanded views
- Truncation with tooltips on summary/notes columns
- Tooltip shim renders Radix Tooltip directly (mirroring datum-ui
styles) so flex truncation actually engages
- Normalize toolbar styling, filter chips, and details-panel padding
across feeds
- Remove "unsaved changes" badge

**Display name support**
- Mirror `displayName` / `email` (both optional) on `Actor` and
`ActivityLink` TS types
- Render `actor.displayName ?? actor.name` with email/UID hover;
user-typed link markers swap in `displayName` as visible text
- Falls back cleanly when backend hasn't populated the new fields, so
this PR can land independently of #193

## Tooltip / truncation notes

datum-ui's Tooltip wraps the trigger in a `relative inline-flex` span
which broke parent flex truncation. To keep datum-ui's visual styling,
the local Tooltip in `ui/tooltip.tsx` re-implements Radix Tooltip
directly with the same class tokens — same look, no flex-chain breakage.

<img width="1502" height="829" alt="Screenshot 2026-05-01 at 16 27 29"
src="https://github.com/user-attachments/assets/57a564c5-dfd3-4d8c-91f8-3e5bc46367ba"
/>
<img width="1624" height="1061" alt="Screenshot 2026-05-01 at 13 55 32"
src="https://github.com/user-attachments/assets/716e691e-3b80-4928-a139-54aafd2839fe"
/>
<img width="1624" height="1061" alt="Screenshot 2026-05-01 at 13 55 40"
src="https://github.com/user-attachments/assets/53e7ff60-8132-4645-bb13-d4d989190e4f"
/>
<img width="1624" height="1061" alt="Screenshot 2026-05-01 at 13 55 54"
src="https://github.com/user-attachments/assets/9c013a99-3e0a-4120-aa14-b69126510632"
/>
<img width="1624" height="1061" alt="Screenshot 2026-05-01 at 13 56 07"
src="https://github.com/user-attachments/assets/38bed23d-40a1-4a05-949d-d9871cc39c1f"
/>
<img width="1624" height="1061" alt="Screenshot 2026-05-01 at 14 09 47"
src="https://github.com/user-attachments/assets/a8f94d88-5038-4cfe-83b9-19ed75a14ab7"
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants