30 lines
1.6 KiB
TypeScript
30 lines
1.6 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
type Session = { id: string; device_name: string | null; created_at: string; expires_at: string; current: boolean };
|
|
|
|
export function SessionManager() {
|
|
const [sessions, setSessions] = useState<Session[]>([]);
|
|
const [message, setMessage] = useState('');
|
|
|
|
async function load() {
|
|
const response = await fetch('/api/account/sessions', { cache: 'no-store' });
|
|
if (response.ok) setSessions(await response.json());
|
|
}
|
|
|
|
useEffect(() => { void load(); }, []);
|
|
|
|
async function revoke(id: string) {
|
|
const response = await fetch(`/api/account/sessions/${id}`, { method: 'DELETE' });
|
|
if (response.ok) { setMessage('Session revoked.'); await load(); }
|
|
}
|
|
|
|
async function logoutAll() {
|
|
const response = await fetch('/api/account/sessions', { method: 'POST' });
|
|
if (response.ok) { setMessage('All sessions revoked.'); await load(); }
|
|
}
|
|
|
|
return <div className="session-manager"><div className="session-heading"><h2>Sessions</h2><button className="text-button" onClick={() => void logoutAll()} type="button">Revoke all</button></div>{sessions.length ? sessions.map((session) => <div className="session-row" key={session.id}><div><strong>{session.device_name ?? 'Unnamed device'} {session.current && '(this device)'}</strong><p>Created {new Date(session.created_at).toLocaleString()}</p></div>{!session.current && <button className="text-button" onClick={() => void revoke(session.id)} type="button">Revoke</button>}</div>) : <p>No active sessions.</p>}{message && <p className="form-message" role="status">{message}</p>}</div>;
|
|
}
|