Revamp admin dashboard analytics and UI

Refactored the admin dashboard to use tabbed navigation for analytics and management. Enhanced AdminAnalytics with Recharts visualizations, added top vendors by revenue, and improved chart tooltips. Removed unused columns from vendor table. Updated layout and notification context to exclude admin pages from dashboard-specific UI and notifications. Minor debug logging added to SystemStatusCard.
This commit is contained in:
g
2025-11-28 19:08:40 +00:00
parent f212859bda
commit 4b0bd2cf8c
8 changed files with 337 additions and 219 deletions

View File

@@ -8,6 +8,9 @@ import { Button } from "@/components/ui/button";
import { AlertCircle, BarChart, RefreshCw, Users, ShoppingCart,
TrendingUp, TrendingDown, DollarSign, Package } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { fetchClient } from "@/lib/api-client";
import { BarChart as RechartsBarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line, ComposedChart } from 'recharts';
import { formatGBP } from "@/utils/format";
// API response data structure
interface AnalyticsData {
@@ -18,7 +21,14 @@ interface AnalyticsData {
activeToday?: number;
active?: number;
stores?: number;
activeStores?: number;
dailyGrowth?: { date: string; count: number }[];
topVendors?: Array<{
vendorId: string;
vendorName: string;
totalRevenue: number;
orderCount: number;
}>;
};
orders?: {
total?: number;
@@ -35,8 +45,9 @@ interface AnalyticsData {
dailyRevenue?: { date: string; amount: number }[];
};
engagement?: {
totalMessages?: number;
totalChats?: number;
activeChats?: number;
totalMessages?: number;
dailyMessages?: { date: string; count: number }[];
};
products?: {
@@ -59,29 +70,36 @@ export default function AdminAnalytics() {
const [dateRange, setDateRange] = useState("7days");
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [refreshing, setRefreshing] = useState(false);
const [showDebug, setShowDebug] = useState(false);
const fetchAnalyticsData = async () => {
try {
setLoading(true);
setErrorMessage(null);
const token = document.cookie
.split("; ")
.find((row) => row.startsWith("Authorization="))
?.split("=")[1];
const response = await fetch(`/api/admin/analytics?range=${dateRange}`, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
const data = await fetchClient<AnalyticsData>(`/admin/analytics?range=${dateRange}`);
console.log('=== ADMIN ANALYTICS DATA ===');
console.log('Date Range:', dateRange);
console.log('Full Response:', JSON.stringify(data, null, 2));
console.log('Orders:', {
total: data?.orders?.total,
dailyOrders: data?.orders?.dailyOrders,
dailyOrdersLength: data?.orders?.dailyOrders?.length,
sample: data?.orders?.dailyOrders?.slice(0, 3)
});
if (!response.ok) {
throw new Error("Failed to fetch analytics data");
}
const data = await response.json();
console.log('Revenue:', {
total: data?.revenue?.total,
dailyRevenue: data?.revenue?.dailyRevenue,
dailyRevenueLength: data?.revenue?.dailyRevenue?.length,
sample: data?.revenue?.dailyRevenue?.slice(0, 3)
});
console.log('Vendors:', {
total: data?.vendors?.total,
dailyGrowth: data?.vendors?.dailyGrowth,
dailyGrowthLength: data?.vendors?.dailyGrowth?.length,
sample: data?.vendors?.dailyGrowth?.slice(0, 3)
});
console.log('===========================');
setAnalyticsData(data);
} catch (error) {
console.error("Error fetching analytics data:", error);
@@ -109,64 +127,89 @@ export default function AdminAnalytics() {
);
}
// Chart component for line/area charts
const Chart = ({ data, valueKey = "count", color = "#3b82f6", height = 200 }:
{ data: any[]; valueKey?: string; color?: string; height?: number }) => {
if (!data || data.length === 0) {
// Helper to transform data for recharts
const transformChartData = (data: Array<{ date: string; [key: string]: any }>, valueKey: string = "count") => {
if (!data || data.length === 0) return [];
return data.map(item => {
const dateStr = item.date;
// Parse YYYY-MM-DD format
const parts = dateStr.split('-');
const date = parts.length === 3
? new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]))
: new Date(dateStr);
return {
date: dateStr,
formattedDate: date.toLocaleDateString('en-GB', { month: 'short', day: 'numeric' }),
value: Number(item[valueKey]) || 0,
[valueKey]: Number(item[valueKey]) || 0
};
});
};
// Helper to combine orders and revenue data for dual-axis chart
const combineOrdersAndRevenue = (orders: Array<{ date: string; count: number }>, revenue: Array<{ date: string; amount: number }>) => {
if (!orders || orders.length === 0) return [];
// Create a map of revenue by date for quick lookup
const revenueMap = new Map<string, number>();
if (revenue && revenue.length > 0) {
revenue.forEach(r => {
revenueMap.set(r.date, r.amount || 0);
});
}
return orders.map(order => {
const dateStr = order.date;
const parts = dateStr.split('-');
const date = parts.length === 3
? new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]))
: new Date(dateStr);
return {
date: dateStr,
formattedDate: date.toLocaleDateString('en-GB', { month: 'short', day: 'numeric' }),
orders: order.count || 0,
revenue: revenueMap.get(dateStr) || 0
};
});
};
// Custom tooltip for charts
const CustomTooltip = ({ active, payload, label }: any) => {
if (active && payload && payload.length) {
const data = payload[0].payload;
const dataKey = payload[0].dataKey;
const isDualAxis = data.orders !== undefined && data.revenue !== undefined;
// Determine if this is a currency amount or a count
// transformChartData creates both 'value' and the original key (count/amount)
// So we check the original key to determine the type
const isAmount = dataKey === 'amount' || dataKey === 'revenue' ||
(dataKey === 'value' && data.amount !== undefined && data.count === undefined);
return (
<div className="w-full flex items-center justify-center text-muted-foreground" style={{ height: `${height}px` }}>
No data available
<div className="bg-background border border-border p-3 rounded-lg shadow-lg">
<p className="text-sm font-medium mb-2">{data.formattedDate || label}</p>
{isDualAxis ? (
<div className="space-y-1">
<p className="text-sm text-blue-600">
Orders: <span className="font-semibold">{data.orders}</span>
</p>
<p className="text-sm text-green-600">
Revenue: <span className="font-semibold">{formatGBP(data.revenue)}</span>
</p>
</div>
) : (
<p className="text-sm text-primary">
{isAmount ? formatGBP(data.value || data.amount || 0) : `${data.value || data.count || 0}`}
</p>
)}
</div>
);
}
// Find min and max for scaling
const values = data.map(d => d[valueKey] || 0);
const max = Math.max(...values, 1);
const min = Math.min(...values, 0);
const range = max - min || 1;
return (
<div className="w-full relative" style={{ height: `${height}px` }}>
<div className="absolute inset-0 flex items-end">
{data.map((item, index) => {
const value = item[valueKey] || 0;
const normalizedHeight = ((value - min) / range) * 90 + 10; // Scale to 10%-100%
return (
<div
key={index}
className="group flex-1 relative"
>
<div
className="w-full rounded-t transition-all duration-200"
style={{
height: `${normalizedHeight}%`,
backgroundColor: color,
opacity: 0.7
}}
/>
<div className="absolute bottom-0 left-0 right-0 opacity-0 group-hover:opacity-100 bg-background text-xs text-center rounded p-1 transform -translate-y-full pointer-events-none z-10 transition-opacity">
{valueKey === "amount" ?
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value) :
value}
<div className="text-[10px] text-muted-foreground">
{new Date(item.date).toLocaleDateString('en-US', {month: 'short', day: 'numeric'})}
</div>
</div>
</div>
);
})}
</div>
<div className="absolute bottom-0 left-0 right-0 h-px bg-border"></div>
{/* X-axis labels */}
<div className="absolute bottom-[-24px] left-0 right-0 flex justify-between text-xs text-muted-foreground">
<span>{new Date(data[0]?.date).toLocaleDateString('en-US', {month: 'short', day: 'numeric'})}</span>
<span>{new Date(data[data.length - 1]?.date).toLocaleDateString('en-US', {month: 'short', day: 'numeric'})}</span>
</div>
</div>
);
return null;
};
// Trend indicator component for metric cards
@@ -189,9 +232,9 @@ export default function AdminAnalytics() {
// Format currency
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('en-US', {
return new Intl.NumberFormat('en-GB', {
style: 'currency',
currency: 'USD',
currency: 'GBP',
maximumFractionDigits: 0
}).format(value);
};
@@ -237,9 +280,74 @@ export default function AdminAnalytics() {
className={`h-4 w-4 ${refreshing ? 'animate-spin' : ''}`}
/>
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setShowDebug(!showDebug)}
>
{showDebug ? 'Hide' : 'Show'} Debug
</Button>
</div>
</div>
{showDebug && analyticsData && (
<Card className="mt-4">
<CardHeader>
<CardTitle>Debug: Raw Data</CardTitle>
<CardDescription>Date Range: {dateRange}</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4 text-xs font-mono">
<div>
<div className="font-semibold mb-2">Orders:</div>
<div className="pl-4 space-y-1">
<div>Total: {analyticsData?.orders?.total || 'N/A'}</div>
<div>Today: {analyticsData?.orders?.totalToday || 'N/A'}</div>
<div>Daily Orders Array Length: {analyticsData?.orders?.dailyOrders?.length || 0}</div>
<div>First 3 Daily Orders:</div>
<pre className="pl-4 bg-muted p-2 rounded overflow-auto max-h-32">
{JSON.stringify(analyticsData?.orders?.dailyOrders?.slice(0, 3), null, 2)}
</pre>
</div>
</div>
<div>
<div className="font-semibold mb-2">Revenue:</div>
<div className="pl-4 space-y-1">
<div>Total: {analyticsData?.revenue?.total || 'N/A'}</div>
<div>Today: {analyticsData?.revenue?.today || 'N/A'}</div>
<div>Daily Revenue Array Length: {analyticsData?.revenue?.dailyRevenue?.length || 0}</div>
<div>First 3 Daily Revenue:</div>
<pre className="pl-4 bg-muted p-2 rounded overflow-auto max-h-32">
{JSON.stringify(analyticsData?.revenue?.dailyRevenue?.slice(0, 3), null, 2)}
</pre>
</div>
</div>
<div>
<div className="font-semibold mb-2">Vendors:</div>
<div className="pl-4 space-y-1">
<div>Total: {analyticsData?.vendors?.total || 'N/A'}</div>
<div>Daily Growth Array Length: {analyticsData?.vendors?.dailyGrowth?.length || 0}</div>
<div>First 3 Daily Growth:</div>
<pre className="pl-4 bg-muted p-2 rounded overflow-auto max-h-32">
{JSON.stringify(analyticsData?.vendors?.dailyGrowth?.slice(0, 3), null, 2)}
</pre>
</div>
</div>
<details className="mt-4">
<summary className="font-semibold cursor-pointer">Full JSON Response</summary>
<pre className="mt-2 bg-muted p-4 rounded overflow-auto max-h-96 text-[10px]">
{JSON.stringify(analyticsData, null, 2)}
</pre>
</details>
</div>
</CardContent>
</Card>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{/* Orders Card */}
<Card>
@@ -261,13 +369,17 @@ export default function AdminAnalytics() {
/>
</div>
{analyticsData?.orders?.dailyOrders && (
<div className="mt-3 h-10">
<Chart
data={analyticsData.orders.dailyOrders}
height={40}
/>
{analyticsData?.orders?.dailyOrders && analyticsData.orders.dailyOrders.length > 0 ? (
<div className="mt-3 h-12">
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart data={transformChartData(analyticsData.orders.dailyOrders, 'count')} margin={{ top: 0, right: 0, left: 0, bottom: 0 }}>
<Bar dataKey="value" fill="#3b82f6" radius={[2, 2, 0, 0]} />
<Tooltip content={<CustomTooltip />} />
</RechartsBarChart>
</ResponsiveContainer>
</div>
) : (
<div className="mt-3 text-xs text-muted-foreground">No chart data available</div>
)}
</CardContent>
</Card>
@@ -292,15 +404,17 @@ export default function AdminAnalytics() {
/>
</div>
{analyticsData?.revenue?.dailyRevenue && (
<div className="mt-3 h-10">
<Chart
data={analyticsData.revenue.dailyRevenue}
valueKey="amount"
color="#10b981"
height={40}
/>
{analyticsData?.revenue?.dailyRevenue && analyticsData.revenue.dailyRevenue.length > 0 ? (
<div className="mt-3 h-12">
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart data={transformChartData(analyticsData.revenue.dailyRevenue, 'amount')} margin={{ top: 0, right: 0, left: 0, bottom: 0 }}>
<Bar dataKey="value" fill="#10b981" radius={[2, 2, 0, 0]} />
<Tooltip content={<CustomTooltip />} />
</RechartsBarChart>
</ResponsiveContainer>
</div>
) : (
<div className="mt-3 text-xs text-muted-foreground">No chart data available</div>
)}
</CardContent>
</Card>
@@ -317,6 +431,10 @@ export default function AdminAnalytics() {
<div className="text-2xl font-bold">
{analyticsData?.vendors?.total?.toLocaleString() || '0'}
</div>
<div className="flex items-center text-xs text-muted-foreground mt-1">
<span>Active: {analyticsData?.vendors?.active || 0}</span>
<span className="ml-2">Stores: {analyticsData?.vendors?.activeStores || 0}</span>
</div>
<div className="flex items-center text-xs text-muted-foreground mt-1">
<span>New Today: {analyticsData?.vendors?.newToday || 0}</span>
<TrendIndicator
@@ -325,14 +443,17 @@ export default function AdminAnalytics() {
/>
</div>
{analyticsData?.vendors?.dailyGrowth && (
<div className="mt-3 h-10">
<Chart
data={analyticsData.vendors.dailyGrowth}
color="#8b5cf6"
height={40}
/>
{analyticsData?.vendors?.dailyGrowth && analyticsData.vendors.dailyGrowth.length > 0 ? (
<div className="mt-3 h-12">
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart data={transformChartData(analyticsData.vendors.dailyGrowth, 'count')} margin={{ top: 0, right: 0, left: 0, bottom: 0 }}>
<Bar dataKey="value" fill="#8b5cf6" radius={[2, 2, 0, 0]} />
<Tooltip content={<CustomTooltip />} />
</RechartsBarChart>
</ResponsiveContainer>
</div>
) : (
<div className="mt-3 text-xs text-muted-foreground">No chart data available</div>
)}
</CardContent>
</Card>
@@ -359,9 +480,7 @@ export default function AdminAnalytics() {
<Tabs defaultValue="orders" className="mt-6">
<TabsList>
<TabsTrigger value="orders">Orders</TabsTrigger>
<TabsTrigger value="revenue">Revenue</TabsTrigger>
<TabsTrigger value="vendors">Vendors</TabsTrigger>
<TabsTrigger value="engagement">Engagement</TabsTrigger>
</TabsList>
<TabsContent value="orders" className="mt-4">
@@ -369,18 +488,49 @@ export default function AdminAnalytics() {
<CardHeader>
<CardTitle>Order Trends</CardTitle>
<CardDescription>
Daily order volume over the selected time period
Daily order volume and revenue processed over the selected time period
</CardDescription>
</CardHeader>
<CardContent>
{analyticsData?.orders?.dailyOrders ? (
<Chart
data={analyticsData.orders.dailyOrders}
height={300}
/>
{analyticsData?.orders?.dailyOrders && analyticsData.orders.dailyOrders.length > 0 ? (
<div className="h-80">
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
data={combineOrdersAndRevenue(
analyticsData.orders.dailyOrders,
analyticsData.revenue?.dailyRevenue || []
)}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis
dataKey="formattedDate"
tick={{ fontSize: 12 }}
angle={-45}
textAnchor="end"
height={60}
/>
<YAxis
yAxisId="left"
tick={{ fontSize: 12 }}
label={{ value: 'Orders', angle: -90, position: 'insideLeft' }}
/>
<YAxis
yAxisId="right"
orientation="right"
tick={{ fontSize: 12 }}
tickFormatter={(value) => `£${(value / 1000).toFixed(0)}k`}
label={{ value: 'Revenue', angle: 90, position: 'insideRight' }}
/>
<Tooltip content={<CustomTooltip />} />
<Bar yAxisId="left" dataKey="orders" fill="#3b82f6" radius={[2, 2, 0, 0]} name="Orders" />
<Line yAxisId="right" type="monotone" dataKey="revenue" stroke="#10b981" strokeWidth={2} dot={{ fill: '#10b981', r: 4 }} name="Revenue" />
</ComposedChart>
</ResponsiveContainer>
</div>
) : (
<div className="flex items-center justify-center h-[300px] text-muted-foreground">
No order data available
No order data available for the selected time period
</div>
)}
@@ -402,46 +552,6 @@ export default function AdminAnalytics() {
</Card>
</TabsContent>
<TabsContent value="revenue" className="mt-4">
<Card>
<CardHeader>
<CardTitle>Revenue Trends</CardTitle>
<CardDescription>
Daily revenue over the selected time period
</CardDescription>
</CardHeader>
<CardContent>
{analyticsData?.revenue?.dailyRevenue ? (
<Chart
data={analyticsData.revenue.dailyRevenue}
valueKey="amount"
color="#10b981"
height={300}
/>
) : (
<div className="flex items-center justify-center h-[300px] text-muted-foreground">
No revenue data available
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-6">
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">Total Revenue</div>
<div className="text-2xl font-bold">{formatCurrency(analyticsData?.revenue?.total || 0)}</div>
</div>
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">Today's Revenue</div>
<div className="text-2xl font-bold">{formatCurrency(analyticsData?.revenue?.today || 0)}</div>
</div>
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">This Week's Revenue</div>
<div className="text-2xl font-bold">{formatCurrency(analyticsData?.revenue?.thisWeek || 0)}</div>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="vendors" className="mt-4">
<Card>
<CardHeader>
@@ -451,71 +561,73 @@ export default function AdminAnalytics() {
</CardDescription>
</CardHeader>
<CardContent>
{analyticsData?.vendors?.dailyGrowth ? (
<Chart
data={analyticsData.vendors.dailyGrowth}
color="#8b5cf6"
height={300}
/>
{analyticsData?.vendors?.dailyGrowth && analyticsData.vendors.dailyGrowth.length > 0 ? (
<div className="h-80">
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart data={transformChartData(analyticsData.vendors.dailyGrowth, 'count')} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis
dataKey="formattedDate"
tick={{ fontSize: 12 }}
angle={-45}
textAnchor="end"
height={60}
/>
<YAxis tick={{ fontSize: 12 }} />
<Tooltip content={<CustomTooltip />} />
<Bar dataKey="value" fill="#8b5cf6" radius={[2, 2, 0, 0]} />
</RechartsBarChart>
</ResponsiveContainer>
</div>
) : (
<div className="flex items-center justify-center h-[300px] text-muted-foreground">
No vendor data available
No vendor data available for the selected time period
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-6">
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mt-6">
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">Total Vendors</div>
<div className="text-2xl font-bold">{analyticsData?.vendors?.total?.toLocaleString() || '0'}</div>
</div>
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">New Today</div>
<div className="text-2xl font-bold">{analyticsData?.vendors?.newToday?.toLocaleString() || '0'}</div>
<div className="text-sm font-medium mb-1">Active Vendors</div>
<div className="text-2xl font-bold">{analyticsData?.vendors?.active?.toLocaleString() || '0'}</div>
</div>
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">Active Stores</div>
<div className="text-2xl font-bold">{analyticsData?.vendors?.activeStores?.toLocaleString() || '0'}</div>
</div>
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">New This Week</div>
<div className="text-2xl font-bold">{analyticsData?.vendors?.newThisWeek?.toLocaleString() || '0'}</div>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="engagement" className="mt-4">
<Card>
<CardHeader>
<CardTitle>User Engagement</CardTitle>
<CardDescription>
Chat and message activity
</CardDescription>
</CardHeader>
<CardContent>
{analyticsData?.engagement?.dailyMessages ? (
<Chart
data={analyticsData.engagement.dailyMessages}
color="#ec4899"
height={300}
/>
) : (
<div className="flex items-center justify-center h-[300px] text-muted-foreground">
No engagement data available
{/* Top Vendors by Revenue */}
{analyticsData?.vendors?.topVendors && analyticsData.vendors.topVendors.length > 0 && (
<div className="mt-6">
<h3 className="text-lg font-semibold mb-4">Top Vendors by Revenue</h3>
<div className="space-y-2">
{analyticsData.vendors.topVendors.map((vendor, index) => (
<div key={vendor.vendorId} className="flex items-center justify-between p-3 bg-muted/30 rounded-lg">
<div className="flex items-center gap-3">
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-primary/10 text-primary font-semibold text-sm">
{index + 1}
</div>
<div>
<div className="font-medium">{vendor.vendorName}</div>
<div className="text-xs text-muted-foreground">{vendor.orderCount} orders</div>
</div>
</div>
<div className="text-right">
<div className="font-semibold text-green-600">{formatCurrency(vendor.totalRevenue)}</div>
</div>
</div>
))}
</div>
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-6">
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">Total Messages</div>
<div className="text-2xl font-bold">{analyticsData?.engagement?.totalMessages?.toLocaleString() || '0'}</div>
</div>
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">Active Chats</div>
<div className="text-2xl font-bold">{analyticsData?.engagement?.activeChats?.toLocaleString() || '0'}</div>
</div>
<div className="bg-muted/50 p-4 rounded-lg">
<div className="text-sm font-medium mb-1">Sessions</div>
<div className="text-2xl font-bold">{analyticsData?.sessions?.active?.toLocaleString() || '0'} / {analyticsData?.sessions?.total?.toLocaleString() || '0'}</div>
</div>
</div>
</CardContent>
</Card>
</TabsContent>

View File

@@ -25,6 +25,7 @@ export default function SystemStatusCard() {
(async () => {
try {
const res = await fetchClient<Status>("/admin/system-status");
console.log(`Here is your mother fuckin data: ${JSON.stringify(res)}`);
if (mounted) setData(res);
} catch (e: any) {
if (mounted) setError(e?.message || "Failed to load status");

View File

@@ -6,7 +6,8 @@ import KeepOnline from "@/components/KeepOnline";
const KeepOnlineWrapper = () => {
const pathname = usePathname();
if (!pathname?.includes("/dashboard")) {
// Don't show KeepOnline on admin pages or non-dashboard pages
if (!pathname?.includes("/dashboard") || pathname?.includes("/dashboard/admin")) {
return null;
}

View File

@@ -18,6 +18,8 @@ export default function Layout({ children }: LayoutProps) {
// Check if we're in a chat detail page
const isChatDetailPage = pathname?.includes('/dashboard/chats/') && !pathname?.endsWith('/chats') && !pathname?.endsWith('/new')
// Check if we're on an admin page
const isAdminPage = pathname?.includes('/dashboard/admin')
useEffect(() => setMounted(true), [])
@@ -27,7 +29,7 @@ export default function Layout({ children }: LayoutProps) {
<div className={`flex h-screen ${theme === "dark" ? "dark" : ""}`}>
<Sidebar />
<div className="w-full flex flex-1 flex-col">
{!isChatDetailPage && (
{!isChatDetailPage && !isAdminPage && (
<header className="h-16 border-b border-border flex items-center justify-end px-6">
<div className="flex items-center space-x-2">
<UnifiedNotifications />