---
id: w5/m8/t005
title: Database detail page — status, metadata, on-demand connection-info reveal
worker: worker5
status: done
estimate: 60m
depends_on: [w5/m8/t002]
---

## Objective

Ship the database detail page: status + metadata from `database(id)`, and a connection-info section that fetches `databaseConnectionInfo(id)` **only when the operator asks** — masked password with reveal + copy buttons, internal/external connection strings, and the psql command — mirroring Render's info page and its credentials interaction from the t001 capture.

## Context

- `connection-info` is the only place the DB password is surfaced (read from CNPG's `<name>-app` Secret at request time — `docs/ADR006-bex-api.md`); the query must be lazy (fired on reveal, never on page load) and the value never logged or cached beyond component state.
- Precedent for the on-demand-secret UX: the env-vars tab's "Show secret" pattern (w4/m6.5, Render `envVar(key)` on demand).
- Deferred Render sections (backups/PITR, credentials rotation, pooler, failover) are omitted, not stubbed.

## Steps

1. Detail route `databases.$databaseId` rendering `database(id)` status + metadata sections per the t001 capture.
2. Connection-info card: reveal button → lazy `databaseConnectionInfo(id)` → masked password with show/copy, copy buttons for both connection strings + psql command.
3. Loading/error/not-found states; localized strings.

## Files

- `dashboard/src/routes/databases.$databaseId.tsx`
- `dashboard/src/features/databases/components/` (detail sections, connection-info card)
- `dashboard/src/features/databases/hooks/` (lazy connection-info hook), locales

## Acceptance criteria

- [ ] Detail page renders live `database(id)` data with loading/error/not-found handled.
- [ ] Connection info is fetched only on user action; password masked by default; copy works for password, both strings, and psql command.
- [ ] No deferred-verb UI is stubbed or faked.

## Out of scope

- Create/delete (t004); metrics/logs for databases (no backend surface).
