import { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import Layout from '../../components/Layout'; import StatusBadge from '../../components/StatusBadge'; import { supabase } from '../../lib/supabase'; import { useAuth } from '../../context/AuthContext'; import { withTimeout } from '../../lib/withTimeout'; function TaskRow({ task, project }) { return (
{task.title}
{project?.name || '—'} ); } function TaskColumn({ title, tasks, projects, emptyMessage }) { return (
0 ? '1px solid var(--border)' : 'none', background: 'var(--card-bg-2)' }}> {title} {tasks.length > 0 && ( {tasks.length} )}
{tasks.length === 0 ? (
{emptyMessage}
) : ( tasks.map(task => ( p.id === task.project_id)} /> )) )}
); } export default function ClientDashboard() { const { currentUser } = useAuth(); const hasCompany = Boolean(currentUser?.company_id || currentUser?.company?.id || currentUser?.companies?.length); const [stats, setStats] = useState({ notStarted: 0, inProgress: 0, awaitingReview: 0, outstandingInvoices: 0 }); const [reviewTasks, setReviewTasks] = useState([]); const [inProgressTasks, setInProgressTasks] = useState([]); const [projects, setProjects] = useState([]); const [loading, setLoading] = useState(hasCompany); useEffect(() => { if (!hasCompany) { setLoading(false); return; } async function load() { try { const [ { count: notStartedCount }, { count: inProgressCount }, { count: reviewCount }, { data: sentInvoices }, { data: activeTasks }, ] = await withTimeout(Promise.all([ supabase.from('tasks').select('id', { count: 'exact', head: true }).eq('status', 'not_started'), supabase.from('tasks').select('id', { count: 'exact', head: true }).in('status', ['in_progress', 'on_hold']), supabase.from('tasks').select('id', { count: 'exact', head: true }).eq('status', 'client_review'), supabase.from('invoices').select('total').eq('status', 'sent'), supabase.from('tasks').select('id, title, status, project_id').in('status', ['client_review', 'in_progress', 'on_hold', 'not_started']).order('submitted_at', { ascending: false }), ]), 12000, 'Client dashboard load'); setStats({ notStarted: notStartedCount || 0, inProgress: inProgressCount || 0, awaitingReview: reviewCount || 0, outstandingInvoices: (sentInvoices || []).reduce((sum, inv) => sum + Number(inv.total || 0), 0), }); const tasks = activeTasks || []; const review = tasks.filter(t => t.status === 'client_review'); const inProg = tasks.filter(t => ['in_progress', 'on_hold', 'not_started'].includes(t.status)); setReviewTasks(review); setInProgressTasks(inProg); if (tasks.length > 0) { const projectIds = [...new Set(tasks.map(t => t.project_id).filter(Boolean))]; const { data: proj } = await supabase.from('projects').select('id, name').in('id', projectIds); setProjects(proj || []); } } catch (error) { console.error('ClientDashboard load failed:', error); } finally { setLoading(false); } } load(); }, [hasCompany]); if (loading) return

Loading...

; return (
Welcome back, {currentUser?.name?.split(' ')[0]}
Track active work and the items that need your attention.
+ New Request
0 ? 'var(--accent)' : undefined }}>{stats.awaitingReview}
Awaiting Review
{stats.inProgress}
In Progress
{stats.notStarted}
Not Started
${stats.outstandingInvoices.toFixed(2)}
Outstanding Invoices
); }