wulf-pulse/app/kiosk/page.tsx
lorentz 3c13defacb feat: kiosk UI updates - new cards, gauge chart, performance improvements
- Updated kiosk stats API with expanded metrics
- New components: company-tickets-card, gauge-chart, service-desk-card, ticket-leaders-card
- Updated cycling-display, kpi-card, and ticker components
- Added performance.css for kiosk optimizations
- Added Wulf logo asset
2026-02-19 15:31:23 -05:00

173 lines
4.2 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { CyclingDisplay } from '@/components/kiosk/cycling-display';
import { Ticker } from '@/components/kiosk/ticker';
import { SettingsPanel } from '@/components/kiosk/settings-panel';
import './performance.css';
interface TicketItem {
ticketNumber: string;
title: string;
companyName: string;
}
interface ResourceLeader {
resourceName: string;
ticketsClosed: number;
}
interface IssueType {
issueType: string;
subIssueType: string;
count: number;
}
interface KpiStats {
criticalTickets: number;
topCritical?: TicketItem[];
waitingTickets: number;
topWaiting?: TicketItem[];
staleTickets: number;
topStale?: TicketItem[];
overdueTickets: number;
topOverdue?: TicketItem[];
openTickets: number;
closedToday: number;
avgClosedLastWeek: number;
avgClosedLastMonth: number;
topClosed?: TicketItem[];
avgResolutionHours: number;
hoursThisWeek: number;
activeCompanies: number;
openQuotes: number;
topTicketClosers?: ResourceLeader[];
topManagedIssueTypes?: IssueType[];
nmsCoverage: number;
rmmCoverage: number;
serviceDeskManaged: number;
serviceDeskTM: number;
serviceDeskVendor: number;
hynesTickets: number;
seubertTickets: number;
universalTickets: number;
}
interface TickerActivity {
ticketNumber: string;
title: string;
priority: number;
statusLabel: string;
companyName: string;
}
export default function KioskPage() {
const [stats, setStats] = useState<KpiStats>({
criticalTickets: 0,
waitingTickets: 0,
staleTickets: 0,
overdueTickets: 0,
openTickets: 0,
closedToday: 0,
avgClosedLastWeek: 0,
avgClosedLastMonth: 0,
avgResolutionHours: 0,
hoursThisWeek: 0,
activeCompanies: 0,
openQuotes: 0,
nmsCoverage: 0,
rmmCoverage: 0,
serviceDeskManaged: 0,
serviceDeskTM: 0,
serviceDeskVendor: 0,
hynesTickets: 0,
seubertTickets: 0,
universalTickets: 0,
});
const [activities, setActivities] = useState<TickerActivity[]>([]);
const [cycleInterval, setCycleInterval] = useState(7);
const [tickerSpeed, setTickerSpeed] = useState(30);
useEffect(() => {
// Fetch settings from API
const fetchSettings = async () => {
try {
const res = await fetch('/api/kiosk/settings');
if (res.ok) {
const data = await res.json();
if (data.cycle_interval) {
setCycleInterval(data.cycle_interval);
}
if (data.ticker_speed) {
setTickerSpeed(data.ticker_speed);
}
}
} catch (error) {
console.error('Error fetching kiosk settings:', error);
}
};
fetchSettings();
}, []);
useEffect(() => {
// Fetch initial data
fetchData();
// Set up auto-refresh every 60 seconds
const statsInterval = setInterval(fetchData, 60000);
return () => {
clearInterval(statsInterval);
};
}, []);
const fetchData = async () => {
try {
// Fetch stats
const statsRes = await fetch('/api/kiosk/stats');
if (statsRes.ok) {
const statsData = await statsRes.json();
setStats(statsData);
}
// Fetch activity feed
const activityRes = await fetch('/api/kiosk/activity');
if (activityRes.ok) {
const activityData = await activityRes.json();
setActivities(activityData.activities || []);
}
} catch (error) {
console.error('Error fetching kiosk data:', error);
}
};
return (
<div className="h-screen w-screen bg-black text-white overflow-hidden flex flex-col">
{/* Settings Panel */}
<SettingsPanel
cycleInterval={cycleInterval}
onIntervalChange={setCycleInterval}
tickerSpeed={tickerSpeed}
onTickerSpeedChange={setTickerSpeed}
/>
{/* Main Content Area */}
<CyclingDisplay stats={stats} cycleInterval={cycleInterval} />
{/* Ticker */}
<Ticker activities={activities} speed={tickerSpeed} />
{/* Custom CSS for ticker animation */}
<style jsx global>{`
@keyframes ticker {
0% {
transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(-50%, 0, 0);
}
}
`}</style>
</div>
);
}