Skip to content

ENG-3100: Seed Data developer UI, DashboardSnapshot model, and dashboard polish#7737

Open
galvana wants to merge 12 commits intofeat/pbac-ui-managementfrom
feat/seed-data-ui
Open

ENG-3100: Seed Data developer UI, DashboardSnapshot model, and dashboard polish#7737
galvana wants to merge 12 commits intofeat/pbac-ui-managementfrom
feat/seed-data-ui

Conversation

@galvana
Copy link
Contributor

@galvana galvana commented Mar 23, 2026

Ticket ENG-3100

Description Of Changes

Add the Seed Data developer page, DashboardSnapshot model for trend sparklines, is_overdue privacy request filter, and dashboard UI polish.

Seed Data page:

  • New developer page at /poc/seed-data with scenario-based seeding UI
  • RTK Query slice with trigger, status polling, and cache invalidation per task
  • "Dashboard (Landing Page)" scenario that populates all dashboard cards

DashboardSnapshot model:

  • New model for daily metric snapshots powering trend sparkline history
  • Alembic migration creating dashboard_snapshot table with date+metric unique constraint

Privacy request is_overdue filter:

  • New is_overdue boolean field on PrivacyRequestFilter schema
  • Query utils filter: due_date < now() AND status IN active_statuses
  • ACTIVE_REQUEST_STATUSES frozenset defined centrally in privacy request schemas
  • Frontend: nuqs URL param support in privacy request filter hook

Dashboard UI polish:

  • DSR status card links filter request manager by action_type and is_overdue via URL params
  • Sentence case titles (DSR status, System coverage, SLA health)
  • Dynamic YAxis width in StackedBarChart based on longest label
  • "X days overdue" badge for negative days instead of "-X days left"
  • Equal height System coverage and DSR status cards
  • Rename PostureCard title to "Governance posture", TrendCard to "Governance Posture"

Code Changes

Backend:

  • src/fides/api/models/dashboard_snapshot.py — New DashboardSnapshot model with snapshot_date, metric_key, value columns
  • src/fides/api/alembic/migrations/versions/xx_2026_03_24_*_add_dashboard_snapshot.py — Migration creating dashboard_snapshot table
  • src/fides/api/schemas/privacy_request.py — Add ACTIVE_REQUEST_STATUSES frozenset, add is_overdue to PrivacyRequestFilter
  • src/fides/service/privacy_request/privacy_request_query_utils.py — Add is_overdue filter logic
  • src/fides/api/db/base.py — Import DashboardSnapshot for Alembic discovery

Frontend:

  • clients/admin-ui/src/features/seed-data/seed-data.slice.ts — RTK Query slice with trigger/status endpoints and cache invalidation tags
  • clients/admin-ui/src/features/seed-data/SeedDataPanel.tsx — Seed scenario UI with Dashboard scenario
  • clients/admin-ui/src/features/privacy-requests/dashboard/hooks/usePrivacyRequestsFilters.ts — Add is_overdue URL param via nuqs
  • clients/admin-ui/src/features/privacy-requests/privacy-requests.slice.ts — Add is_overdue to state and setRequestOverdue action
  • clients/admin-ui/src/features/privacy-requests/dashboard/list-item/components/DaysLeft.tsx — Show "X days overdue" for negative values
  • clients/admin-ui/src/features/dashboard/constants.ts — ACTION_CTA supports is_overdue routing
  • clients/admin-ui/src/home/DSRStatusCard.tsx — Links filter by action_type and is_overdue via URL params
  • clients/admin-ui/src/home/HomeDashboard.tsx — Equal height cards via flex container
  • clients/admin-ui/src/home/SystemCoverageCard.tsx — Sentence case title
  • clients/admin-ui/src/home/PostureCard.tsx — Rename to "Governance posture"
  • clients/admin-ui/src/home/TrendCard.tsx — Rename to "Governance Posture"
  • clients/fidesui/src/components/charts/StackedBarChart.tsx — Dynamic YAxis width, alphabetical sort

Tests:

  • tests/unit/test_active_request_statuses.py — Tests for ACTIVE_REQUEST_STATUSES membership and PrivacyRequestFilter is_overdue field

Steps to Confirm

  1. Start dev environment, navigate to /poc/seed-data
  2. Check "Dashboard" scenario, click Seed, wait for completion
  3. Navigate to Home, verify all cards render with data
  4. Click "Erasure" on the SLA bar, confirm request manager filters to erasure requests
  5. Click "4 overdue" link, confirm request manager shows only overdue active requests
  6. Verify overdue requests show "X days overdue" badges (not "-X days left")
  7. Verify card titles are sentence case

Pre-Merge Checklist

  • All CI Pipelines Succeeded
  • Issue Requirements are Met
  • Optional: Follow-Up Issues Created
  • Update CHANGELOG.md
    • Add a high-risk label to the entry if your change includes a high-risk change
  • Documentation:
    • documentation complete, PR opened in fidesdocs
    • documentation issue created in fidesdocs
    • if there are any new client scopes created as part of the pull request, remember to update public-facing documentation that references our scope registry

