cpns / apps /web /src /components /settings /ApiKeyList.tsx
pramudya-yb
Rename project from Labas to Pram
2b62b0c
Raw
History Blame Contribute Delete
5.37 kB
import { type ApiKeyConfig } from "@/hooks/use-api-key";
import { Button } from "@pram/ui/components/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@pram/ui/components/card";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
interface ProviderOption {
value: string;
label: string;
}
interface ApiKeyListProps {
configs: ApiKeyConfig[];
isLoading: boolean;
editingId: string | null;
isFormOpen: boolean;
providers: ProviderOption[];
onStartAdd: () => void;
onStartEdit: (config: ApiKeyConfig) => void;
onRemove: (id: string) => void;
}
export function ApiKeyList({
configs,
isLoading,
editingId,
isFormOpen,
providers,
onStartAdd,
onStartEdit,
onRemove,
}: ApiKeyListProps) {
return (
<Card className="clay-shadow bg-[var(--pure-white)] border-2 border-[var(--oat-border)] rounded-[var(--radius-xl)]">
<CardHeader>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<MaterialIcon name="key" className="text-xl" />
<div>
<CardTitle className="font-headline text-[var(--clay-black)]">API Keys</CardTitle>
<CardDescription className="text-[var(--warm-charcoal)]">
{configs.length} konfigurasi tersimpan
</CardDescription>
</div>
</div>
{!isFormOpen && (
<Button
onClick={onStartAdd}
className="bg-[var(--clay-black)] text-[var(--pure-white)] hover:bg-[var(--warm-charcoal)] rounded-[var(--radius-lg)] clay-shadow clay-hover text-sm"
>
<MaterialIcon name="add" className="mr-1" />
Tambah
</Button>
)}
</div>
</CardHeader>
<CardContent className="space-y-4">
{isLoading ? (
<div className="h-20 bg-[var(--oat-light)] animate-pulse rounded-[var(--radius-lg)]" />
) : configs.length === 0 ? (
<div className="text-center py-8">
<MaterialIcon name="key_off" className="text-4xl text-[var(--warm-silver)] mx-auto mb-3" />
<p className="text-[var(--warm-charcoal)] font-semibold">Belum ada API key</p>
<p className="text-xs text-[var(--warm-silver)] mt-1">
Tambahkan minimal satu key untuk generate soal.
</p>
<Button
onClick={onStartAdd}
className="mt-4 bg-[var(--clay-black)] text-[var(--pure-white)] hover:bg-[var(--warm-charcoal)] rounded-[var(--radius-lg)]"
>
<MaterialIcon name="add" className="mr-1" />
Tambah API Key
</Button>
</div>
) : (
<div className="space-y-3">
{configs.map((config) => {
const isEditing = editingId === config.id;
return (
<div
key={config.id}
className={`p-4 rounded-[var(--radius-lg)] border-2 transition-colors ${
isEditing
? "border-[var(--matcha-600)] bg-[var(--matcha-100)]"
: "border-[var(--oat-border)] bg-[var(--warm-cream)]"
}`}
>
<div className="flex items-center justify-between">
<div className="min-w-0">
<div className="flex items-center gap-2 mb-1">
<span className="font-semibold text-[var(--clay-black)] truncate">
{config.name}
</span>
<span className="px-2 py-0.5 rounded-full bg-[var(--matcha-300)] text-[var(--matcha-800)] text-[10px] font-semibold">
{providers.find((p) => p.value === config.provider)?.label ?? config.provider}
</span>
</div>
<div className="text-xs text-[var(--warm-charcoal)]">
{config.modelName} · {config.baseUrl.replace("https://", "").replace("/v1", "")}
</div>
</div>
<div className="flex gap-2 shrink-0">
<button
onClick={() => onStartEdit(config)}
className="p-2 rounded-[var(--radius-md)] hover:bg-[var(--oat-light)] text-[var(--warm-charcoal)] transition-colors"
title="Edit"
>
<MaterialIcon name="edit" className="text-sm" />
</button>
<button
onClick={() => {
if (confirm("Yakin mau hapus konfigurasi ini?")) {
onRemove(config.id);
}
}}
className="p-2 rounded-[var(--radius-md)] hover:bg-[var(--pomegranate-400)]/10 text-[var(--pomegranate-400)] transition-colors"
title="Hapus"
>
<MaterialIcon name="delete" className="text-sm" />
</button>
</div>
</div>
</div>
);
})}
</div>
)}
</CardContent>
</Card>
);
}