File size: 5,365 Bytes
1798222 2b62b0c 1798222 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 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>
);
}
|