admin / Synapse-Cortex
publicSelf Hosted ITSM Tool with RBAC/Tenanting and MFA
Synapse-Cortex / Synapse-Cortexv2 / frontend / src / pages / Dashboard.tsx
4670 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | import { useQuery } from '@tanstack/react-query' import { Link } from 'react-router-dom' import { dashboardApi } from '../api/dashboard' import { PriorityPill } from '../layout/Badges' import { usePageHeader } from '../layout/PageHeaderContext' import { useAuth } from '../auth/AuthContext' const CARDS: { key: keyof import('../api/types').QueueCounts; label: string; filter: string; borderClass: string; okLabel: string }[] = [ { key: 'unassigned', label: 'Unassigned', filter: 'unassigned', borderClass: 'border-t-accent-blue', okLabel: 'Needs Triage' }, { key: 'awaiting_user', label: 'Awaiting User', filter: 'awaiting_user', borderClass: 'border-t-accent-green', okLabel: 'Pending Requester' }, { key: 'sla_breaching', label: 'SLA Breaching', filter: 'sla_breaching', borderClass: 'border-t-accent-red', okLabel: 'All Clear' }, { key: 'resolved_today', label: 'Resolved Today', filter: 'resolved_today', borderClass: 'border-t-accent-orange', okLabel: 'Closed Loop' }, ] export function Dashboard() { const { user, aiEnabled } = useAuth() usePageHeader(`Command Overview // ${user?.tenant_name.toUpperCase()}`) const { data } = useQuery({ queryKey: ['dashboard'], queryFn: dashboardApi.get }) return ( <div> <div className="mb-4"> <span className={`text-[10px] px-2 py-0.5 rounded uppercase border ${ aiEnabled ? 'bg-accent-blue/15 text-accent-blue border-accent-blue/30' : 'bg-cortex-panel2 text-cortex-muted border-cortex-border' }`} > AI {aiEnabled ? 'Enabled' : 'Disabled'} </span> </div> <div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6"> {CARDS.map((c) => { const count = data?.queue[c.key] ?? 0 const breaching = c.key === 'sla_breaching' && count > 0 return ( <Link key={c.key} to={`/tickets?filter=${c.filter}`} className={`block border border-cortex-border bg-cortex-panel rounded-md p-4 border-t-2 ${c.borderClass} hover:bg-cortex-panel2 transition ${breaching ? 'shadow-[0_0_24px_rgba(255,59,92,0.25)]' : ''}`} > <div className="text-[10px] text-cortex-muted uppercase tracking-wider">{c.label}</div> <div className={`text-3xl font-display font-bold mt-1 ${breaching ? 'text-accent-red animate-pulse' : 'text-chrome'}`}> {count} </div> <div className={`text-[10px] uppercase tracking-wider mt-1 ${breaching ? 'text-accent-red' : 'text-cortex-muted'}`}> {breaching ? '▲ Action Required' : c.okLabel} </div> </Link> ) })} </div> <div className="border border-cortex-border bg-cortex-panel rounded-md"> <div className="flex items-center justify-between px-4 py-3 border-b border-cortex-border"> <div className="text-[10px] text-cortex-muted uppercase tracking-wider">Recent Tickets</div> <Link to="/tickets" className="text-xs text-accent-blue hover:text-accent-blue-bright"> View all → </Link> </div> <table className="w-full text-sm"> <thead> <tr className="text-left text-[10px] text-cortex-muted uppercase tracking-wider border-b border-cortex-border"> <th className="px-4 py-2">Ticket</th> <th className="px-4 py-2">Title</th> <th className="px-4 py-2">Priority</th> <th className="px-4 py-2">Status</th> </tr> </thead> <tbody> {(data?.tickets ?? []).map((t) => ( <tr key={t.id} className="border-b border-cortex-border/60 hover:bg-cortex-panel2"> <td className="px-4 py-2"> <Link to={`/tickets/${t.id}`} className="text-accent-blue hover:text-accent-blue-bright hover:underline"> {t.ticket_number} </Link> </td> <td className="px-4 py-2">{t.title}</td> <td className="px-4 py-2"> <PriorityPill priority={t.priority} /> </td> <td className="px-4 py-2 text-xs uppercase text-chrome">{t.status.replace('_', ' ')}</td> </tr> ))} {data && data.tickets.length === 0 && ( <tr> <td colSpan={4} className="px-4 py-6 text-center text-cortex-muted text-xs"> No tickets logged yet. </td> </tr> )} </tbody> </table> </div> </div> ) } |