import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useI18n } from "../i18n"; import { useToast } from "../hooks/useToast"; import { apiDelete, apiGet, apiPost } from "../services/api"; import { formatSystemTime } from "../utils/time"; type SessionRow = { id: string; created_at: string | null; expires_at: string | null; refresh_expires_at: string | null; last_seen_at: string | null; client_ip: string; user_agent: string; current: boolean; }; export function SessionsPage() { const { t } = useI18n(); const { showOk, showError } = useToast(); const qc = useQueryClient(); const sessionsQuery = useQuery({ queryKey: ["auth-sessions"], queryFn: () => apiGet<{ items: SessionRow[]; total: number }>("/v1/auth/sessions"), }); const revokeMut = useMutation({ mutationFn: (id: string) => apiDelete(`/v1/auth/sessions/${encodeURIComponent(id)}`), onSuccess: () => { showOk(t("auth.sessionRevoked")); void qc.invalidateQueries({ queryKey: ["auth-sessions"] }); }, onError: (err) => showError(String(err instanceof Error ? err.message : err)), }); const revokeOthersMut = useMutation({ mutationFn: () => apiPost<{ revoked: number }>("/v1/auth/sessions/revoke-others", {}), onSuccess: (data) => { showOk(t("auth.sessionsRevokedOthers", { count: data.revoked ?? 0 })); void qc.invalidateQueries({ queryKey: ["auth-sessions"] }); }, onError: (err) => showError(String(err instanceof Error ? err.message : err)), }); const items = sessionsQuery.data?.items || []; return (

{t("auth.sessionsTitle")}

{t("auth.sessionsHint")}

{sessionsQuery.isLoading ?

{t("common.refreshing")}

: null} {!items.length && !sessionsQuery.isLoading ? (

{t("auth.sessionsEmpty")}

) : ( {items.map((row) => ( ))}
{t("auth.colSession")} {t("auth.colIp")} {t("auth.colLastSeen")} {t("auth.colCreated")} {t("auth.actions")}
{row.id.slice(0, 10)}… {row.current ? ( {t("auth.sessionCurrent")} ) : null} {row.client_ip || "—"} {row.last_seen_at ? formatSystemTime(row.last_seen_at) : "—"} {row.created_at ? formatSystemTime(row.created_at) : "—"}
)}
); }