From dd7ca332a07a30f9d07debe178c1e0eb39482252 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Sat, 15 Nov 2025 09:13:02 +0000 Subject: [PATCH] Implement NEXUS Dashboard with Creator/Client dual-view toggle cgen-650ce509724c4005b93d2209d33d2da5 --- client/pages/dashboards/NexusDashboard.tsx | 943 ++++++++++++++------- 1 file changed, 647 insertions(+), 296 deletions(-) diff --git a/client/pages/dashboards/NexusDashboard.tsx b/client/pages/dashboards/NexusDashboard.tsx index 852e7fa6..ea0c271c 100644 --- a/client/pages/dashboards/NexusDashboard.tsx +++ b/client/pages/dashboards/NexusDashboard.tsx @@ -28,18 +28,26 @@ import { Heart, Star, ExternalLink, + ToggleLeft, + ToggleRight, } from "lucide-react"; const API_BASE = import.meta.env.VITE_API_BASE || ""; +type ViewMode = "creator" | "client"; + export default function NexusDashboard() { const navigate = useNavigate(); const { user, loading: authLoading } = useAuth(); + const [viewMode, setViewMode] = useState("creator"); const [activeTab, setActiveTab] = useState("overview"); const [creatorProfile, setCreatorProfile] = useState(null); const [applications, setApplications] = useState([]); const [contracts, setContracts] = useState([]); const [payoutInfo, setPayoutInfo] = useState(null); + const [postedOpportunities, setPostedOpportunities] = useState([]); + const [applicants, setApplicants] = useState([]); + const [paymentHistory, setPaymentHistory] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { @@ -89,6 +97,33 @@ export default function NexusDashboard() { const data = await payoutRes.json(); setPayoutInfo(data.summary); } + + // Load client data (posted opportunities) + const oppRes = await fetch(`${API_BASE}/api/nexus/client/opportunities?limit=10`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (oppRes.ok) { + const data = await oppRes.json(); + setPostedOpportunities(data.opportunities || []); + } + + // Load applicants + const appliRes = await fetch(`${API_BASE}/api/nexus/client/applicants?limit=50`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (appliRes.ok) { + const data = await appliRes.json(); + setApplicants(data.applicants || []); + } + + // Load payment history + const payHistRes = await fetch(`${API_BASE}/api/nexus/client/payment-history?limit=10`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (payHistRes.ok) { + const data = await payHistRes.json(); + setPaymentHistory(data.payments || []); + } } catch (error: any) { aethexToast({ message: "Failed to load dashboard data", @@ -127,12 +162,18 @@ export default function NexusDashboard() { const isProfileComplete = creatorProfile?.verified || (creatorProfile?.headline && creatorProfile?.skills?.length > 0); const pendingApplications = applications.filter((a) => a.status === "submitted").length; const activeContracts = contracts.filter((c) => c.status === "active").length; + const openOpportunities = postedOpportunities.filter((o) => o.status === "open").length; + const applicantStats = { + applied: applicants.filter((a) => a.status === "applied").length, + interviewing: applicants.filter((a) => a.status === "interviewing").length, + hired: applicants.filter((a) => a.status === "hired").length, + }; return (
- {/* Header */} + {/* Header with View Toggle */}
@@ -140,13 +181,43 @@ export default function NexusDashboard() { NEXUS Marketplace

- Showcase your skills and land paid opportunities + {viewMode === "creator" + ? "Showcase your skills and land paid opportunities" + : "Hire talent and manage your team"}

+ + {/* View Toggle */} +
+ + +
- {/* Setup Banner */} - {!isProfileComplete && ( + {/* Setup Banner (Creator View) */} + {viewMode === "creator" && !isProfileComplete && (
@@ -172,310 +243,590 @@ export default function NexusDashboard() { )}
- {/* Tabs */} - - - Overview - Applications - Contracts - Profile - + {/* Creator View */} + {viewMode === "creator" && ( + <> + {/* Tabs */} + + + Overview + Applications + Contracts + Profile + - {/* Overview Tab */} - -
- {/* Stat: Total Earnings */} - - -
-

Total Earnings

- -
-

- ${(payoutInfo?.total_earnings || 0).toLocaleString('en-US', { minimumFractionDigits: 2 })} -

-
-
- - {/* Stat: Pending Payouts */} - - -
-

Pending Payouts

- -
-

- ${(payoutInfo?.pending_payouts || 0).toLocaleString('en-US', { minimumFractionDigits: 2 })} -

-
-
- - {/* Stat: Pending Applications */} - - -
-

Pending Applications

- -
-

{pendingApplications}

-
-
- - {/* Stat: Active Contracts */} - - -
-

Active Contracts

- -
-

{activeContracts}

-
-
-
- - {/* Recent Applications */} - - - Recent Applications - Your most recent bids - - - {applications.length === 0 ? ( -
- -

No applications yet. Browse opportunities to get started!

- -
- ) : ( -
- {applications.slice(0, 5).map((app: any) => ( -
-
-

{app.opportunity?.title}

-

{app.opportunity?.category}

-
- - {app.status} - + {/* Overview Tab */} + +
+ {/* Stat: Total Earnings */} + + +
+

Total Earnings

+
- ))} -
- )} - - +

+ ${(payoutInfo?.total_earnings || 0).toLocaleString('en-US', { minimumFractionDigits: 2 })} +

+ + - {/* CTA Section */} - {applications.length < 3 && ( - - -

Ready to Earn?

-

- Browse thousands of opportunities from clients looking for talented creators -

- -
-
- )} -
- - {/* Applications Tab */} - - - - My Applications - Track all your bids and applications - - - {applications.length === 0 ? ( -
- -

No applications submitted yet

- -
- ) : ( -
- {applications.map((app: any) => ( -
-
-
-

{app.opportunity?.title}

-

{app.opportunity?.description?.substring(0, 100)}...

-
- {app.status} -
-
- Proposed: ${app.proposed_rate?.toLocaleString()}/hr - Submitted {new Date(app.created_at).toLocaleDateString()} -
+ {/* Stat: Pending Payouts */} + + +
+

Pending Payouts

+
- ))} -
- )} - - - +

+ ${(payoutInfo?.pending_payouts || 0).toLocaleString('en-US', { minimumFractionDigits: 2 })} +

+ + - {/* Contracts Tab */} - - - - Active Contracts - Manage your ongoing work - - - {contracts.length === 0 ? ( -
- -

No active contracts

-
- ) : ( -
- {contracts.map((contract: any) => ( -
-
-
-

{contract.title}

-

Total: ${contract.total_amount?.toLocaleString()}

-
- {contract.status} -
- - {/* Milestones */} - {contract.milestones?.length > 0 && ( -
-

Progress

-
- {contract.milestones.map((m: any) => ( -
- - {m.description} - ${m.amount?.toLocaleString()} -
- ))} -
-
- )} + {/* Stat: Pending Applications */} + + +
+

Pending Applications

+
- ))} -
- )} - - - +

{pendingApplications}

+ + - {/* Profile Tab */} - - - - Your NEXUS Profile - Your marketplace identity - - -
-
- - -
- -
- - -
- -
- - -
- -
- - -
+ {/* Stat: Active Contracts */} + + +
+

Active Contracts

+ +
+

{activeContracts}

+
+
- {/* Verification Status */} - {creatorProfile?.verified && ( -
- - Profile Verified ✓ -
+ {/* Recent Applications */} + + + Recent Applications + Your most recent bids + + + {applications.length === 0 ? ( +
+ +

No applications yet. Browse opportunities to get started!

+ +
+ ) : ( +
+ {applications.slice(0, 5).map((app: any) => ( +
+
+

{app.opportunity?.title}

+

{app.opportunity?.category}

+
+ + {app.status} + +
+ ))} +
+ )} +
+
+ + {/* CTA Section */} + {applications.length < 3 && ( + + +

Ready to Earn?

+

+ Browse thousands of opportunities from clients looking for talented creators +

+ +
+
)} +
-

- To edit your profile, go to Dashboard → Profile Settings -

- - + {/* Applications Tab */} + + + + My Applications + Track all your bids and applications + + + {applications.length === 0 ? ( +
+ +

No applications submitted yet

+ +
+ ) : ( +
+ {applications.map((app: any) => ( +
+
+
+

{app.opportunity?.title}

+

{app.opportunity?.description?.substring(0, 100)}...

+
+ {app.status} +
+
+ Proposed: ${app.proposed_rate?.toLocaleString()}/hr + Submitted {new Date(app.created_at).toLocaleDateString()} +
+
+ ))} +
+ )} +
+
+
- {/* Payout Setup */} - - - Payout Information - Manage how you receive payments - - - -

- Connect your Stripe account to receive payouts for completed contracts -

-
-
- - + {/* Contracts Tab */} + + + + Active Contracts + Manage your ongoing work + + + {contracts.length === 0 ? ( +
+ +

No active contracts

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

{contract.title}

+

Total: ${contract.total_amount?.toLocaleString()}

+
+ {contract.status} +
+ + {/* Milestones */} + {contract.milestones?.length > 0 && ( +
+

Progress

+
+ {contract.milestones.map((m: any) => ( +
+ + {m.description} + ${m.amount?.toLocaleString()} +
+ ))} +
+
+ )} +
+ ))} +
+ )} +
+
+
+ + {/* Profile Tab */} + + + + Your NEXUS Profile + Your marketplace identity + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + {/* Verification Status */} + {creatorProfile?.verified && ( +
+ + Profile Verified ✓ +
+ )} + +

+ To edit your profile, go to Dashboard → Profile Settings +

+
+
+ + {/* Payout Setup */} + + + Payout Information + Manage how you receive payments + + + +

+ Connect your Stripe account to receive payouts for completed contracts +

+
+
+
+ + + )} + + {/* Client View */} + {viewMode === "client" && ( + <> + + + Overview + Opportunities + Applicants + Contracts + + + {/* Overview Tab */} + +
+ {/* Stat: Open Opportunities */} + + +
+

Open Opportunities

+ +
+

{openOpportunities}

+
+
+ + {/* Stat: Total Applicants */} + + +
+

Total Applicants

+ +
+

{applicants.length}

+
+
+ + {/* Stat: Active Contracts */} + + +
+

Active Contracts

+ +
+

{contracts.filter(c => c.status === "active").length}

+
+
+ + {/* Stat: Total Spent */} + + +
+

Total Spent

+ +
+

+ ${(paymentHistory.reduce((acc, p) => acc + (p.amount || 0), 0)).toLocaleString('en-US', { minimumFractionDigits: 2 })} +

+
+
+
+ + {/* Quick Stats */} +
+ + +

Reviewing

+

{applicantStats.applied}

+
+
+ + +

Interviewing

+

{applicantStats.interviewing}

+
+
+ + +

Hired

+

{applicantStats.hired}

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

Hire Top Talent

+

+ Post opportunities and find the perfect creators for your projects +

+ +
+
+
+ + {/* Opportunities Tab */} + + + + My Posted Opportunities + Manage your job postings + + + {postedOpportunities.length === 0 ? ( +
+ +

No opportunities posted yet

+ +
+ ) : ( +
+ {postedOpportunities.map((opp: any) => ( +
+
+
+

{opp.title}

+

{opp.description?.substring(0, 100)}...

+
+ {opp.status} +
+
+ Budget: ${opp.budget?.toLocaleString()} + {opp.applications_count || 0} applications +
+
+ ))} +
+ )} +
+
+
+ + {/* Applicants Tab - Kanban Style */} + +
+ {/* Applied Column */} + + + Applied ({applicantStats.applied}) + + + {applicants.filter(a => a.status === "applied").length === 0 ? ( +

No applicants

+ ) : ( + applicants.filter(a => a.status === "applied").map((app: any) => ( +
+

{app.user?.name}

+

{app.opportunity?.title}

+
+ )) + )} +
+
+ + {/* Interviewing Column */} + + + Interviewing ({applicantStats.interviewing}) + + + {applicants.filter(a => a.status === "interviewing").length === 0 ? ( +

No applicants

+ ) : ( + applicants.filter(a => a.status === "interviewing").map((app: any) => ( +
+

{app.user?.name}

+

{app.opportunity?.title}

+
+ )) + )} +
+
+ + {/* Hired Column */} + + + Hired ({applicantStats.hired}) + + + {applicants.filter(a => a.status === "hired").length === 0 ? ( +

No applicants

+ ) : ( + applicants.filter(a => a.status === "hired").map((app: any) => ( +
+

{app.user?.name}

+

{app.opportunity?.title}

+
+ )) + )} +
+
+
+
+ + {/* Contracts Tab */} + + + + My Active Contracts & Payment History + Track your active work and payments + + + {contracts.length === 0 ? ( +
+ +

No contracts yet

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

{contract.title}

+

with {contract.creator?.name}

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

Total Value

+

${contract.total_amount?.toLocaleString()}

+
+
+

Paid

+

${(contract.paid_amount || 0).toLocaleString()}

+
+
+

Remaining

+

${((contract.total_amount || 0) - (contract.paid_amount || 0)).toLocaleString()}

+
+
+
+ ))} +
+ )} +
+
+ + {/* Payment History */} + {paymentHistory.length > 0 && ( + + + Payment History + Recent payments made + + +
+ {paymentHistory.map((payment: any) => ( +
+
+

{payment.description}

+

{new Date(payment.created_at).toLocaleDateString()}

+
+

${payment.amount?.toLocaleString()}

+
+ ))} +
+
+
+ )} +
+
+ + )}