The api-keys component (src/app/api-keys/page.tsx) provides the API keys management view for the dashboard. It handles listing, creating, and revoking API keys.
This is a Next.js route component and accepts no custom React props. It is rendered automatically by the App Router at /api-keys.
The component maintains the following internal state to manage the lifecycle of API keys:
fetchState:FetchState- The load state of the key list. A single discriminated union, mirroring theloading | ok | errorvocabulary used by the shareduseApi/usePollinghooks:{ status: "loading" }- a load is in flight.{ status: "ok"; items: KeyItem[] }- the list loaded;itemsmay be empty.{ status: "error"; message: string }- the load failed.
label:string- The current input value for a new key's label in the creation form.created:string | null- The newly created raw key value, which is shown only once upon creation.showFull:boolean- Toggles whether to reveal the fullcreatedkey value or keep it masked.actionError:string | null- The error message from a failed create or revoke. Kept separate fromfetchStatebecause an action failure annotates an otherwise-usable view rather than replacing it.pendingRevoke:KeyItem | null- The key currently selected for revocation, used to show the confirmation dialog.
The view renders a visually-hidden aria-live="polite" region
(data-testid="api-keys-announcer") so screen-reader users hear when the key
list actually changes. The text is produced by
useApiKeysAnnouncement.
| Fetch state | Announcement key | Announced text |
|---|---|---|
Loading (items === null) |
idle |
(silent) |
| Load/action error | idle |
(silent) |
| Loaded, no keys | empty |
No API keys |
| Loaded, one key | count:1 |
1 API key |
| Loaded, N keys | count:N |
N API keys |
Behaviour notes for reviewers:
- Silent on mount. The region is mounted empty so assistive tech registers it before the first change, and the first settled state is kept as a silent baseline. Only later changes are announced.
- Debounced (300ms). Rapid successive updates — for example a revoke followed immediately by a reload — collapse into a single announcement instead of queueing one per update.
- Errors are not repeated. Failures already render a
role="alert"message, which assistive tech announces on its own, so the polite region stays silent to avoid double-speaking. - Announcement keys are primitives.
useDebouncecompares by identity, so the state is flattened to a string (count:3) rather than an object; an object would be a new reference each render and the timer would never settle. - The hook derives text only. It reads the existing
itemsanderrorstate and does not change how keys are loaded, created, or revoked.
Because this is a route-level page component, it is not imported and rendered manually as a typical React component. It is accessed by navigating to its route:
import Link from "next/link";
export function SettingsNav() {
return (
<nav>
<Link href="/api-keys">Manage API Keys</Link>
</nav>
);
}