Some checks failed
Build Frontend / build (push) Failing after 7s
Replaces imports from 'components/ui' with 'components/common' across the app and dashboard pages, and updates model and API imports to use new paths under 'lib'. Removes redundant authentication checks from several dashboard pages. Adds new dashboard components and utility files, and reorganizes hooks and services into the 'lib' directory for improved structure.
146 lines
4.8 KiB
TypeScript
146 lines
4.8 KiB
TypeScript
import { Suspense } from 'react';
|
|
import { cookies } from 'next/headers';
|
|
import { redirect } from 'next/navigation';
|
|
import Dashboard from "@/components/dashboard/dashboard";
|
|
import AnalyticsDashboard from '@/components/analytics/AnalyticsDashboard';
|
|
import AnalyticsDashboardSkeleton from '@/components/analytics/AnalyticsDashboardSkeleton';
|
|
import StoreSelector from '@/components/analytics/StoreSelector';
|
|
import { getAnalyticsOverviewServer } from '@/lib/api/server-api';
|
|
import { fetchServer } from '@/lib/api';
|
|
import { performance } from 'perf_hooks';
|
|
import { Info, GitCommit, User, Zap, BarChart3 } from 'lucide-react';
|
|
import packageJson from '../../../package.json';
|
|
import { getGitInfo } from '@/lib/utils/git';
|
|
|
|
// Vendor type for consistency
|
|
interface Vendor {
|
|
_id: string;
|
|
username: string;
|
|
storeId: string;
|
|
pgpKey: string;
|
|
__v: number;
|
|
}
|
|
|
|
interface User {
|
|
vendor: Vendor;
|
|
}
|
|
|
|
export default async function AnalyticsPage({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<{ storeId?: string }>;
|
|
}) {
|
|
const startTime = performance.now();
|
|
|
|
// Await searchParams as required by Next.js 15+
|
|
const resolvedSearchParams = await searchParams;
|
|
|
|
// Check for storeId in query parameters (for staff users)
|
|
const storeId = resolvedSearchParams?.storeId;
|
|
|
|
// Check for storeId in cookies (alternative method for staff users)
|
|
const cookieStore = await cookies();
|
|
const cookieStoreId = cookieStore.get('storeId')?.value;
|
|
|
|
// Use query parameter first, then cookie, then undefined (for vendors)
|
|
const finalStoreId = storeId || cookieStoreId;
|
|
|
|
try {
|
|
// Fetch user data and analytics data in parallel
|
|
const [userResponse, initialData] = await Promise.all([
|
|
fetchServer<User>("/auth/me"),
|
|
getAnalyticsOverviewServer(finalStoreId)
|
|
]);
|
|
|
|
// Get git info using the utility
|
|
const gitInfo = getGitInfo();
|
|
|
|
const endTime = performance.now();
|
|
const generationTime = (endTime - startTime).toFixed(2);
|
|
const panelVersion = packageJson.version;
|
|
const commitHash = gitInfo.hash;
|
|
|
|
const vendor = userResponse.vendor;
|
|
|
|
return (
|
|
<Dashboard>
|
|
<div className="space-y-6">
|
|
{/* Analytics Content */}
|
|
<Suspense fallback={<AnalyticsDashboardSkeleton />}>
|
|
<AnalyticsDashboard initialData={initialData} />
|
|
</Suspense>
|
|
</div>
|
|
|
|
<div className="fixed bottom-2 right-2 text-xs text-muted-foreground bg-background/80 backdrop-blur-sm px-2 py-1 rounded border border-border/50 z-50 flex items-center space-x-2">
|
|
<div className="flex items-center gap-1">
|
|
<Info size={12} className="text-muted-foreground/80" />
|
|
<span>v{panelVersion}</span>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-1">
|
|
<User size={12} className="text-muted-foreground/80" />
|
|
<span>{vendor.username}</span>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-1">
|
|
<GitCommit size={12} className="text-muted-foreground/80" />
|
|
<span>{commitHash}</span>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-1">
|
|
<Zap size={12} className="text-amber-500" />
|
|
<span>Generated in {generationTime}ms</span>
|
|
</div>
|
|
|
|
<span className="px-1.5 py-0.5 text-xs rounded-sm bg-emerald-500/20 text-emerald-300">
|
|
{process.env.NODE_ENV || 'development'}
|
|
</span>
|
|
</div>
|
|
</Dashboard>
|
|
);
|
|
} catch (error) {
|
|
console.error('Error fetching analytics data:', error);
|
|
|
|
// If it's a 401/403 error, redirect to login
|
|
if (error instanceof Error && error.message.includes('401')) {
|
|
redirect('/auth/login');
|
|
}
|
|
|
|
// If it's a 400 error (missing storeId for staff), show store selector
|
|
if (error instanceof Error && error.message.includes('400')) {
|
|
return (
|
|
<Dashboard>
|
|
<div className="space-y-6">
|
|
<div className="text-center mb-8">
|
|
<h1 className="text-3xl font-bold text-foreground mb-4">Analytics Dashboard</h1>
|
|
<p className="text-muted-foreground">
|
|
Please select a store to view analytics data.
|
|
</p>
|
|
</div>
|
|
<StoreSelector />
|
|
</div>
|
|
</Dashboard>
|
|
);
|
|
}
|
|
|
|
// For other errors, show a fallback
|
|
return (
|
|
<Dashboard>
|
|
<div className="space-y-6">
|
|
<div className="text-center py-8">
|
|
<h1 className="text-3xl font-bold text-foreground mb-4">Analytics Dashboard</h1>
|
|
<p className="text-muted-foreground">
|
|
Unable to load analytics data. Please try refreshing the page.
|
|
</p>
|
|
{finalStoreId && (
|
|
<p className="text-sm text-muted-foreground mt-2">
|
|
Accessing store: {finalStoreId}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Dashboard>
|
|
);
|
|
}
|
|
}
|