diff --git a/client/pages/hub/ClientDashboard.tsx b/client/pages/hub/ClientDashboard.tsx index bd3af923..d53e93f1 100644 --- a/client/pages/hub/ClientDashboard.tsx +++ b/client/pages/hub/ClientDashboard.tsx @@ -1,55 +1,695 @@ +import { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; import Layout from "@/components/Layout"; import { Button } from "@/components/ui/button"; -import { Card, CardContent } from "@/components/ui/card"; -import { useNavigate } from "react-router-dom"; -import { ArrowLeft, BarChart3 } from "lucide-react"; +import { useAuth } from "@/contexts/AuthContext"; +import { aethexToast } from "@/lib/aethex-toast"; +import { supabase } from "@/lib/supabase"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Progress } from "@/components/ui/progress"; +import LoadingScreen from "@/components/LoadingScreen"; +import { + BarChart3, + TrendingUp, + Users, + FileText, + DollarSign, + Clock, + CheckCircle, + AlertCircle, + ArrowRight, + Calendar, + MapPin, + Phone, + Mail, + Github, + ExternalLink, +} from "lucide-react"; + +const getApiBase = () => + typeof window !== "undefined" ? window.location.origin : ""; + +interface Project { + id: string; + name: string; + description: string; + status: "planning" | "in-progress" | "in-review" | "completed"; + progress: number; + budget: number; + spent: number; + start_date: string; + end_date: string; + team_lead: string; +} + +interface Contract { + id: string; + title: string; + client: string; + amount: number; + status: "draft" | "active" | "completed" | "archived"; + start_date: string; + end_date: string; + milestones: any[]; +} + +interface TeamMember { + id: string; + name: string; + email: string; + role: string; + avatar?: string; + status: "active" | "inactive"; +} + +interface Invoice { + id: string; + number: string; + amount: number; + status: "draft" | "sent" | "paid" | "overdue"; + issued_date: string; + due_date: string; + client: string; +} export default function ClientDashboard() { const navigate = useNavigate(); + const { user, loading: authLoading } = useAuth(); + const [activeTab, setActiveTab] = useState("overview"); + const [projects, setProjects] = useState([]); + const [contracts, setContracts] = useState([]); + const [invoices, setInvoices] = useState([]); + const [teamMembers, setTeamMembers] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + if (!authLoading && user) { + loadDashboardData(); + } else if (!authLoading && !user) { + setLoading(false); + } + }, [user, authLoading]); + + const loadDashboardData = async () => { + try { + setLoading(true); + const { + data: { session }, + } = await supabase.auth.getSession(); + const token = session?.access_token; + + if (!token) throw new Error("No auth token"); + + const apiBase = getApiBase(); + + // Load projects + try { + const projectRes = await fetch( + `${apiBase}/api/corp/projects?limit=20`, + { + headers: { Authorization: `Bearer ${token}` }, + }, + ); + if (projectRes.ok) { + const data = await projectRes.json(); + setProjects(Array.isArray(data) ? data : data.projects || []); + } + } catch { + // Silently ignore + } + + // Load contracts + try { + const contractRes = await fetch( + `${apiBase}/api/corp/contracts?limit=20`, + { + headers: { Authorization: `Bearer ${token}` }, + }, + ); + if (contractRes.ok) { + const data = await contractRes.json(); + setContracts(Array.isArray(data) ? data : data.contracts || []); + } + } catch { + // Silently ignore + } + + // Load invoices + try { + const invoiceRes = await fetch( + `${apiBase}/api/corp/invoices/list?limit=20`, + { + headers: { Authorization: `Bearer ${token}` }, + }, + ); + if (invoiceRes.ok) { + const data = await invoiceRes.json(); + setInvoices(Array.isArray(data) ? data : data.invoices || []); + } + } catch { + // Silently ignore + } + + // Load team + try { + const teamRes = await fetch( + `${apiBase}/api/corp/team?limit=50`, + { + headers: { Authorization: `Bearer ${token}` }, + }, + ); + if (teamRes.ok) { + const data = await teamRes.json(); + setTeamMembers(Array.isArray(data) ? data : data.team || []); + } + } catch { + // Silently ignore + } + } catch (error) { + aethexToast({ + message: "Failed to load dashboard data", + type: "error", + }); + } finally { + setLoading(false); + } + }; + + if (authLoading || loading) { + return ; + } + + if (!user) { + return ( + +
+
+

+ CORP Dashboard +

+

Enterprise services & project management

+ +
+
+
+ ); + } + + const totalProjectValue = projects.reduce((acc, p) => acc + p.budget, 0); + const totalSpent = projects.reduce((acc, p) => acc + p.spent, 0); + const activeProjects = projects.filter((p) => p.status === "in-progress") + .length; + const overdueInvoices = invoices.filter((i) => i.status === "overdue").length; + const totalRevenue = invoices.reduce((acc, i) => acc + i.amount, 0); return ( -
-
+
+
+ {/* Header */} +
+ +
+ +

+ Dashboard +

+
+

+ Manage projects, contracts, team, and invoicing +

+
-
-
-
+ {/* Key Metrics */} +
+ + +
+

Project Value

+ +
+

+ ${(totalProjectValue / 1000).toFixed(0)}k +

+
+
+ + + +
+

Active Projects

+ +
+

{activeProjects}

+
+
+ + + +
+

Total Revenue

+ +
+

+ ${(totalRevenue / 1000).toFixed(0)}k +

+
+
+ + 0 + ? "from-red-950/40 to-red-900/20 border-red-500/20" + : "from-gray-950/40 to-gray-900/20 border-gray-500/20" + }`}> + +
+

Overdue Invoices

+ 0 ? "text-red-400" : "text-gray-400" + }`} + /> +
+