🤖 Generated with Claude Code

Add a Seed Data page under the Developer nav (dev-only) that lets
users select and trigger seed scenarios via the seed API. Includes
RTK Query slice with status polling and cache tag invalidation
mapped per seed task. Currently supports the PBAC scenario.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@galvana galvana requested a review from a team as a code owner March 23, 2026 23:14
@galvana galvana requested review from lucanovera and removed request for a team March 23, 2026 23:14
@vercel
Copy link
Contributor

vercel bot commented Mar 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fides-plus-nightly Error Error Mar 25, 2026 5:00pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
fides-privacy-center Ignored Ignored Mar 25, 2026 5:00pm

Request Review

@galvana galvana changed the title feat: Seed Data developer UI page (ENG-3100) ENG-3100 Seed Data developer UI page Mar 23, 2026
@galvana galvana changed the title ENG-3100 Seed Data developer UI page ENG-3100: Seed Data developer UI page Mar 23, 2026
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 23, 2026

Greptile Summary

This PR adds a developer-only "Seed Data" page under the Developer nav section, allowing developers to trigger predefined seed scenarios (currently PBAC) via a UI backed by RTK Query. The implementation is well-scoped — gated behind NEXT_PUBLIC_APP_ENV === "development" and requiresPlus, with a clean RTK slice that handles cache invalidation per task and a simple polling mechanism for status updates.

The one concrete bug to fix before merging is in SeedDataPanel.tsx: when the triggerSeed mutation itself fails (e.g. a 500 or network error), the catch block silently swallows the error and never sets executionId, meaning polling never starts and the user receives zero feedback about the failure. The comment "Error is shown via the status polling" only holds once an execution ID has been returned — it does not cover this case. A quick inline Alert or toast in the catch path would address it.

Additional minor items:

  • The variable s in computeProgress should be renamed to step (naming convention).
  • The <div className="mt-4"> wrapping the status section could be replaced with a Flex/Space component per the project's semantic HTML guideline.
  • The pollingInterval: 2000 development value should either have a clarifying comment that it is intentional for this dev-only context, or be updated, before merging per the polling interval policy.

Confidence Score: 4/5

  • Safe to merge after addressing the silent error swallowing in the seed trigger failure path.
  • The overall structure is solid — correct gating, clean RTK slice, proper cache invalidation, and sensible polling flow. One P1 logic bug (silent catch on trigger failure) needs a targeted fix; everything else is style/convention cleanup that could be done as a follow-up.
  • clients/admin-ui/src/features/seed-data/SeedDataPanel.tsx — specifically the error handling in handleSeed.

Important Files Changed

Filename Overview
clients/admin-ui/src/features/seed-data/SeedDataPanel.tsx Main UI component for the Seed Data page; has a P1 bug where trigger-level errors are silently swallowed (no user feedback when the POST fails), plus minor style violations (single-char variable, bare div wrapper, development polling interval).
clients/admin-ui/src/features/seed-data/seed-data.slice.ts RTK Query slice for seed endpoints; well-structured with correct invalidatesTags logic, deduplication, and per-task cache tag mapping. No issues found.
clients/admin-ui/src/pages/poc/seed-data.tsx Simple Next.js page wrapper for the Seed Data panel; no issues found.
clients/admin-ui/src/features/common/nav/nav-config.tsx Adds the Seed Data nav entry inside the development-only block, correctly gated behind requiresPlus and DEVELOPER_READ scope. No issues.
clients/admin-ui/src/features/common/nav/routes.ts Adds SEED_DATA_ROUTE constant — straightforward, no issues.

Reviews (1): Last reviewed commit: "feat: add Seed Data developer page (ENG-..." | Re-trigger Greptile

