- Skip SSE connection in demo mode (MSW doesn't support SSE). - Remove unused `useProjectEvents` and related real-time hooks from `Dashboard`. - Temporarily disable activity feed SSE until a global endpoint is available.
125 lines
4.2 KiB
TypeScript
125 lines
4.2 KiB
TypeScript
/**
|
|
* Dashboard Component
|
|
*
|
|
* Main dashboard layout orchestrator.
|
|
* Combines all dashboard sub-components into a cohesive layout.
|
|
*
|
|
* Layout:
|
|
* +------------------------------------------+------------------+
|
|
* | Welcome Header | ACTIVITY |
|
|
* +------------------------------------------+ FEED |
|
|
* | Quick Stats (4 cards) | SIDEBAR |
|
|
* +------------------------------------------+ |
|
|
* | Recent Projects (3-6 cards) | |
|
|
* +------------------------------------------+ |
|
|
* | Pending Approvals (if any) | |
|
|
* +------------------------------------------+------------------+
|
|
*
|
|
* @see Issue #53
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import { useCallback } from 'react';
|
|
import { toast } from 'sonner';
|
|
import { Card } from '@/components/ui/card';
|
|
import { ActivityFeed } from '@/components/activity/ActivityFeed';
|
|
import { WelcomeHeader } from './WelcomeHeader';
|
|
import { DashboardQuickStats } from './DashboardQuickStats';
|
|
import { RecentProjects } from './RecentProjects';
|
|
import { PendingApprovals } from './PendingApprovals';
|
|
import { EmptyState } from './EmptyState';
|
|
import { useDashboard, type PendingApproval } from '@/lib/api/hooks/useDashboard';
|
|
import { useAuth } from '@/lib/auth/AuthContext';
|
|
|
|
export interface DashboardProps {
|
|
/** Additional CSS classes */
|
|
className?: string;
|
|
}
|
|
|
|
export function Dashboard({ className }: DashboardProps) {
|
|
const { user } = useAuth();
|
|
const { data, isLoading, error } = useDashboard();
|
|
|
|
// Get user's first name for empty state
|
|
const firstName = user?.first_name || user?.email?.split('@')[0] || 'there';
|
|
|
|
// Handle approval actions
|
|
const handleApprove = useCallback((approval: PendingApproval) => {
|
|
// TODO: Implement actual approval API call
|
|
toast.success(`Approved: ${approval.title}`, {
|
|
description: `${approval.projectName}`,
|
|
});
|
|
}, []);
|
|
|
|
const handleReject = useCallback((approval: PendingApproval) => {
|
|
// TODO: Implement actual rejection API call
|
|
toast.info(`Rejected: ${approval.title}`, {
|
|
description: `${approval.projectName}`,
|
|
});
|
|
}, []);
|
|
|
|
// Show error state
|
|
if (error) {
|
|
toast.error('Failed to load dashboard data', {
|
|
description: 'Please try refreshing the page',
|
|
});
|
|
}
|
|
|
|
// Check if user has no projects (empty state)
|
|
const hasNoProjects = !isLoading && (!data?.recentProjects || data.recentProjects.length === 0);
|
|
|
|
return (
|
|
<div className={className}>
|
|
<div className="container mx-auto px-4 py-6">
|
|
{/* Welcome Header - always shown */}
|
|
<WelcomeHeader className="mb-6" />
|
|
|
|
{hasNoProjects ? (
|
|
// Empty state for new users
|
|
<EmptyState userName={firstName} />
|
|
) : (
|
|
// Main dashboard layout
|
|
<div className="grid gap-6 lg:grid-cols-[1fr_350px]">
|
|
{/* Main Content */}
|
|
<div className="space-y-6">
|
|
{/* Quick Stats */}
|
|
<DashboardQuickStats stats={data?.stats} isLoading={isLoading} />
|
|
|
|
{/* Recent Projects */}
|
|
<RecentProjects projects={data?.recentProjects} isLoading={isLoading} />
|
|
|
|
{/* Pending Approvals */}
|
|
<PendingApprovals
|
|
approvals={data?.pendingApprovals}
|
|
isLoading={isLoading}
|
|
onApprove={handleApprove}
|
|
onReject={handleReject}
|
|
/>
|
|
</div>
|
|
|
|
{/* Activity Feed Sidebar */}
|
|
{/* TODO: Enable when global activity SSE endpoint is implemented */}
|
|
{/* Currently disabled - there's no dashboard-wide SSE endpoint */}
|
|
<div className="hidden lg:block">
|
|
<Card className="sticky top-4">
|
|
<ActivityFeed
|
|
events={[]}
|
|
connectionState="disconnected"
|
|
isLoading={isLoading}
|
|
maxHeight={600}
|
|
showHeader
|
|
title="Recent Activity"
|
|
enableFiltering={false}
|
|
enableSearch={false}
|
|
compact
|
|
/>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|