import { ActionIcon, Badge, Group, Skeleton, Table, Text, Tooltip } from "@mantine/core"; import { IconTrash } from "@tabler/icons-react"; import { modals } from "@mantine/modals"; import { useTranslation } from "react-i18next"; import NoTableResults from "@/components/common/no-table-results"; import { formatLocalized, useDateFnsLocale } from "@/lib/date-locale.ts"; import { IOAuthGrant } from "@/ee/oauth/types/oauth.types"; import { useRevokeOAuthGrantMutation } from "@/ee/oauth/queries/oauth-query"; // Callback hosts identify the app; the full URL belongs on the consent screen. function callbackHosts(redirectUris: string[] = []): string[] { const hosts = redirectUris.map((uri) => { try { const url = new URL(uri); return url.protocol === "http:" || url.protocol === "https:" ? url.host : url.protocol; } catch { return uri; } }); return Array.from(new Set(hosts)); } type OAuthGrantsTableProps = { grants: IOAuthGrant[]; isLoading?: boolean; }; function TableSkeleton() { return ( <> {Array.from({ length: 3 }).map((_, i) => ( ))} ); } export function OAuthGrantsTable({ grants, isLoading }: OAuthGrantsTableProps) { const { t } = useTranslation(); const locale = useDateFnsLocale(); const revokeMutation = useRevokeOAuthGrantMutation(); const scopeMeta: Record = { read: { color: "blue", label: t("Read") }, write: { color: "orange", label: t("Write") }, }; const formatDate = (date: string | null) => { if (!date) return t("Never"); return formatLocalized(date, "MMM dd, yyyy", "PP", locale); }; const openRevokeModal = (grant: IOAuthGrant) => modals.openConfirmModal({ title: t("Revoke access"), centered: true, children: ( {t( "Are you sure you want to revoke access for {{name}}? The application will no longer be able to access your account.", { name: grant.clientName }, )} ), labels: { confirm: t("Revoke access"), cancel: t("Cancel") }, confirmProps: { color: "red" }, onConfirm: () => revokeMutation.mutate(grant.id), }); return ( {t("Application")} {t("Permissions")} {t("Authorized")} {t("Last used")} {isLoading ? ( ) : grants.length > 0 ? ( grants.map((grant) => ( {grant.clientName} {callbackHosts(grant.redirectUris).join(", ")} {grant.scopes.map((scope) => { const meta = scopeMeta[scope]; if (!meta) return null; return ( {meta.label} ); })} {formatDate(grant.createdAt)} {formatDate(grant.lastUsedAt)} openRevokeModal(grant)} > )) ) : ( )}
); }