diff --git a/client/components/OpportunitiesWidget.tsx b/client/components/OpportunitiesWidget.tsx new file mode 100644 index 00000000..7af5179d --- /dev/null +++ b/client/components/OpportunitiesWidget.tsx @@ -0,0 +1,209 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Briefcase, MapPin, DollarSign, Clock, ArrowRight, AlertCircle } from "lucide-react"; + +export interface Opportunity { + id: string; + title: string; + category?: string; + budget?: number; + timeline?: string; + status: "open" | "in_progress" | "closed"; + description?: string; + applications_count?: number; + posted_by?: string; + location?: string; + skills_required?: string[]; +} + +interface OpportunitiesWidgetProps { + opportunities: Opportunity[]; + title?: string; + description?: string; + onViewDetails?: (oppId: string) => void; + onApply?: (oppId: string) => void; + showApplyButton?: boolean; + 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", + accent: "bg-purple-600 hover:bg-purple-700", + text: "text-purple-300", + }, + blue: { + bg: "bg-gradient-to-br from-blue-950/40 to-blue-900/20", + border: "border-blue-500/20", + accent: "bg-blue-600 hover:bg-blue-700", + text: "text-blue-300", + }, + cyan: { + bg: "bg-gradient-to-br from-cyan-950/40 to-cyan-900/20", + border: "border-cyan-500/20", + accent: "bg-cyan-600 hover:bg-cyan-700", + text: "text-cyan-300", + }, + green: { + bg: "bg-gradient-to-br from-green-950/40 to-green-900/20", + border: "border-green-500/20", + accent: "bg-green-600 hover:bg-green-700", + text: "text-green-300", + }, + amber: { + bg: "bg-gradient-to-br from-amber-950/40 to-amber-900/20", + border: "border-amber-500/20", + accent: "bg-amber-600 hover:bg-amber-700", + text: "text-amber-300", + }, + red: { + bg: "bg-gradient-to-br from-red-950/40 to-red-900/20", + border: "border-red-500/20", + accent: "bg-red-600 hover:bg-red-700", + text: "text-red-300", + }, +}; + +const statusBadge = { + open: "bg-green-600/50 text-green-100", + in_progress: "bg-blue-600/50 text-blue-100", + closed: "bg-gray-600/50 text-gray-100", +}; + +export function OpportunitiesWidget({ + opportunities, + title = "Opportunities", + description = "Available jobs and projects", + onViewDetails, + onApply, + showApplyButton = true, + accentColor = "purple", +}: OpportunitiesWidgetProps) { + const colors = colorMap[accentColor]; + + return ( + + + + + {title} + + {description} + + + {opportunities.length === 0 ? ( +
+ +

No opportunities available

+
+ ) : ( +
+ {opportunities.map((opp) => ( +
+ {/* Header */} +
+
+

+ {opp.title} +

+ {opp.category && ( +

{opp.category}

+ )} +
+ + {opp.status} + +
+ + {/* Description */} + {opp.description && ( +

+ {opp.description} +

+ )} + + {/* Skills */} + {opp.skills_required && opp.skills_required.length > 0 && ( +
+ {opp.skills_required.slice(0, 3).map((skill) => ( + + {skill} + + ))} + {opp.skills_required.length > 3 && ( + + +{opp.skills_required.length - 3} + + )} +
+ )} + + {/* Meta Information */} +
+ {opp.budget && ( +
+ + ${opp.budget.toLocaleString()} +
+ )} + {opp.timeline && ( +
+ + {opp.timeline} +
+ )} + {opp.location && ( +
+ + {opp.location} +
+ )} + {opp.applications_count !== undefined && ( +
+ + {opp.applications_count} applications +
+ )} +
+ + {/* Actions */} +
+ {onViewDetails && ( + + )} + {showApplyButton && onApply && ( + + )} +
+
+ ))} +
+ )} +
+
+ ); +} + +export default OpportunitiesWidget;