"use client"; import { CheckCircle, Clock, Euro, Globe, LogOut, MessageCircle, MessageSquare, MoreVertical, RefreshCw, TrendingUp, Users, Zap, } from "lucide-react"; import { useRouter } from "next/navigation"; import { signOut, useSession } from "next-auth/react"; import { useCallback, useEffect, useId, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Skeleton } from "@/components/ui/skeleton"; import { formatEnumValue } from "@/lib/format-enums"; import ModernBarChart from "../../../components/charts/bar-chart"; import ModernDonutChart from "../../../components/charts/donut-chart"; import ModernLineChart from "../../../components/charts/line-chart"; import GeographicMap from "../../../components/GeographicMap"; import ResponseTimeDistribution from "../../../components/ResponseTimeDistribution"; import TopQuestionsChart from "../../../components/TopQuestionsChart"; import MetricCard from "../../../components/ui/metric-card"; import WordCloud from "../../../components/WordCloud"; import type { Company, MetricsResult, WordCloudWord } from "../../../lib/types"; /** * Loading states component for better organization */ function DashboardLoadingStates({ status }: { status: string }) { if (status === "loading") { return (

Loading session...

); } if (status === "unauthenticated") { return (

Redirecting to login...

); } return null; } /** * Loading skeleton component */ function DashboardSkeleton() { return (
{/* Header Skeleton */}
{/* Metrics Grid Skeleton */}
{Array.from({ length: 8 }, (_, i) => { const metricTypes = [ "sessions", "users", "time", "response", "costs", "peak", "resolution", "languages", ]; return ( ); })}
{/* Charts Skeleton */}
); } /** * Data processing utilities */ function useDashboardData(metrics: MetricsResult | null) { const getSentimentData = useCallback(() => { if (!metrics) return []; const sentimentData = { positive: metrics.sentimentPositiveCount ?? 0, neutral: metrics.sentimentNeutralCount ?? 0, negative: metrics.sentimentNegativeCount ?? 0, }; return [ { name: "Positive", value: sentimentData.positive, color: "hsl(var(--chart-1))", }, { name: "Neutral", value: sentimentData.neutral, color: "hsl(var(--chart-2))", }, { name: "Negative", value: sentimentData.negative, color: "hsl(var(--chart-3))", }, ]; }, [metrics]); const getSessionsOverTimeData = useCallback(() => { if (!metrics?.days) return []; return Object.entries(metrics.days).map(([date, value]) => ({ date: new Date(date).toLocaleDateString("en-US", { month: "short", day: "numeric", }), value: value as number, })); }, [metrics?.days]); const getCategoriesData = useCallback(() => { if (!metrics?.categories) return []; return Object.entries(metrics.categories).map(([name, value]) => { const formattedName = formatEnumValue(name) || name; return { name: formattedName.length > 15 ? `${formattedName.substring(0, 15)}...` : formattedName, value: value as number, }; }); }, [metrics?.categories]); const getLanguagesData = useCallback(() => { if (!metrics?.languages) return []; return Object.entries(metrics.languages).map(([name, value]) => ({ name, value: value as number, })); }, [metrics?.languages]); const getWordCloudData = useCallback((): WordCloudWord[] => { if (!metrics?.wordCloudData) return []; return metrics.wordCloudData; }, [metrics?.wordCloudData]); const getCountryData = useCallback(() => { if (!metrics?.countries) return {}; return Object.entries(metrics.countries).reduce( (acc, [code, count]) => { if (code && count) { acc[code] = count; } return acc; }, {} as Record ); }, [metrics?.countries]); const getResponseTimeData = useCallback(() => { const avgTime = metrics?.avgResponseTime || 1.5; const simulatedData: number[] = []; for (let i = 0; i < 50; i++) { const randomFactor = 0.5 + Math.random(); simulatedData.push(avgTime * randomFactor); } return simulatedData; }, [metrics?.avgResponseTime]); return { getSentimentData, getSessionsOverTimeData, getCategoriesData, getLanguagesData, getWordCloudData, getCountryData, getResponseTimeData, }; } /** * Dashboard header component */ function DashboardHeader({ company, metrics, isAuditor, refreshing, onRefresh, }: { company: Company; metrics: MetricsResult; isAuditor: boolean; refreshing: boolean; onRefresh: () => void; }) { const refreshStatusId = useId(); return (

{company.name}

Analytics Dashboard

Last updated{" "} {new Date(metrics.lastUpdated || Date.now()).toLocaleString()}

{refreshing && (
Dashboard data is being refreshed
)} signOut({ callbackUrl: "/login" })} >
); } /** * Individual metric card components for better organization */ function SessionMetricCard({ metrics }: { metrics: MetricsResult }) { return ( } trend={{ value: metrics.sessionTrend ?? 0, isPositive: (metrics.sessionTrend ?? 0) >= 0, }} variant="primary" /> ); } function UsersMetricCard({ metrics }: { metrics: MetricsResult }) { return ( } trend={{ value: metrics.usersTrend ?? 0, isPositive: (metrics.usersTrend ?? 0) >= 0, }} variant="success" /> ); } function SessionTimeMetricCard({ metrics }: { metrics: MetricsResult }) { return ( } trend={{ value: metrics.avgSessionTimeTrend ?? 0, isPositive: (metrics.avgSessionTimeTrend ?? 0) >= 0, }} /> ); } function ResponseTimeMetricCard({ metrics }: { metrics: MetricsResult }) { return ( } trend={{ value: metrics.avgResponseTimeTrend ?? 0, isPositive: (metrics.avgResponseTimeTrend ?? 0) <= 0, }} variant="warning" /> ); } function CostsMetricCard({ metrics }: { metrics: MetricsResult }) { return ( } description="Average per day" /> ); } function PeakUsageMetricCard({ metrics }: { metrics: MetricsResult }) { return ( } description="Busiest hour" /> ); } function ResolutionRateMetricCard({ metrics }: { metrics: MetricsResult }) { return ( } trend={{ value: metrics.resolvedChatsPercentage ?? 0, isPositive: (metrics.resolvedChatsPercentage ?? 0) >= 80, }} variant={ metrics.resolvedChatsPercentage && metrics.resolvedChatsPercentage >= 80 ? "success" : "warning" } /> ); } function LanguagesMetricCard({ metrics }: { metrics: MetricsResult }) { return ( } description="Languages detected" /> ); } /** * Simplified metrics grid component */ function MetricsGrid({ metrics }: { metrics: MetricsResult }) { return (
); } /** * Main dashboard content with reduced complexity */ function DashboardContent() { const { data: session, status } = useSession(); const router = useRouter(); const [metrics, setMetrics] = useState(null); const [company, setCompany] = useState(null); const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); const [isInitialLoad, setIsInitialLoad] = useState(true); const isAuditor = session?.user?.role === "AUDITOR"; const dataHelpers = useDashboardData(metrics); // Function to fetch metrics with optional date range const fetchMetrics = useCallback( async (startDate?: string, endDate?: string, isInitial = false) => { setLoading(true); try { let url = "/api/dashboard/metrics"; if (startDate && endDate) { url += `?startDate=${startDate}&endDate=${endDate}`; } const res = await fetch(url); const data = await res.json(); setMetrics(data.metrics); setCompany(data.company); // Set initial load flag if (isInitial) { setIsInitialLoad(false); } } catch (error) { console.error("Error fetching metrics:", error); } finally { setLoading(false); } }, [] ); useEffect(() => { // Redirect if not authenticated if (status === "unauthenticated") { router.push("/login"); return; } // Fetch metrics and company on mount if authenticated if (status === "authenticated" && isInitialLoad) { fetchMetrics(undefined, undefined, true); } }, [status, router, isInitialLoad, fetchMetrics]); async function handleRefresh() { if (isAuditor) return; try { setRefreshing(true); if (!company?.id) { setRefreshing(false); alert("Cannot refresh: Company ID is missing"); return; } const res = await fetch("/api/admin/refresh-sessions", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ companyId: company.id }), }); if (res.ok) { const metricsRes = await fetch("/api/dashboard/metrics"); const data = await metricsRes.json(); setMetrics(data.metrics); } else { const errorData = await res.json(); alert(`Failed to refresh sessions: ${errorData.error}`); } } finally { setRefreshing(false); } } // Show loading state while session status is being determined const loadingState = DashboardLoadingStates({ status }); if (loadingState) return loadingState; if (loading || !metrics || !company) { return ; } return (
{/* Date Range Picker */} {/* {dateRange && ( )} */} {/* Charts Section */}
{/* Geographic and Topics Section */}
Geographic Distribution Common Topics
{/* Top Questions Chart */} {/* Response Time Distribution */} Response Time Distribution {/* Token Usage Summary */}
AI Usage & Costs
Total Tokens: {metrics.totalTokens?.toLocaleString() || 0} Total Cost:€ {metrics.totalTokensEur?.toFixed(4) || 0}

Token usage chart will be implemented with historical data

); } export default function DashboardPage() { return ; }