"use client";
import { useEffect, useState } from "react";
import { signOut, useSession } from "next-auth/react";
import {
SessionsLineChart,
CategoriesBarChart,
SentimentChart,
LanguagePieChart,
TokenUsageChart,
} from "../../components/Charts";
import DashboardSettings from "./settings";
import UserManagement from "./users";
import { Company, MetricsResult } from "../../lib/types";
interface MetricsCardProps {
label: string;
value: string | number | null | undefined;
className?: string;
}
interface StatCardProps {
label: string;
value: string | number | null | undefined;
description?: string;
icon?: string;
trend?: number;
trendLabel?: string;
}
function MetricsCard({ label, value, className = "" }: MetricsCardProps) {
return (
{value ?? "-"}
{label}
);
}
function StatCard({
label,
value,
description,
icon,
trend,
trendLabel,
}: StatCardProps) {
return (
{label}
{value ?? "-"}
{description && (
{description}
)}
{icon &&
{icon}
}
{trend !== undefined && (
= 0 ? "text-green-500" : "text-red-500"}`}
>
{trend >= 0 ? "↑" : "↓"} {Math.abs(trend).toFixed(1)}%
{trendLabel && (
{trendLabel}
)}
)}
);
}
// Safely wrapped component with useSession
function DashboardContent() {
const { data: session } = useSession();
const [metrics, setMetrics] = useState(null);
const [company, setCompany] = useState(null);
const [, setLoading] = useState(false);
// Remove unused csvUrl state variable
const [refreshing, setRefreshing] = useState(false);
const isAdmin = session?.user?.role === "admin";
const isAuditor = session?.user?.role === "auditor";
useEffect(() => {
// Fetch metrics and company on mount
const fetchData = async () => {
setLoading(true);
const res = await fetch("/api/dashboard/metrics");
const data = await res.json();
setMetrics(data.metrics);
setCompany(data.company);
// Removed unused csvUrl assignment
setLoading(false);
};
fetchData();
}, []);
async function handleRefresh() {
if (isAuditor) return; // Prevent auditors from refreshing
try {
setRefreshing(true);
// Make sure we have a company ID to send
if (!company?.id) {
console.error("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) {
// Refetch metrics
const metricsRes = await fetch("/api/dashboard/metrics");
const data = await metricsRes.json();
setMetrics(data.metrics);
} else {
const errorData = await res.json();
console.error("Failed to refresh sessions:", errorData.error);
}
} finally {
setRefreshing(false);
}
}
// Calculate sentiment distribution
const getSentimentData = () => {
if (!metrics) return { positive: 0, neutral: 0, negative: 0 };
// If we have the new sentiment count fields, use those
if (
metrics.sentimentPositiveCount !== undefined &&
metrics.sentimentNeutralCount !== undefined &&
metrics.sentimentNegativeCount !== undefined
) {
return {
positive: metrics.sentimentPositiveCount,
neutral: metrics.sentimentNeutralCount,
negative: metrics.sentimentNegativeCount,
};
}
// Fallback to estimating based on total
const total = metrics.totalSessions || 1;
return {
positive: Math.round(total * 0.6), // 60% positive as fallback
neutral: Math.round(total * 0.3), // 30% neutral as fallback
negative: Math.round(total * 0.1), // 10% negative as fallback
};
};
// Prepare token usage data
const getTokenData = () => {
if (!metrics || !metrics.tokensByDay) {
return { labels: [], values: [], costs: [] };
}
const days = Object.keys(metrics.tokensByDay).sort();
// Get the last 7 days if available
const labels = days.slice(-7);
const values = labels.map((day) => metrics.tokensByDay?.[day] || 0);
const costs = labels.map((day) => metrics.tokensCostByDay?.[day] || 0);
return { labels, values, costs };
};
if (!metrics || !company) {
return Loading dashboard...
;
}
return (
{/* Header with company info */}
{company.name}
Dashboard updated{" "}
{new Date(metrics.lastUpdated || Date.now()).toLocaleString()}
{refreshing ? "Refreshing..." : "Refresh Data"}
signOut()}
>
Sign Out
{/* Key Performance Metrics */}
{/* Sentiment & Escalation Metrics */}
{/* Charts Row */}
Sessions by Day
Categories
{/* Language & Token Usage */}
Languages
Token Usage & Costs
Total Tokens:{" "}
{metrics.totalTokens?.toLocaleString() || 0}
Total Cost:{" "}
€{metrics.totalTokensEur?.toFixed(4) || 0}
{/* Admin Controls */}
{isAdmin && (
<>
>
)}
);
}
// Our exported component
export default function DashboardPage() {
// We don't use useSession here to avoid the error outside the provider
return ;
}