Files
stack/apps/web/src/app/(authenticated)/settings/credentials/page.tsx
Jason Woltje 6a5a4e4de8 feat(web): add credential management UI pages and components
Add credentials settings page, audit log page, CRUD dialog components
(create, view, edit, rotate), credential card, dialog UI component,
and API client for the M7-CredentialSecurity feature.

Refs #346

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 09:42:41 -06:00

99 lines
3.1 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import { Plus, History } from "lucide-react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { fetchCredentials, type Credential } from "@/lib/api/credentials";
export default function CredentialsPage(): React.ReactElement {
const [credentials, setCredentials] = useState<Credential[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const workspaceId = "default-workspace-id";
useEffect(() => {
void loadCredentials();
}, []);
async function loadCredentials(): Promise<void> {
try {
setIsLoading(true);
const response = await fetchCredentials(workspaceId);
setCredentials(response.data);
setError(null);
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to load credentials");
} finally {
setIsLoading(false);
}
}
return (
<div className="max-w-6xl mx-auto p-6">
{/* Header */}
<div className="mb-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Credentials</h1>
<p className="text-muted-foreground mt-1">
Securely store and manage API keys, tokens, and passwords
</p>
</div>
<div className="flex gap-2">
<Button disabled>
<Plus className="mr-2 h-4 w-4" />
Add Credential
</Button>
<Link href="/settings/credentials/audit">
<Button variant="outline">
<History className="mr-2 h-4 w-4" />
Audit Log
</Button>
</Link>
</div>
</div>
</div>
{/* Error Display */}
{error && <div className="mb-4 p-4 bg-red-50 text-red-800 rounded-md">{error}</div>}
{/* Loading State */}
{isLoading ? (
<div className="text-center py-12">
<p className="text-gray-500">Loading credentials...</p>
</div>
) : credentials.length === 0 ? (
<Card>
<CardContent className="flex flex-col items-center justify-center py-12">
<p className="text-gray-500 mb-4">No credentials found</p>
<Button disabled>
<Plus className="mr-2 h-4 w-4" />
Add First Credential
</Button>
</CardContent>
</Card>
) : (
<div className="grid gap-4 grid-cols-1">
<Card>
<CardContent className="pt-6">
<div className="text-sm text-gray-600">
<p>Credentials feature coming soon.</p>
<p className="mt-2">
<Link href="/settings/credentials/audit">
<Button variant="link" className="p-0">
View Audit Log
</Button>
</Link>
</p>
</div>
</CardContent>
</Card>
</div>
)}
</div>
);
}