Files
fourge-portal/src/pages/team/Dashboard.jsx
T
2026-03-26 23:42:06 -04:00

175 lines
7.4 KiB
React
Executable File

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';
function CompanyGroup({ company, tasks, projects }) {
const [open, setOpen] = useState(true);
return (
<div style={{ marginBottom: 10, border: '1px solid var(--border)', borderRadius: 8, overflow: 'hidden', background: 'var(--card-bg)' }}>
<button
onClick={() => setOpen(o => !o)}
style={{
width: '100%', display: 'flex', alignItems: 'center',
justifyContent: 'space-between', padding: '10px 14px',
background: 'var(--card-bg-2)', border: 'none', cursor: 'pointer',
borderBottom: open ? '1px solid var(--border)' : 'none',
fontFamily: 'inherit',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span style={{ fontSize: 13, fontWeight: 700, color: 'var(--text-primary)' }}>
{company.name}
</span>
<span style={{ fontSize: 11, fontWeight: 600, padding: '1px 7px', borderRadius: 20, background: 'var(--accent)', color: '#1a1a1a' }}>
{tasks.length}
</span>
</div>
<span style={{ fontSize: 12, color: 'var(--text-muted)' }}>{open ? '▲' : '▼'}</span>
</button>
{open && (
<div>
{tasks.map(task => {
const project = projects.find(p => p.id === task.project_id);
return (
<div key={task.id} style={{ padding: '10px 14px', borderBottom: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: 4 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Link to={`/tasks/${task.id}`} className="table-link" style={{ fontSize: 13 }}>
{task.title} <span style={{ fontWeight: 400, color: 'var(--text-muted)' }}>{'v' + String(task.current_version).padStart(2, '0')}</span>
</Link>
<StatusBadge status={task.status} />
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ fontSize: 11, color: 'var(--text-secondary)' }}>{project?.name}</span>
<span style={{ fontSize: 11, color: task.assigned_name ? 'var(--text-secondary)' : 'var(--text-muted)' }}>
{task.assigned_name || 'Unassigned'}
</span>
</div>
</div>
);
})}
</div>
)}
</div>
);
}
function GroupedColumn({ tasks, companies, projects, emptyText }) {
if (tasks.length === 0) return (
<div style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--text-muted)', fontSize: 13, background: 'var(--card-bg)', borderRadius: 8, border: '1px solid var(--border)' }}>
{emptyText}
</div>
);
const groups = companies
.map(company => {
const companyProjectIds = projects.filter(p => p.company_id === company.id).map(p => p.id);
const companyTasks = tasks.filter(t => companyProjectIds.includes(t.project_id));
return { company, tasks: companyTasks };
})
.filter(g => g.tasks.length > 0);
return (
<div>
{groups.map(({ company, tasks: groupTasks }) => (
<CompanyGroup key={company.id} company={company} tasks={groupTasks} projects={projects} />
))}
</div>
);
}
export default function Dashboard() {
const { currentUser } = useAuth();
const [tasks, setTasks] = useState([]);
const [projects, setProjects] = useState([]);
const [companies, setCompanies] = useState([]);
const [loading, setLoading] = useState(true);
const [showCompleted, setShowCompleted] = useState(false);
useEffect(() => {
async function load() {
const [{ data: t }, { data: p }, { data: co }] = await Promise.all([
supabase.from('tasks').select('*').order('submitted_at', { ascending: false }),
supabase.from('projects').select('*'),
supabase.from('companies').select('*').order('name'),
]);
setTasks(t || []);
setProjects(p || []);
setCompanies(co || []);
setLoading(false);
}
load();
}, []);
if (loading) return <Layout><p style={{ padding: 24, color: 'var(--text-muted)' }}>Loading...</p></Layout>;
const activeTasks = tasks.filter(t => ['in_progress', 'on_hold', 'client_review'].includes(t.status));
const notStartedTasks = tasks.filter(t => t.status === 'not_started');
const completedTasks = tasks.filter(t => t.status === 'client_approved');
const activeProjects = projects.filter(p => p.status === 'active');
return (
<Layout>
<div className="page-header">
<div>
<div className="page-title">Welcome back, {currentUser?.name?.split(' ')[0]}</div>
<div className="page-subtitle">Here's what's happening across your projects.</div>
</div>
<div style={{ display: 'flex', gap: 8 }}>
<button className="btn btn-outline" onClick={() => setShowCompleted(s => !s)}>
{showCompleted ? 'Hide Completed' : 'Show Completed'}
</button>
<Link to="/requests" className="btn btn-primary">View Requests</Link>
</div>
</div>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-icon">📁</div>
<div className="stat-value">{activeProjects.length}</div>
<div className="stat-label">Active Projects</div>
</div>
<div className="stat-card">
<div className="stat-icon"></div>
<div className="stat-value">{activeTasks.length}</div>
<div className="stat-label">Active Jobs</div>
</div>
<div className="stat-card">
<div className="stat-icon">🔴</div>
<div className="stat-value">{notStartedTasks.length}</div>
<div className="stat-label">Not Started</div>
</div>
<div className="stat-card">
<div className="stat-icon"></div>
<div className="stat-value">{completedTasks.length}</div>
<div className="stat-label">Completed</div>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: showCompleted ? '1fr 1fr 1fr' : '1fr 1fr', gap: 24 }}>
<div>
<div className="card-title">Not Started</div>
<GroupedColumn tasks={notStartedTasks} companies={companies} projects={projects} emptyText="Nothing waiting to start" />
</div>
<div>
<div className="card-title">Active Jobs</div>
<GroupedColumn tasks={activeTasks} companies={companies} projects={projects} emptyText="No active jobs" />
</div>
{showCompleted && (
<div>
<div className="card-title" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
Completed
<button onClick={() => setShowCompleted(false)} style={{ fontSize: 11, color: 'var(--text-muted)', background: 'none', border: 'none', cursor: 'pointer' }}>
Hide
</button>
</div>
<GroupedColumn tasks={completedTasks} companies={companies} projects={projects} emptyText="No completed jobs yet" />
</div>
)}
</div>
</Layout>
);
}