AcadeDoc/apps/client/src/features/acadenice/api-keys/components/revoke-api-key-modal.tsx
Corentin 5b512e6324 feat(acadedoc): replace EE Settings with open source UI (audit log, API keys, OIDC status) — R4.5
Server (NestJS):
- AcadeniceAuditLogModule: GET /api/acadenice/audit-log (admin/owner, Kysely, paginated + filtered)
- AcadeniceApiKeysModule: GET/POST/DELETE /api/acadenice/api-keys (JWT, bcrypt hash, acdk_ prefix)
- AcadeniceSecurityModule: GET /api/acadenice/security/oidc-status (admin, no secrets exposed)
- Migration 20260510T100000: acadenice_api_key table with token_hash + bcrypt
- Permissions catalog: added audit_log:read

Client (React 18 + Mantine v7):
- Audit log page: paginated table with filters (event, userId, date range)
- API keys page: list/create/revoke personal tokens, one-time display modal
- Security/OIDC status page: read-only, env-var config reference
- Sidebar rewired: Security & SSO, API keys, Audit log -> acadenice/* routes (no EE feature gates)
- Prefetch functions for new routes

Tests: 36 server (Jest) + client typecheck clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:24:00 +02:00

50 lines
1.3 KiB
TypeScript

import { Button, Group, Modal, Text } from "@mantine/core";
import { useTranslation } from "react-i18next";
import { AcadeniceApiKey } from "../types/api-key.types";
import { useRevokeAcadeniceApiKeyMutation } from "../queries/api-key.queries";
interface RevokeApiKeyModalProps {
opened: boolean;
onClose: () => void;
apiKey: AcadeniceApiKey | null;
}
export function AcadeniceRevokeApiKeyModal({
opened,
onClose,
apiKey,
}: RevokeApiKeyModalProps) {
const { t } = useTranslation();
const revokeMutation = useRevokeAcadeniceApiKeyMutation();
if (!apiKey) return null;
const handleRevoke = () => {
revokeMutation.mutate(apiKey.id, { onSuccess: onClose });
};
return (
<Modal opened={opened} onClose={onClose} title={t("Revoke API key")} size="sm">
<Text size="sm" mb="md">
{t(
'Are you sure you want to revoke "{{label}}"? This action cannot be undone.',
{ label: apiKey.label },
)}
</Text>
<Group justify="flex-end">
<Button variant="default" onClick={onClose}>
{t("Cancel")}
</Button>
<Button
color="red"
loading={revokeMutation.isPending}
onClick={handleRevoke}
aria-label={t("Confirm revoke API key")}
>
{t("Revoke")}
</Button>
</Group>
</Modal>
);
}