'use client';
import { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { listApiKeys, createApiKey, revokeApiKey } from '@/lib/api/api-keys';
import type { ApiKeyDto, CreateApiKeyResultDto } from '@/lib/api/api-keys';
import { useSubscription } from '@/lib/context/subscription-context';
import {
Key,
Plus,
Trash2,
Copy,
Check,
AlertTriangle,
Clock,
X,
ShieldCheck,
Lock,
} from 'lucide-react';
// ─── Helpers ────────────────────────────────────────────────────────────────
function formatDate(iso: string | null): string {
if (!iso) return '—';
return new Intl.DateTimeFormat('fr-FR', { dateStyle: 'medium' }).format(new Date(iso));
}
function keyStatusBadge(key: ApiKeyDto) {
if (!key.isActive) {
return (
Révoquée
);
}
if (key.expiresAt && new Date(key.expiresAt) < new Date()) {
return (
Expirée
);
}
return (
Active
);
}
// ─── Copy button ─────────────────────────────────────────────────────────────
function CopyButton({ text }: { text: string }) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
);
}
// ─── Creation success modal ──────────────────────────────────────────────────
function CreatedKeyModal({
result,
onClose,
}: {
result: CreateApiKeyResultDto;
onClose: () => void;
}) {
return (
{/* Header */}
Clé API créée
{result.name}
{/* Warning */}
Copiez cette clé maintenant. Elle ne sera plus jamais affichée après
la fermeture de cette fenêtre.
{/* Key */}
{result.fullKey}
Stockez-la dans vos variables d'environnement ou un gestionnaire de secrets.
{/* Footer */}
);
}
// ─── Create key form modal ───────────────────────────────────────────────────
function CreateKeyModal({
onSuccess,
onClose,
}: {
onSuccess: (result: CreateApiKeyResultDto) => void;
onClose: () => void;
}) {
const [name, setName] = useState('');
const [expiresAt, setExpiresAt] = useState('');
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: createApiKey,
onSuccess: result => {
queryClient.invalidateQueries({ queryKey: ['api-keys'] });
onSuccess(result);
},
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
mutation.mutate({
name: name.trim(),
...(expiresAt ? { expiresAt: new Date(expiresAt).toISOString() } : {}),
});
};
return (
);
}
// ─── Revoke confirm modal ────────────────────────────────────────────────────
function RevokeConfirmModal({
apiKey,
onConfirm,
onClose,
}: {
apiKey: ApiKeyDto;
onConfirm: () => void;
onClose: () => void;
}) {
return (
Révoquer cette clé ?
{apiKey.name}
Cette action est immédiate et irréversible. Toute requête utilisant
cette clé sera refusée.
);
}
// ─── Main page ────────────────────────────────────────────────────────────────
export default function ApiKeysPage() {
const { hasFeature } = useSubscription();
const queryClient = useQueryClient();
const hasApiAccess = hasFeature('api_access');
const [showCreateModal, setShowCreateModal] = useState(false);
const [createdKey, setCreatedKey] = useState(null);
const [revokeTarget, setRevokeTarget] = useState(null);
const { data: apiKeys, isLoading } = useQuery({
queryKey: ['api-keys'],
queryFn: listApiKeys,
enabled: hasApiAccess,
});
const revokeMutation = useMutation({
mutationFn: revokeApiKey,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['api-keys'] });
setRevokeTarget(null);
},
});
// Plan upsell screen
if (!hasApiAccess) {
return (
Accès API
L'accès programmatique à l'API Xpeditis est disponible sur les plans{' '}
Gold et Platinium uniquement.
Voir les plans
);
}
const activeKeys = apiKeys?.filter(k => k.isActive) ?? [];
return (
<>
{/* Modals */}
{showCreateModal && (
{
setShowCreateModal(false);
setCreatedKey(result);
}}
onClose={() => setShowCreateModal(false)}
/>
)}
{createdKey && (
setCreatedKey(null)} />
)}
{revokeTarget && (
revokeMutation.mutate(revokeTarget.id)}
onClose={() => setRevokeTarget(null)}
/>
)}
{/* Page header */}
Clés API
Gérez les clés d'accès programmatique à l'API Xpeditis.
{/* Info banner */}
Comment utiliser vos clés API
Ajoutez l'en-tête{' '}
X-API-Key: xped_live_...
{' '}
à chaque requête HTTP.{' '}
Voir la documentation
{/* Keys list */}
{isLoading ? (
) : !apiKeys || apiKeys.length === 0 ? (
Aucune clé API pour le moment.
) : (
{/* Table header */}
Nom / Préfixe
Dernière utilisation
Expiration
Statut
{apiKeys.map(key => (
{/* Name + prefix */}
{key.name}
{key.keyPrefix}…
{/* Last used */}
{formatDate(key.lastUsedAt)}
{/* Expiry */}
{formatDate(key.expiresAt)}
{/* Status */}
{keyStatusBadge(key)}
{/* Actions */}
))}
)}
{/* Quota */}
{apiKeys && apiKeys.length > 0 && (
{activeKeys.length} / 20 clés actives utilisées
)}
>
);
}