Comment on lines +109 to +114
try {
const result = await triggerSeed({ tasks }).unwrap();
setExecutionId(result.execution_id);
} catch {
// Error is shown via the status polling
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Silent error swallowing leaves no user feedback on trigger failure

When triggerSeed(...).unwrap() throws (e.g. 500, network error, 401), setExecutionId is never called, so polling never starts and statusData remains undefined. The comment "Error is shown via the status polling" only holds once an executionId has been set — it does not apply to this failure path. The user will see the button return to idle with no indication that anything went wrong.

Consider showing an error toast or inline Alert in the catch block:

Suggested change
try {
const result = await triggerSeed({ tasks }).unwrap();
setExecutionId(result.execution_id);
} catch {
// Error is shown via the status polling
}
try {
const result = await triggerSeed({ tasks }).unwrap();
setExecutionId(result.execution_id);
} catch (error) {
// triggerSeed failed before an execution was created; surface the error directly
const errorMessage =
(error as { data?: { detail?: string } })?.data?.detail ??
"Failed to start seed. Please try again.";
// Replace with your preferred error notification mechanism
console.error("Seed trigger failed:", errorMessage);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in b471019. Used RTK Query's isError state from the mutation hook to show an inline Alert when the trigger fails, rather than adding manual error state. This surfaces the failure to the user without needing an execution ID.

Comment on lines +76 to +79
const { data: statusData } = useGetSeedStatusQuery(executionId!, {
skip: !executionId,
pollingInterval: 2000,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Development polling interval should be updated before merging

pollingInterval: 2000 (2 s) is a development-only value. Per the project's polling interval guidance, development values should be reviewed and updated before merging — even for dev-mode-only pages — to ensure intentional configuration. If a 2-second interval is the deliberate choice for this lightweight tool, add an explanatory comment so reviewers know it was considered.

Suggested change
const { data: statusData } = useGetSeedStatusQuery(executionId!, {
skip: !executionId,
pollingInterval: 2000,
});
const { data: statusData } = useGetSeedStatusQuery(executionId!, {
skip: !executionId,
// Seed tasks complete within seconds; 2 s polling is intentional for this dev-only tool.
pollingInterval: 2000,
});

Rule Used: Polling intervals for async operations should be s... (source)

Learnt From
ethyca/fides#6566

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in b471019. Added a clarifying comment that the 2s interval is intentional for this dev-only tool.

Comment on lines +61 to +64
const done = entries.filter(
(s) =>
s.status === "complete" || s.status === "skipped" || s.status === "error",
).length;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Single-character variable name

The parameter s should use a full descriptive name per the project's variable naming convention.

Suggested change
const done = entries.filter(
(s) =>
s.status === "complete" || s.status === "skipped" || s.status === "error",
).length;
const done = entries.filter(
(step) =>
step.status === "complete" || step.status === "skipped" || step.status === "error",
).length;

Rule Used: Use full names for variables, not 1 to 2 character... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in b471019. Renamed s to step.

Comment on lines +153 to +154
{showStatus ? (
<div className="mt-4">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Prefer component-library alternatives over bare div

The <div className="mt-4"> wrapper is not purely a padding container — it groups the Progress bar, step list, and Alerts. Consider replacing it with a <Flex vertical gap="small"> or a <Space> component with a top margin, consistent with how the rest of the component uses Space and Flex for layout.

Rule Used: Avoid using div elements when possible. Use sema... (source)

Learnt From
ethyca/fides#6763

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in b471019. Replaced the bare div with <Flex vertical gap="small"> to stay consistent with the rest of the component.

- New DashboardSnapshot model with snapshot_date, metric_key, value columns
- Unique constraint on (snapshot_date, metric_key) for daily snapshots
- Alembic migration to create dashboardsnapshot table
- Rename PostureCard title to "Governance posture"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@galvana galvana requested a review from a team as a code owner March 24, 2026 23:18
@galvana galvana requested review from vcruces and removed request for a team March 24, 2026 23:18
- Add dashboard task to SeedTasksConfig interface
- Add cache invalidation tags for dashboard seed task
- Add "Dashboard (Landing Page)" scenario to seed data UI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adrian Galvan and others added 2 commits March 24, 2026 16:35
- Set explicit __tablename__ = "dashboard_snapshot" on model
- Update migration table/index names to use dashboard_snapshot
- Fix down_revision to chain after 190e4603ad38 (resolves multiple heads)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@galvana galvana changed the title ENG-3100: Seed Data developer UI page ENG-3100: Seed Data developer UI, DashboardSnapshot model, and dashboard polish Mar 25, 2026
Backend:
- Add ACTIVE_REQUEST_STATUSES central definition to privacy_request schemas
- Add is_overdue filter to PrivacyRequestFilter and query utils
  (filters to active statuses with due_date in the past)

Frontend:
- DSR status and briefing links filter by action_type and is_overdue
  via URL params (nuqs) instead of Redux dispatch
- Sentence case card titles (DSR status, System coverage, SLA health)
- Dynamic YAxis width in StackedBarChart based on longest label
- Sort SLA bars alphabetically (Access before Erasure)
- "X days overdue" badge for negative days instead of "-X days left"
- Equal height System coverage and DSR status cards
- ACTION_CTA supports is_overdue routing for DSR actions

Tests:
- ACTIVE_REQUEST_STATUSES membership and exclusion tests
- PrivacyRequestFilter is_overdue field acceptance tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adrian Galvan and others added 2 commits March 25, 2026 09:23
- Surface trigger errors via RTK Query isError state and inline Alert
- Add clarifying comment for 2s polling interval (dev-only tool)
- Rename single-char variable `s` to `step` in computeProgress
- Replace bare div with Flex component for semantic layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add type: ignore for __tablename__ override in DashboardSnapshot
- Use `as const` for STATUS_COLORS to satisfy Tag color prop types
- Cast invalidatesTags return to satisfy RTK Query TagDescription type
- Add is_overdue to PrivacyRequestParams interface
- Add typeof guard before `in` operator for action_data.status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
galvana and others added 2 commits March 25, 2026 09:32
Add tests/unit/ to TEST_DIRECTORY_COVERAGE and pytest_misc_unit so
the collect_tests check recognizes the new directory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant