# Issue #358: Build frontend credential management pages ## Objective Create frontend credential management pages at `/settings/credentials` with full CRUD operations, following PDA-friendly design principles and existing UI patterns. ## Backend API Reference - `POST /api/credentials` - Create (encrypt + store) - `GET /api/credentials` - List (masked values only) - `GET /api/credentials/:id` - Get single (masked) - `GET /api/credentials/:id/value` - Decrypt and return value (rate-limited) - `PATCH /api/credentials/:id` - Update metadata only - `POST /api/credentials/:id/rotate` - Replace value - `DELETE /api/credentials/:id` - Soft delete ## Approach ### 1. Component Architecture ``` /app/(authenticated)/settings/credentials/ └── page.tsx (main list + modal orchestration) /components/credentials/ ├── CredentialList.tsx (card grid) ├── CredentialCard.tsx (individual credential display) ├── CreateCredentialDialog.tsx (create form) ├── EditCredentialDialog.tsx (metadata edit) ├── ViewCredentialDialog.tsx (reveal value) ├── RotateCredentialDialog.tsx (rotate value) └── DeleteCredentialDialog.tsx (confirm deletion) /lib/api/ └── credentials.ts (API client functions) ``` ### 2. UI Patterns (from existing code) - Use shadcn/ui components: `Card`, `Button`, `Badge`, `AlertDialog` - Follow personalities page pattern for list/modal state management - Use lucide-react icons: `Plus`, `Eye`, `EyeOff`, `Pencil`, `RotateCw`, `Trash2` - Mobile-first responsive design ### 3. Security Requirements - **NEVER display plaintext in list** - only `maskedValue` - **Reveal button** requires explicit click - **Auto-hide revealed value** after 30 seconds - **Warn user** before revealing (security-conscious UX) - Show rate-limit warnings (10 requests/minute) ### 4. PDA-Friendly Language ``` ❌ NEVER ✅ ALWAYS ───────────────────────────────────────── "Delete credential" "Remove credential" "EXPIRED" "Past target date" "CRITICAL" "High priority" "You must rotate" "Consider rotating" ``` ## Progress - [x] Read issue details and design doc - [x] Study existing patterns (personalities page) - [x] Identify available UI components - [x] Create API client functions (`lib/api/credentials.ts`) - [x] Create dialog component (`components/ui/dialog.tsx`) - [x] Create credential components - [x] CreateCredentialDialog.tsx - [x] ViewCredentialDialog.tsx (with reveal + auto-hide) - [x] EditCredentialDialog.tsx - [x] RotateCredentialDialog.tsx - [x] CredentialCard.tsx - [x] Create settings page (`app/(authenticated)/settings/credentials/page.tsx`) - [x] TypeScript typecheck passes - [x] Build passes - [ ] Add navigation link to settings - [ ] Manual testing - [ ] Verify PDA language compliance - [ ] Mobile responsiveness check ## Implementation Notes ### Missing UI Components - Need to add `dialog.tsx` from shadcn/ui - Have: `alert-dialog`, `card`, `button`, `badge`, `input`, `label`, `textarea` ### Provider Icons Support providers: GitHub, GitLab, OpenAI, Bitbucket, Custom - Use lucide-react icons or provider-specific SVGs - Fallback to generic `Key` icon ### State Management Follow personalities page pattern: ```typescript const [mode, setMode] = useState<"list" | "create" | "edit" | "view" | "rotate">("list"); const [selectedCredential, setSelectedCredential] = useState(null); ``` ## Testing - [ ] Create credential flow - [ ] Edit metadata (name, description) - [ ] Reveal value (with auto-hide) - [ ] Rotate credential - [ ] Delete credential - [ ] Error handling (validation, API errors) - [ ] Rate limiting on reveal - [ ] Empty state display - [ ] Mobile layout ## Notes - Backend API complete (commit 46d0a06) - RLS enforced - users only see own credentials - Activity logging automatic on backend - Custom UI components (no Radix UI dependencies) - Dialog component created matching existing alert-dialog pattern - Navigation: Direct URL access at `/settings/credentials` (no nav link added - settings accessed directly) - Workspace ID: Currently hardcoded as placeholder - needs context integration ## Files Created ``` apps/web/src/ ├── components/ │ ├── ui/ │ │ └── dialog.tsx (new custom dialog component) │ └── credentials/ │ ├── index.ts │ ├── CreateCredentialDialog.tsx │ ├── ViewCredentialDialog.tsx │ ├── EditCredentialDialog.tsx │ ├── RotateCredentialDialog.tsx │ └── CredentialCard.tsx ├── lib/api/ │ └── credentials.ts (API client with PDA-friendly helpers) └── app/(authenticated)/settings/credentials/ └── page.tsx (main credentials management page) ``` ## PDA Language Verification ✅ All dialogs use PDA-friendly language: - "Remove credential" instead of "Delete" - "Past target date" instead of "EXPIRED" - "Approaching target" instead of "URGENT" - "Consider rotating" instead of "MUST rotate" - Warning messages use informative tone, not demanding ## Security Features Implemented ✅ Masked values only in list view ✅ Reveal requires explicit user action (with warning) ✅ Auto-hide revealed value after 30 seconds ✅ Copy-to-clipboard for revealed values ✅ Manual hide button for revealed values ✅ Rate limit warning on reveal errors ✅ Password input fields for sensitive values ✅ Security warnings before revealing ## Next Steps for Production - [ ] Integrate workspace context (remove hardcoded workspace ID) - [ ] Add settings navigation menu or dropdown - [ ] Test with real OpenBao backend - [ ] Add loading states for API calls - [ ] Add optimistic updates for better UX - [ ] Add filtering/search for large credential lists - [ ] Add pagination for credential list - [ ] Write component tests