- Update `DELETE /api/sessions/:id` in `server/routes/sessions.ts` to allow event creators to delete guests' sessions. - Update page hierarchy and top headings in `ui/components/SessionsPage.tsx`. - Refactor `EventAttendeesDrawer.tsx` to `EventGuestsDrawer.tsx` as a fixed slide-over overlay. - Add multi-event compact view toggle with `localStorage` persistence in `EventCockpitDeck.tsx`. - Standardize dynamic countdown pills across `EventCockpitDeck.tsx`, `EventGuestsDrawer.tsx`, `SessionDeck.tsx`, and `SessionTable.tsx`. - Optimize mobile session deck in `SessionDeck.tsx`. Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
123 lines
5.0 KiB
TypeScript
123 lines
5.0 KiB
TypeScript
export const SessionDeck = (
|
||
{ sessions, currentSessionId }: {
|
||
sessions: any[];
|
||
currentSessionId: string;
|
||
},
|
||
) => {
|
||
return (
|
||
<div
|
||
class="mobile-only"
|
||
style="display: flex; flex-direction: column; gap: 1rem;"
|
||
>
|
||
{sessions.length === 0
|
||
? (
|
||
<div class="card" style="text-align: center; padding: 2rem;">
|
||
<p style="color: var(--text-muted); margin: 0;">
|
||
No active sessions found.
|
||
</p>
|
||
</div>
|
||
)
|
||
: (
|
||
sessions.map((session) => {
|
||
const isCurrent = session.id === currentSessionId;
|
||
const isAgent = !!session.is_agent;
|
||
const scopes = Array.isArray(session.custom_scopes)
|
||
? session.custom_scopes
|
||
: [];
|
||
|
||
return (
|
||
<div class="card" key={session.id} style="margin-bottom: 0;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem;">
|
||
<div style="display: flex; align-items: center; gap: 0.65rem;">
|
||
<div style="display: flex; align-items: center; justify-content: center; width: 38px; height: 38px; background: var(--surface-muted); border-radius: var(--radius-md); font-size: 1.2rem;">
|
||
{isAgent ? "🔑" : isCurrent ? "📱" : "💻"}
|
||
</div>
|
||
<div>
|
||
<div style="font-weight: 700; font-size: 0.95rem; color: var(--text-primary);">
|
||
{session.label ||
|
||
(isCurrent ? "This Device" : "Remote Device")}
|
||
</div>
|
||
<div style="font-size: 0.75rem; color: var(--text-muted); font-family: monospace;">
|
||
{session.id.substring(0, 14)}...
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
||
{isAgent
|
||
? <span class="badge badge-info">Delegated</span>
|
||
: isCurrent
|
||
? <span class="badge badge-success">Active Now</span>
|
||
: <span class="badge badge-secondary">Active</span>}
|
||
{!isCurrent && (
|
||
<button
|
||
type="button"
|
||
class="btn-danger revoke-btn"
|
||
data-session-id={session.id}
|
||
style="padding: 0.15rem 0.5rem; font-size: 0.75rem; min-height: 24px; border-radius: var(--radius-sm);"
|
||
>
|
||
🗑️ Revoke
|
||
</button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<div style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 1rem; line-height: 1.6;">
|
||
{isAgent && (
|
||
<div style="margin-bottom: 0.25rem;">
|
||
<strong>Scopes:</strong>{" "}
|
||
{scopes.length > 0 ? scopes.join(", ") : "Delegated"}
|
||
</div>
|
||
)}
|
||
<div>
|
||
<span
|
||
class="countdown-pill"
|
||
data-expires-at={session.expires_at}
|
||
>
|
||
⏳ 0h 0m left · (Expires{" "}
|
||
{new Date(session.expires_at).toLocaleTimeString([], {
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
})})
|
||
</span>
|
||
</div>
|
||
{session.last_activity_action && (
|
||
<div>
|
||
<strong>Last Active:</strong>{" "}
|
||
{session.last_activity_action} ({new Date(
|
||
session.last_activity_at || session.created_at,
|
||
).toLocaleTimeString()})
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{isAgent && (
|
||
<div style="display: flex; gap: 0.5rem;">
|
||
<button
|
||
type="button"
|
||
class="btn-outline"
|
||
style="flex: 1; justify-content: center; min-height: 38px; font-size: 0.8rem;"
|
||
onclick={`extendSession('${session.id}', 1)`}
|
||
>
|
||
+1h Extend
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="btn-outline"
|
||
style="flex: 1; justify-content: center; min-height: 38px; font-size: 0.8rem;"
|
||
onclick={`openEditScopesModal('${session.id}', '${
|
||
session.label || "Delegated"
|
||
}', ${JSON.stringify(JSON.stringify(scopes))})`}
|
||
>
|
||
Scopes
|
||
</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
})
|
||
)}
|
||
</div>
|
||
);
|
||
};
|