"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"; // Safely wrapped component with useSession 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 refreshStatusId = useId(); const isAuditor = session?.user?.role === "AUDITOR"; // 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 if (status === "loading") { return (

Loading session...

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

Redirecting to login...

); } if (loading || !metrics || !company) { 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 preparation functions const getSentimentData = () => { 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))", }, ]; }; const getSessionsOverTimeData = () => { 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, })); }; const getCategoriesData = () => { 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, }; }); }; const getLanguagesData = () => { if (!metrics?.languages) return []; return Object.entries(metrics.languages).map(([name, value]) => ({ name, value: value as number, })); }; const getWordCloudData = (): WordCloudWord[] => { if (!metrics?.wordCloudData) return []; return metrics.wordCloudData; }; const getCountryData = () => { if (!metrics?.countries) return {}; return Object.entries(metrics.countries).reduce( (acc, [code, count]) => { if (code && count) { acc[code] = count; } return acc; }, {} as Record ); }; const getResponseTimeData = () => { 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; }; return (
{/* Modern Header */}

{company.name}

Analytics Dashboard

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

{refreshing && (
Dashboard data is being refreshed
)} signOut({ callbackUrl: "/login" })} >
{/* Date Range Picker - Temporarily disabled to debug infinite loop */} {/* {dateRange && ( )} */} {/* Modern Metrics Grid */}
} trend={{ value: metrics.sessionTrend ?? 0, isPositive: (metrics.sessionTrend ?? 0) >= 0, }} variant="primary" /> } trend={{ value: metrics.usersTrend ?? 0, isPositive: (metrics.usersTrend ?? 0) >= 0, }} variant="success" /> } trend={{ value: metrics.avgSessionTimeTrend ?? 0, isPositive: (metrics.avgSessionTimeTrend ?? 0) >= 0, }} /> } trend={{ value: metrics.avgResponseTimeTrend ?? 0, isPositive: (metrics.avgResponseTimeTrend ?? 0) <= 0, }} variant="warning" /> } description="Average per day" /> } description="Busiest hour" /> } trend={{ value: metrics.resolvedChatsPercentage ?? 0, isPositive: (metrics.resolvedChatsPercentage ?? 0) >= 80, }} variant={ metrics.resolvedChatsPercentage && metrics.resolvedChatsPercentage >= 80 ? "success" : "warning" } /> } description="Languages detected" />
{/* 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 ; }