From 72eb16a4f6b2726bf432b0a414ac514007e09dea Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Sat, 15 Nov 2025 09:21:17 +0000 Subject: [PATCH] Create reusable Contracts widget for marketplace cgen-a66166315fa44c029eb7f030b503f035 --- client/components/ContractsWidget.tsx | 194 ++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 client/components/ContractsWidget.tsx diff --git a/client/components/ContractsWidget.tsx b/client/components/ContractsWidget.tsx new file mode 100644 index 00000000..a067af89 --- /dev/null +++ b/client/components/ContractsWidget.tsx @@ -0,0 +1,194 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { FileText, AlertCircle, CheckCircle, Clock, DollarSign } from "lucide-react"; + +export interface Contract { + id: string; + title: string; + client_name?: string; + creator_name?: string; + status: "active" | "completed" | "paused" | "cancelled"; + total_amount: number; + paid_amount?: number; + start_date?: string; + end_date?: string; + description?: string; + milestones?: Milestone[]; +} + +export interface Milestone { + id: string; + description: string; + amount: number; + status: "pending" | "approved" | "paid"; +} + +interface ContractsWidgetProps { + contracts: Contract[]; + title?: string; + description?: string; + type?: "creator" | "client"; + accentColor?: "purple" | "blue" | "cyan" | "green" | "amber" | "red"; +} + +const colorMap = { + purple: { + bg: "bg-gradient-to-br from-purple-950/40 to-purple-900/20", + border: "border-purple-500/20", + text: "text-purple-300", + }, + blue: { + bg: "bg-gradient-to-br from-blue-950/40 to-blue-900/20", + border: "border-blue-500/20", + text: "text-blue-300", + }, + cyan: { + bg: "bg-gradient-to-br from-cyan-950/40 to-cyan-900/20", + border: "border-cyan-500/20", + text: "text-cyan-300", + }, + green: { + bg: "bg-gradient-to-br from-green-950/40 to-green-900/20", + border: "border-green-500/20", + text: "text-green-300", + }, + amber: { + bg: "bg-gradient-to-br from-amber-950/40 to-amber-900/20", + border: "border-amber-500/20", + text: "text-amber-300", + }, + red: { + bg: "bg-gradient-to-br from-red-950/40 to-red-900/20", + border: "border-red-500/20", + text: "text-red-300", + }, +}; + +const statusMap = { + active: { color: "bg-green-600/50 text-green-100", icon: CheckCircle }, + completed: { color: "bg-blue-600/50 text-blue-100", icon: CheckCircle }, + paused: { color: "bg-yellow-600/50 text-yellow-100", icon: Clock }, + cancelled: { color: "bg-red-600/50 text-red-100", icon: AlertCircle }, +}; + +export function ContractsWidget({ + contracts, + title = "My Contracts", + description = "Active and completed projects", + type = "creator", + accentColor = "purple", +}: ContractsWidgetProps) { + const colors = colorMap[accentColor]; + const activeContracts = contracts.filter(c => c.status === "active"); + + return ( + + + + + {title} + + {description} + + + {contracts.length === 0 ? ( +
+ +

No contracts yet

+
+ ) : ( +
+ {contracts.map((contract) => { + const StatusIcon = statusMap[contract.status].icon; + const progress = contract.paid_amount && contract.total_amount + ? Math.round((contract.paid_amount / contract.total_amount) * 100) + : 0; + + return ( +
+
+
+

+ {contract.title} +

+

+ {type === "creator" ? ( + <>with {contract.client_name || "Client"} + ) : ( + <>with {contract.creator_name || "Creator"} + )} +

+
+ + {contract.status} + +
+ + {contract.description && ( +

{contract.description}

+ )} + +
+
+

Total Value

+

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

+
+ {contract.paid_amount !== undefined && ( +
+

Paid

+

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

+
+ )} +
+ + {contract.milestones && contract.milestones.length > 0 && ( +
+

Milestones

+
+ {contract.milestones.map((m) => ( +
+
+
+ + {m.description} + +
+ + ${m.amount.toLocaleString()} + +
+ ))} +
+
+ )} +
+ ); + })} +
+ )} + + + ); +} + +export default ContractsWidget;