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
);
}