'use client'; import {proportional, pixel} from '@astryxdesign/core/Table'; import type {TableColumn} from '@astryxdesign/core/Table'; import {HStack} from '@astryxdesign/core/Layout'; import {Text} from '@astryxdesign/core/Text'; import {Badge} from '@astryxdesign/core/Badge'; import {Button} from '@astryxdesign/core/Button'; import {useToast} from '@astryxdesign/core/Toast'; import {ResponsiveTable} from '@/shared/components/patterns/ResponsiveTable'; import {PanelCard} from '@/shared/components/patterns/PanelCard'; import {SkeletonRows} from '@/shared/components/patterns/LoadingState'; import {EmptyPanel} from '@/shared/components/patterns/EmptyPanel'; import {useResource} from '@/shared/hooks/useResource'; import {securityRepository} from '@/features/settings/repositories/securityRepository'; import type {DeviceSession} from '@/features/settings/services/mapSession'; /** * Every device signed in as the current person, from GET /api/auth/sessions. * * Shared by the merchant Security settings and the platform console's * Settings: `/api/auth/*` is scoped to whoever is signed in, not to a tenant, * so it answers for a platform admin exactly as it does for a merchant. One * component so the two consoles cannot drift on what "sign out" means. * * Nothing optimistic: after a revoke the list is re-read, because the platform * decides what is still live. */ export function ActiveSessionsPanel() { const toast = useToast(); const sessions = useResource(securityRepository.sessions()); const handleRevokeSession = async (id: string) => { const res = await securityRepository.revoke(id); if (!res.ok) { toast({type: 'error', body: res.message ?? 'Could not sign that device out.'}); return; } sessions.refetch(); toast({body: 'That device has been signed out.'}); }; const handleRevokeAllOther = async () => { const res = await securityRepository.revokeOthers(); if (!res.ok) { toast({type: 'error', body: res.message ?? 'Could not sign the other devices out.'}); return; } sessions.refetch(); toast({body: 'Every other device has been signed out.'}); }; const columns: TableColumn>[] = [ { key: 'device', header: 'Device', width: proportional(2), renderCell: (row) => ( {row.device} {row.isCurrent ? : null} ), }, { key: 'lastUsedAt', header: 'Last used', width: proportional(1.2), renderCell: (row) => ( {new Date(row.lastUsedAt).toLocaleString()} ), }, { key: 'actions', header: 'Action', align: 'center', width: pixel(120), // The current session CAN be revoked upstream, but doing it from this // table signs you out mid-page with no warning. Signing out is what the // account menu is for. renderCell: (row) => row.isCurrent ? null : (