import { useState, useEffect, useCallback } from "react"; import { motion } from "framer-motion"; import { useAlert } from "../../context/AlertContext"; import ConfirmModal from "../ConfirmModal"; import useModalLock from "../../hooks/useModalLock"; import apiFetch from "../../utils/api"; import { formatSessionDate } from "../../utils/dashboardHelpers"; const API_BASE = "/api/admin"; interface DeviceInfo { icon?: string; browser?: string; os?: string; } interface Session { id: number | string; is_current: boolean; device_info?: DeviceInfo; ip_address: string; created_at: string; } interface DeleteModalState { isOpen: boolean; session: Session | null; } function getDeviceIcon(iconType?: string) { switch (iconType) { case "smartphone": return ( ); case "tablet": return ( ); default: return ( ); } } export default function DashSessions() { const alert = useAlert(); const [sessions, setSessions] = useState([]); const [sessionsLoading, setSessionsLoading] = useState(true); const [deleteModal, setDeleteModal] = useState({ isOpen: false, session: null, }); const [deleteAllModal, setDeleteAllModal] = useState(false); const [deleting, setDeleting] = useState(false); useModalLock(deleteAllModal); const fetchSessions = useCallback(async () => { try { const response = await apiFetch(`${API_BASE}/sessions`); const data = await response.json(); if (data.success) { setSessions( Array.isArray(data.data) ? data.data : data.data?.sessions || [], ); } } catch { // session fetch failed silently } finally { setSessionsLoading(false); } }, []); useEffect(() => { fetchSessions(); }, [fetchSessions]); const handleDeleteSession = async () => { if (!deleteModal.session) { return; } const sessionId = deleteModal.session.id; setDeleting(true); try { const response = await apiFetch(`${API_BASE}/sessions/${sessionId}`, { method: "DELETE", }); const data = await response.json(); if (data.success) { setDeleteModal({ isOpen: false, session: null }); setSessions((prev) => prev.filter((s) => s.id !== sessionId)); alert.success("Relace byla ukončena"); } else { alert.error(data.error || "Nepodařilo se ukončit relaci"); } } catch { alert.error("Chyba připojení"); } finally { setDeleting(false); } }; const handleDeleteAllSessions = async () => { setDeleting(true); try { const response = await apiFetch(`${API_BASE}/sessions?action=all`, { method: "DELETE", }); const data = await response.json(); if (data.success) { setDeleteAllModal(false); setSessions((prev) => prev.filter((s) => s.is_current)); alert.success(data.message || "Ostatní relace byly ukončeny"); } else { alert.error(data.error || "Nepodařilo se ukončit relace"); } } catch { alert.error("Chyba připojení"); } finally { setDeleting(false); } }; return ( <>

Přihlášená zařízení

{sessions.filter((s) => !s.is_current).length > 0 && ( )}
{sessionsLoading && (
{[0, 1, 2].map((i) => (
))}
)} {!sessionsLoading && sessions.length === 0 && (
Žádné aktivní relace
)} {!sessionsLoading && sessions.length > 0 && (
{sessions.map((session) => (
{getDeviceIcon(session.device_info?.icon)}
{session.device_info?.browser} na{" "} {session.device_info?.os} {session.is_current && ( Aktuální )}
{session.ip_address} | {formatSessionDate(session.created_at)}
{!session.is_current && ( )}
))}
)}
setDeleteModal({ isOpen: false, session: null })} onConfirm={handleDeleteSession} title="Ukončit relaci" message={`Opravdu chcete ukončit relaci na zařízení "${deleteModal.session?.device_info?.browser} na ${deleteModal.session?.device_info?.os}"? Toto zařízení bude odhlášeno.`} confirmText="Ukončit" cancelText="Zrušit" type="danger" loading={deleting} /> setDeleteAllModal(false)} onConfirm={handleDeleteAllSessions} title="Odhlásit ostatní zařízení" message="Opravdu chcete ukončit všechny ostatní relace? Budete odhlášeni ze všech zařízení kromě tohoto." confirmText="Odhlásit vše" cancelText="Zrušit" type="warning" loading={deleting} /> ); }