feat(web): wire dashboard loaders, mutations, and error toasts (backlog #4)#46
Conversation
…oasts
Replace the EmptyState/no-loader fallbacks across the authed dashboard with
real loaders against the existing /v1/web/* endpoints, wire the previously
dead mutations, and surface api error envelopes as toasts.
- dashboard: loads GET /v1/web/workspace + recent artifacts + recent audit in
parallel; renders a usage-policy card, recent tables, and a first-run key
card gated on default_key_first_run (secret held in component state only).
- artifacts: index rows link to detail; detail surfaces entrypoint, file
count, size, and pinned/lockdown badges.
- keys: KeyCreateForm (POST /v1/web/keys) reveals the one-time secret in state;
KeysTable Revoke (POST /v1/web/keys/{id}/revoke) refreshes via router
invalidate. Both go through createServerFn -> apiFetch with a generated
Idempotency-Key.
- settings: SettingsForm saves name + auto-deletion days (PATCH
/v1/web/settings).
- audit: reads ?request_id= and highlights the matching row.
- toasts: ToastProvider/useToast mounted in _authed plus an errorToast helper;
failures show code + message + a link to /audit?request_id=<id>.
- Access Links and the operator lockdown list stay EmptyState (endpoints
deferred / Phase 4).
Adds Vitest coverage for the toast mechanism, first-run card, key create/revoke,
settings save, and dashboard cards. pnpm verify green (70 tasks).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- ToastProvider: clear pending auto-dismiss timers on unmount - SettingsForm: reject auto-deletion days outside 1..90 client-side - audit: mark the request_id-matched row with aria-current - test: cover the out-of-range auto-deletion guard Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Address CodeRabbit round 2: - web-mutations: validate createKey/revokeKey/saveSettings inputs against the canonical contract schemas (CreateApiKeyRequest, ApiKeyId, UpdateWebSettingsRequest) so malformed payloads never reach the API - artifact-status: map Expired->warning and Deleted->destructive badge tones, shared by the dashboard and artifacts index - FirstRunKeyCard: clarify the missing-secret copy - keys: memoize the refresh callback Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Address CodeRabbit round 3: - artifacts detail route now uses the shared artifactStatusTone helper - remove the unreachable neutral fallback (the tone map is exhaustive over the WebArtifactStatus enum) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
WalkthroughAdds a data-driven dashboard loader plus dashboard cards (usage, recent artifacts, recent audit, first-run key). Introduces toast context/provider/UI and integrates it into the authed layout. Adds validated server mutations for key creation/revocation and settings update. Adds KeyCreateForm, KeysTable, NewKeySecretCard, SettingsForm, artifact-status utility, audit request_id highlighting, and tests for components and server flows. Sequence Diagram(s)sequenceDiagram
participant Page as DashboardPage
participant Loader as loadDashboardFn
participant API as Upstream API
Page->>Loader: request workspace/artifacts/audit
Loader->>API: parallel fetch workspace, artifacts, audit
API-->>Loader: return data/errors
Loader-->>Page: { workspace, artifacts, audit }
Page->>Page: render UsagePolicy / FirstRunKeyCard / RecentArtifacts / RecentAudit
sequenceDiagram
participant User
participant KeyForm as KeyCreateForm
participant Server as createKeyFn
participant Upstream as /v1/web/keys
User->>KeyForm: submit name
KeyForm->>Server: createKeyFn(payload)
Server->>Upstream: POST /v1/web/keys (idempotency-key)
Upstream-->>Server: response
Server-->>KeyForm: MutationResult (data or error)
KeyForm->>User: show secret or error toast
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
|
agent-paste PR preview is ready. API: https://agent-paste-api-pr-46.isaac-a46.workers.dev |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/src/routes/_authed.artifacts.index.tsx (1)
55-55:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate column header to match content.
The column header reads "Actions" but the corresponding cells (lines 83–85) only display a "Pinned" badge with no interactive actions. The AI summary confirms that "the previous trailing ellipsis/actions cell was removed."
🔧 Proposed fix
- <TH className="text-right">Actions</TH> + <TH className="text-right">Pinned</TH>Or remove the header entirely if the column is purely informational:
- <TH className="text-right">Actions</TH> + <TH className="text-right"></TH>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/routes/_authed.artifacts.index.tsx` at line 55, The table header currently reads "Actions" (the <TH className="text-right">Actions</TH>) but the corresponding cells only render a non-interactive "Pinned" badge (the code that outputs the "Pinned" badge in the row cells), so update the header to match the content (e.g., change "Actions" to "Status" or "Pinned") or remove the <TH> entirely if you prefer no header for that informational column; make the change next to the existing <TH className="text-right"> and the code that renders the "Pinned" badge so they stay semantically consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/components/settings/SettingsForm.tsx`:
- Around line 20-35: After a successful save in the Save handler (where
saveSettingsFn is called and parsedDays is computed), normalize the local form
state to match the persisted values: call setName(name.trim()) and
setDays(String(parsedDays)) immediately after router.invalidate() (or before
resolving) so the inputs reflect the trimmed workspace_name and numeric
auto_deletion_days; ensure this runs only when result.error is falsy and
preserves the existing setPending(false)/push(...) flow.
In `@apps/web/src/routes/_authed.artifacts.index.tsx`:
- Line 67: The link currently renders row.title directly and can be blank when
title is null/undefined/empty; update the rendering logic in the component that
outputs {row.title} to use a fallback (e.g., row.title || 'Untitled' or prefer
row.fileName || 'Untitled artifact') so a sensible string is shown when title is
missing; locate the place that renders row.title in the artifacts index route
and replace it with a fallback expression or helper that normalizes empty
strings as well as null/undefined.
In `@apps/web/src/server/web-mutations.ts`:
- Around line 89-91: The handler currently accesses data.apiKeyId directly which
will throw if data is null/not an object; change the handler in web-mutations.ts
(the .handler(({ data }) => { ... }) block) to validate that data is an object
before reading apiKeyId (e.g., check data != null && typeof data === 'object' or
use safe access like data?.apiKeyId) and if invalid return the same
Promise.resolve({ data: null, error: <validation_error> }) contract; then call
parseInput(ApiKeyId, apiKeyIdValue) only after the safe check so parseInput is
never passed an access that can throw.
---
Outside diff comments:
In `@apps/web/src/routes/_authed.artifacts.index.tsx`:
- Line 55: The table header currently reads "Actions" (the <TH
className="text-right">Actions</TH>) but the corresponding cells only render a
non-interactive "Pinned" badge (the code that outputs the "Pinned" badge in the
row cells), so update the header to match the content (e.g., change "Actions" to
"Status" or "Pinned") or remove the <TH> entirely if you prefer no header for
that informational column; make the change next to the existing <TH
className="text-right"> and the code that renders the "Pinned" badge so they
stay semantically consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 8f564cb1-8011-48ae-8cd1-3e7e2e0f3d33
📒 Files selected for processing (29)
apps/web/src/components/dashboard/FirstRunKeyCard.tsxapps/web/src/components/dashboard/RecentArtifacts.tsxapps/web/src/components/dashboard/RecentAudit.tsxapps/web/src/components/dashboard/UsagePolicyCard.tsxapps/web/src/components/keys/KeyCreateForm.tsxapps/web/src/components/keys/KeysTable.tsxapps/web/src/components/keys/NewKeySecretCard.tsxapps/web/src/components/settings/SettingsForm.tsxapps/web/src/components/ui/ToastList.tsxapps/web/src/components/ui/ToastProvider.tsxapps/web/src/components/ui/toast-context.tsapps/web/src/lib/artifact-status.tsapps/web/src/routes/_authed.artifacts.$artifactId.tsxapps/web/src/routes/_authed.artifacts.index.tsxapps/web/src/routes/_authed.audit.tsxapps/web/src/routes/_authed.dashboard.tsxapps/web/src/routes/_authed.keys.tsxapps/web/src/routes/_authed.settings.tsxapps/web/src/routes/_authed.tsxapps/web/src/server/web-mutations.tsapps/web/test/FirstRunKeyCard.test.tsxapps/web/test/KeyCreateForm.test.tsxapps/web/test/KeysTable.test.tsxapps/web/test/RecentAudit.test.tsxapps/web/test/SettingsForm.test.tsxapps/web/test/UsagePolicyCard.test.tsxapps/web/test/artifact-status.test.tsapps/web/test/toast.test.tsxdocs/ops/web-app-todo.md
- SettingsForm: sync local name/days state to the persisted server response after save so the form stops showing stale (untrimmed/unclamped) input. - Artifacts index + detail: render "Untitled" when an artifact title is empty. - revokeKeyFn: read data?.apiKeyId defensively so a malformed server-fn payload yields a 400 validation envelope instead of a TypeError. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
agent-paste PR preview is ready. API: https://agent-paste-api-pr-46.isaac-a46.workers.dev |
|
agent-paste PR preview resources were cleaned up. The pr-preview-${context.issue.number} environment is left in place; remove it from the GitHub UI if desired. |
Snapshot now points at ad85175 (#46); record the operator lockdown (#45) and web loader-wiring (#46) merges in Recently Completed; strike backlog #4 as done; update the Phase 3 (~55%), web.md, ADR 0055 rows. Remaining Phase 3 code work is CLI login (#5) and smoke:web (#6), both gated on the WorkOS/Access click-ops in backlog item #1. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
What
Wires the
apps/webdashboard to live data, the backlog #4 "web follow-up wiring" item indocs/ops/web-app-todo.md. All backing/v1/web/*endpoints already exist; this connects the UI to them./v1/web/workspace+ recent artifacts + audit viaPromise.all), artifacts index/detail, audit, and settings now fetch real data. Genuine zero-row states still renderEmptyState.access-linksand the admin lockdown list stay asEmptyState(Phase 4 / deferred endpoints, out of scope).src/server/web-mutations.ts):createKey,revokeKey,saveSettingsviacreateServerFn→apiFetchwith the WorkOS access token and a per-requestIdempotency-Key. Inputs are parsed against the canonical@agent-paste/contractsschemas before the upstream call. One-time secrets (first-run default key + create-key response) live in component state only, never persisted or logged.GET /v1/web/workspacereturnsdefault_key_first_run = true.ToastProvider/ToastListsurfaceapierror envelopes (code + message + a/audit?request_id=…link).Tests
pnpm verifygreen (70/70 Turbo tasks).aria-current, status-tone consistency, dead-fallback removal). Two finding classes declined with rationale:userEvent-over-fireEvent(not a repo dependency; matches existing test convention) and "derive idempotency key server-side" (a fresh key per click is correct; the API owns idempotency).Not verified here
Live-preview rendering against a real WorkOS login is not verified: it depends on Isaac's WorkOS project + Cloudflare Access click-ops (backlog #1). Verification here is static (typecheck/lint/build) plus jsdom component tests.
smoke:web+ CI deploy remain separate, blocked backlog items.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation