auth-yes/ui/components/SessionsPage.tsx

128 lines
3.9 KiB
TypeScript

import { AuthenticatedLayout } from "./AuthenticatedLayout.tsx";
export const SessionsPage = ({
sessions,
currentSessionId,
isAdmin = false,
}: {
sessions: any[];
currentSessionId: string;
isAdmin?: boolean;
}) => {
return (
<AuthenticatedLayout
title="Active Sessions"
currentPath="/dashboard/sessions"
isAdmin={isAdmin}
>
<div class="card">
<h2>Active Sessions</h2>
<p
style={{
color: "#6c757d",
fontSize: "0.9rem",
marginBottom: "1.5rem",
}}
>
Review and revoke active sessions connected to your account.
</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>Status</th>
<th>Created At</th>
<th>Expires At</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{sessions.length === 0
? (
<tr>
<td
colSpan={4}
style={{ textAlign: "center", padding: "2rem" }}
>
No active sessions found.
</td>
</tr>
)
: (
sessions.map((session) => {
const isCurrent = session.id === currentSessionId;
return (
<tr key={session.id}>
<td>
{isCurrent
? (
<span class="badge badge-success">
Current Session
</span>
)
: <span class="badge badge-secondary">Active</span>}
</td>
<td>{new Date(session.created_at).toLocaleString()}</td>
<td>{new Date(session.expires_at).toLocaleString()}</td>
<td>
{!isCurrent && (
<button
type="button"
class="btn-danger revoke-btn"
data-session-id={session.id}
>
Revoke
</button>
)}
</td>
</tr>
);
})
)}
</tbody>
</table>
</div>
</div>
<script
dangerouslySetInnerHTML={{
__html: `
document.querySelectorAll('.revoke-btn').forEach(btn => {
btn.addEventListener('click', async (e) => {
if (!confirm('Are you sure you want to revoke this session?')) return;
const sessionId = e.target.getAttribute('data-session-id');
const originalText = e.target.textContent;
e.target.textContent = 'Revoking...';
e.target.disabled = true;
try {
const res = await fetch(\`/api/sessions/\${sessionId}\`, {
method: 'DELETE'
});
if (res.ok) {
// Reload the page to reflect changes
window.location.reload();
} else {
const data = await res.json();
alert(data.error || 'Failed to revoke session');
e.target.textContent = originalText;
e.target.disabled = false;
}
} catch (err) {
alert('An error occurred');
e.target.textContent = originalText;
e.target.disabled = false;
}
});
});
`,
}}
>
</script>
</AuthenticatedLayout>
);
};