You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(admin-ui): surface anchor-coverage warnings and unify schema labels
Hoist the anchor-coverage query to PolicyEditPage so the side-nav and panel
share one cache; add a red count pill on the `Anchor coverage` section, a
top-of-page banner on other sections, and redesign the panel into per-table
cards. Introduce `effectiveSchemaName` as the single schema-label rule across
RelationshipsPanel, PolicyAnchorCoveragePanel, and the `?focus=` deep-link
matcher, rendering the upstream name in muted parens where helpful. Smooth
the anchor creation flow from the warning deep-link with auto-selected
viable relationships and inline empty-state guidance (switch to alias mode
or add a relationship without leaving the form). Extend the proxy's
AnchorCoverageTableEntry with `schema_upstream` to support alias-aware
labels.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+11Lines changed: 11 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Changed
11
+
12
+
-**[Admin UI] Anchor-coverage warning is harder to miss and easier to act on** — `PolicyEditPage` now hoists the `useQuery(['policy-anchor-coverage'])` to the page level so the side-nav and the panel share one cache entry. The `Anchor coverage` section in the secondary nav now carries a red count pill (`SectionDef.indicator`) when the policy will silently deny on any table; a top-of-page red banner ("This row filter will silently deny on N tables") with a `Review` button appears on every section except the coverage section itself, and inherits the active section's max-width so it lines up with the form/content below. The coverage panel itself was redesigned: each broken table is its own card with bold header (data source + `schema.table`), per-column rows with a clearer reason line, and a tertiary `Add anchor →` button instead of a plain underline. The outer wrapper switched from red wash to a neutral white card so the per-table red still reads as the alarm without desensitizing.
13
+
-**[Admin UI] Consistent schema-alias labeling across relationship/anchor surfaces** — new `effectiveSchemaName(schema_name, schema_alias)` helper in `src/utils/schemaLabel.ts` is now the single rule used by `RelationshipsPanel`, `PolicyAnchorCoveragePanel`, and the `?focus=` deep-link matcher: display the alias if set, raw upstream name otherwise. Dropdowns and selection contexts also render the upstream name in muted parens (`pg.orders (postgres.orders)`) so admins can verify the mapping without bouncing to the discovery wizard. The anchor table's relationship cell now shows the full `child_schema.child_table.child_col → parent_schema.parent_table.parent_col` join path (the child side was previously missing), and the column header was renamed from `Via relationship` to `Resolves via` to honestly cover both the FK-walk and same-table-alias modes.
14
+
-**[Admin UI] Anchor creation flow on deep-link from the policy warning** — when arriving via the `Add anchor →` link, the form now auto-selects the relationship if exactly one candidate's parent table contains the resolved column. Multiple viable candidates are sorted first in the dropdown and prefixed with `✓`. When the prefilled child table has zero relationships, the form replaces the previous one-line amber sentence with an empty-state guidance block: a "Switch to Same-table alias" button + an inline `FkSuggestionsList` filtered to the child table + a compact `InlineManualRelationshipForm` (parent table + columns) that auto-injects the new relationship into the anchor on success. The user no longer has to leave the anchor flow to fix the missing prerequisite.
15
+
16
+
### Added
17
+
18
+
-**[Proxy]`schema_upstream` field on the anchor-coverage response** — `AnchorCoverageTableEntry` now carries both `schema` (effective name, alias if set) and `schema_upstream` (raw upstream name). The admin UI uses this to render `pg.payments (postgres.payments)` in the warning panel when an alias is in play, while continuing to deep-link by the effective name (which is what the proxy keys columns by at query time).
19
+
-**[Admin UI]`SectionDef.indicator` slot on `SecondaryNav`** — sections can now carry an optional `{ tone: 'red' | 'yellow'; label: string; ariaLabel?: string }` indicator that renders as a small count pill on the right of the nav button. Currently consumed by `PolicyEditPage` for anchor coverage; the slot is generic so future sections can adopt it without further changes to the component.
Copy file name to clipboardExpand all lines: admin-ui/CLAUDE.md
+4-3Lines changed: 4 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,8 +17,9 @@ Before building a new page or extending an existing one, read `admin-ui/DESIGN.m
17
17
-`src/components/RoleMemberPanel.tsx` — Effective member list (direct + inherited with source badges), add/remove for direct members
18
18
-`src/components/RoleInheritancePanel.tsx` — Parent/child role management with cycle detection feedback
19
19
-`src/components/RoleAccessPanel.tsx` — Checkbox-based role access panel for datasource edit page
20
-
-`src/components/PolicyAnchorCoveragePanel.tsx` — Edit-time silent-deny warning on `PolicyEditPage` for `row_filter` policies. Calls `GET /policies/{id}/anchor-coverage` (keyed on `(policyId, version)` so saves auto-refetch via the existing `['policy', policyId]` invalidation); hidden for non-row-filter types. Renders a green banner when every (assigned table × referenced column) resolves cleanly, or a red panel listing each broken `(table, column)` pair with a link to the datasource page where the missing anchor can be added. Rendered inside the `Anchor coverage` section of the policy edit page's T1 sidebar (omitted from the nav for non-row-filter policies).
21
-
-`src/components/RelationshipsPanel.tsx` — Datasource edit page panel for admin-curated `table_relationship` + `column_anchor` CRUD. Surfaces live FK candidates from `GET /datasources/{id}/fk-suggestions` (already-added tuples greyed out) and the resolved-column → anchor designations that drive transitive row-filter resolution. The anchor form's "Resolve via" radio picks between **Relationship (FK walk)** — rendered via a dropdown of relationships scoped to the selected child table — and **Same-table alias** — rendered as a text input with a `<datalist>` populated from the child table's discovered columns. Long child→parent labels stack on two lines so Delete buttons stay reachable inside the narrow `max-w-2xl` page container. See `docs/security-vectors.md` vector 73 for the trust model.
20
+
-`src/components/PolicyAnchorCoveragePanel.tsx` — Pure display component for the edit-time silent-deny warning on `PolicyEditPage`. Receives `data: PolicyAnchorCoverageResponse | undefined` as a prop (the query is hoisted to `PolicyEditPage` so the side-nav indicator and the panel share one cache entry). Renders a green banner when every (assigned table × referenced column) resolves cleanly, or a red panel listing each broken `(table, column)` pair grouped by `(data_source_name, schema.table)` as a card row with an `Add anchor →` button link to the datasource page. When the schema has an alias, shows the upstream raw name in muted parens next to the table label. Rendered inside the `Anchor coverage` section of the policy edit page's T1 sidebar (omitted from the nav for non-row-filter policies).
21
+
- `src/components/RelationshipsPanel.tsx` — Datasource edit page panel for admin-curated `table_relationship` + `column_anchor` CRUD. Surfaces live FK candidates from `GET /datasources/{id}/fk-suggestions` (already-added tuples greyed out) and the resolved-column → anchor designations that drive transitive row-filter resolution. The anchor table's `Resolves via` column shows the full `child_schema.child_table.child_col → parent_schema.parent_table.parent_col` join path for relationship-mode anchors and the `alias → .col` shape for same-table-alias mode. The anchor form's "Resolve via" radio picks between **Relationship (FK walk)** — rendered via a dropdown of relationships scoped to the selected child table, with viable candidates (parent table contains the resolved column) sorted first and prefixed `✓`; auto-selected when there is exactly one viable candidate — and **Same-table alias** — rendered as a text input with a `<datalist>` populated from the child table's discovered columns. When the prefilled child table has zero relationships, the form shows an empty-state guidance block with two CTAs (Switch to Same-table alias / Add a relationship ↓); the latter expands an inline `FkSuggestionsList` filtered to the child table plus an `InlineManualRelationshipForm` mini-form, both of which auto-populate the anchor's `relationshipId` on success. Long child→parent labels stack on two lines so Delete buttons stay reachable inside the narrow `max-w-2xl` page container. See `docs/security-vectors.md` vector 73 for the trust model.
22
+
-`src/utils/schemaLabel.ts` — `effectiveSchemaName(schema_name, schema_alias)` helper. **Convention: every admin-ui surface that displays a schema name uses the effective name (alias if set, else raw upstream).** This matches what policies and queries operate on (the proxy keys columns by the effective/df name in `resolution/graph.rs`) and what the policy target dropdowns already do via `useCatalogHints`. When a place needs to surface the raw upstream alongside (selection contexts like dropdowns, the anchor coverage warning), render `(schema_upstream.table)` muted next to the effective label. Used by `RelationshipsPanel.tsx`, `PolicyAnchorCoveragePanel.tsx`, and the `?focus=` deep-link matcher in `ColumnAnchorsSection`.
22
23
-`src/api/catalog.ts` — API client for discovery catalog + `table_relationship` / `column_anchor` / FK suggestions (`listRelationships`, `createRelationship`, `deleteRelationship`, `listFkSuggestions`, `listColumnAnchors`, `createColumnAnchor`, `deleteColumnAnchor`)
-`src/pages/PolicyEditPage.tsx` — T1 sidebar edit page. Sections: Details, Assignments, Anchor coverage (row_filter only), View as code, Activity. Danger zone offers typed-name delete with a "Disable instead" escape via `is_enabled`. Save keeps the user on the page (toast-only); 409 conflicts show an inline banner.
51
+
-`src/pages/PolicyEditPage.tsx` — T1 sidebar edit page. Sections: Details, Assignments, Anchor coverage (row_filter only), View as code, Activity. Owns the `useQuery(['policy-anchor-coverage', policyId, version])` and passes the result down to `PolicyAnchorCoveragePanel`; `sectionsFor()` reads the broken-entry count and stamps a red count pill on the `Anchor coverage` nav item via `SectionDef.indicator`. Danger zone offers typed-name delete with a "Disable instead" escape via `is_enabled`. Save keeps the user on the page (toast-only); 409 conflicts show an inline banner.
0 commit comments