+ {overdueInvoices} +

+
+
+
+ + {/* Tabs */} + + + Overview + Projects + Contracts + Invoices + + + {/* Overview Tab */} + + {/* Active Projects */} + {projects.filter((p) => p.status === "in-progress").length > 0 && ( + + + Active Projects + + Currently in development + + + + {projects + .filter((p) => p.status === "in-progress") + .slice(0, 3) + .map((project) => ( +
+
+
+

+ {project.name} +

+

+ {project.description} +

+
+ + {project.progress}% + +
+ +
+
+ Progress + + ${project.spent.toLocaleString()} / $ + {project.budget.toLocaleString()} + +
+ +
+ +
+ + Due:{" "} + {new Date(project.end_date).toLocaleDateString()} + + Lead: {project.team_lead} +
+
+ ))} +
+
+ )} + + {/* Recent Invoices */} + {invoices.length > 0 && ( + + + Recent Invoices + + Latest billing activity + + + + {invoices.slice(0, 5).map((invoice) => ( +
+
+

+ Invoice {invoice.number} +

+

+ {invoice.client} •{" "} + {new Date(invoice.issued_date).toLocaleDateString()} +

+
+
+

+ ${invoice.amount.toLocaleString()} +

+ + {invoice.status} + +
+
+ ))} +
+
+ )} + + {/* Team Summary */} + {teamMembers.length > 0 && ( + + + Team Members + + {teamMembers.length} active team members + + + +
+ {teamMembers.slice(0, 6).map((member) => ( +
+

+ {member.name} +

+

{member.role}

+ + {member.status} + +
+ ))} +
+
+
+ )} +
+ + {/* Projects Tab */} + + {projects.length === 0 ? ( + + + +

No projects yet

+ +
+
+ ) : ( +
+ {projects.map((project) => ( + + +
+
+

+ {project.name} +

+

+ {project.description} +

+
+ + {project.status.replace("-", " ")} + +
+ +
+
+

+ Budget +

+

+ ${(project.budget / 1000).toFixed(0)}k +

+
+
+

+ Spent +

+

+ ${(project.spent / 1000).toFixed(0)}k +

+
+
+

+ Progress +

+

+ {project.progress}% +

+
+
+

+ Due +

+

+ {new Date(project.end_date).toLocaleDateString()} +

+
+
+ + + + +
+
+ ))} +
+ )} +
+ + {/* Contracts Tab */} + + {contracts.length === 0 ? ( + + + +

No contracts yet

+ +
+
+ ) : ( +
+ {contracts.map((contract) => ( + + +
+
+

+ {contract.title} +

+

+ Client: {contract.client} +

+
+ + {contract.status} + +
+ +
+
+

+ Amount +

+

+ ${contract.amount.toLocaleString()} +

+
+
+

+ Start +

+

+ {new Date(contract.start_date).toLocaleDateString()} +

+
+
+

+ End +

+

+ {new Date(contract.end_date).toLocaleDateString()} +

+
+
+ + +
+
+ ))} +
+ )} +
+ + {/* Invoices Tab */} + + {invoices.length === 0 ? ( + + + +

No invoices yet

+ +
+
+ ) : ( +
+ {invoices.map((invoice) => ( + + +
+
+
+

+ Invoice {invoice.number} +

+ + {invoice.status} + +
+

+ {invoice.client} +

+
+
+

+ ${invoice.amount.toLocaleString()} +

+

+ Due:{" "} + {new Date(invoice.due_date).toLocaleDateString()} +

+
+
+
+
+ ))} +
+ )} +
+
+ + {/* CTA Section */} + + +

+ Need Help Managing Your Projects? +

+

+ Contact our CORP team for consultation, dedicated support, or + custom development services +

-
- -

Dashboard

-
-
-
- -
-
- - - -

- Advanced dashboard analytics coming soon -

- -
-
-
-
-
+ + +
);