import { Badge, Container, Group, Stack, Text, Paper, TextInput, Select, Avatar, Box, Divider, Pagination, Center, Tooltip, } from '@mantine/core' import { useState, useMemo, useEffect } from 'react' import { createFileRoute } from '@tanstack/react-router' import { TbSearch, TbClock, TbCheck, TbX } from 'react-icons/tb' import { DashboardLayout } from '@/frontend/components/DashboardLayout' import useSWR from 'swr' import { API_URLS } from '../config/api' export const Route = createFileRoute('/logs')({ component: GlobalLogsPage, }) const fetcher = (url: string) => fetch(url).then((res) => res.json()) const typeConfig: Record = { CREATE: { color: 'blue', icon: TbCheck }, UPDATE: { color: 'teal', icon: TbCheck }, DELETE: { color: 'red', icon: TbX }, LOGIN: { color: 'green', icon: TbClock }, LOGOUT: { color: 'orange', icon: TbClock }, } const getRoleColor = (role: string) => { const r = (role || '').toLowerCase() if (r.includes('super')) return 'red' if (r.includes('admin')) return 'brand-blue' if (r.includes('developer')) return 'violet' return 'gray' } function groupLogsByDate(logs: any[]) { const groups: Record = {} const today = new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' }).toUpperCase() const yesterday = new Date(Date.now() - 86400000).toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' }).toUpperCase() logs.forEach(log => { const dateObj = new Date(log.createdAt) let dateStr = dateObj.toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' }).toUpperCase() if (dateStr === today) dateStr = 'TODAY' else if (dateStr === yesterday) dateStr = 'YESTERDAY' if (!groups[dateStr]) groups[dateStr] = [] const timeStr = dateObj.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }) groups[dateStr].push({ id: log.id, time: timeStr, user: log.user, type: log.type, content: log.message, color: log.user ? getRoleColor(log.user.role) : 'gray', icon: typeConfig[log.type as string]?.icon }) }) // We want to keep the order as they came from the API (sorted by createdAt desc) // but grouped by date. Object.entries might mess up the order if dates are not sequential. // However, since the source logs are sorted, the first encounter of a date defines the group order. const result: { date: string; logs: any[] }[] = [] const seenDates = new Set() logs.forEach(log => { const dateObj = new Date(log.createdAt) let dateStr = dateObj.toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' }).toUpperCase() if (dateStr === today) dateStr = 'TODAY' else if (dateStr === yesterday) dateStr = 'YESTERDAY' if (!seenDates.has(dateStr)) { result.push({ date: dateStr, logs: groups[dateStr] }) seenDates.add(dateStr) } }) return result } function GlobalLogsPage() { const [search, setSearch] = useState('') const [debouncedSearch, setDebouncedSearch] = useState('') const [logType, setLogType] = useState('all') const [operatorId, setOperatorId] = useState('all') const [page, setPage] = useState(1) useEffect(() => { const timer = setTimeout(() => setDebouncedSearch(search), 300) return () => clearTimeout(timer) }, [search]) const { data: operatorsData } = useSWR(API_URLS.getLogOperators(), fetcher) const operatorOptions = useMemo(() => { if (!operatorsData || !Array.isArray(operatorsData)) return [{ value: 'all', label: 'All Operators' }] return [ { value: 'all', label: 'All Operators' }, ...operatorsData.map((op: any) => ({ value: op.id, label: op.name })) ] }, [operatorsData]) const { data: response, isLoading } = useSWR( API_URLS.getGlobalLogs(page, debouncedSearch, logType || 'all', operatorId || 'all'), fetcher ) const filteredTimeline = useMemo(() => { if (!response?.data) return [] return groupLogsByDate(response.data) }, [response?.data]) return ( {/* Header Controls */} } radius="md" w={250} value={search} onChange={(e) => { setSearch(e.currentTarget.value) setPage(1) }} /> { setOperatorId(val) setPage(1) }} /> {/* Timeline Content */} {isLoading ? (
Loading logs...
) : filteredTimeline.length === 0 ? ( No logs found matching your filters. ) : ( <> {filteredTimeline.map((group, groupIndex) => ( 0 ? "xl" : 0} mb="md" style={{ textTransform: 'uppercase' }} > {group.date} {group.logs.map((log, logIndex) => { const isLastLog = logIndex === group.logs.length - 1; return ( {/* Left: Time */} {log.time} {/* Middle: Line & Avatar */} {/* Vertical Line */} {!isLastLog && ( )} {/* Avatar */} {log.icon ? : (log.user?.name?.charAt(0) || '?')} {/* Right: Content */} {log.user?.name || 'Unknown'} {log.content} ) })} {groupIndex < filteredTimeline.length - 1 && ( )} ))} {response?.totalPages > 1 && (
)} )}
) }