From 012d05e735635d6969ef5514a7d674c2412eabd5 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Wed, 12 Nov 2025 04:51:14 +0000 Subject: [PATCH] Create Opportunity Posting Form component cgen-46716f58adad44e6b7042700b6564e27 --- .../opportunities/OpportunityPostForm.tsx | 399 ++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 client/pages/opportunities/OpportunityPostForm.tsx diff --git a/client/pages/opportunities/OpportunityPostForm.tsx b/client/pages/opportunities/OpportunityPostForm.tsx new file mode 100644 index 00000000..264e008c --- /dev/null +++ b/client/pages/opportunities/OpportunityPostForm.tsx @@ -0,0 +1,399 @@ +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import Layout from "@/components/Layout"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Loader2, ArrowLeft, Briefcase } from "lucide-react"; +import { createOpportunity } from "@/api/opportunities"; +import { useAuth } from "@/contexts/AuthContext"; +import { useAethexToast } from "@/hooks/use-aethex-toast"; +import type { CreateOpportunityData } from "@/api/opportunities"; + +const JOB_TYPES = [ + "Full-Time", + "Part-Time", + "Contract", + "Freelance", + "Internship", + "Advisory", +]; + +const EXPERIENCE_LEVELS = ["Entry", "Mid", "Senior", "Lead", "Any"]; + +const ARMS = [ + { value: "labs", label: "Labs (Research & Innovation)" }, + { value: "gameforge", label: "GameForge (Game Publishing)" }, + { value: "corp", label: "Corp (Enterprise Services)" }, + { value: "foundation", label: "Foundation (Education)" }, + { value: "nexus", label: "Nexus (Talent Marketplace)" }, +]; + +export default function OpportunityPostForm() { + const navigate = useNavigate(); + const { user } = useAuth(); + const { toast } = useAethexToast(); + const [isSubmitting, setIsSubmitting] = useState(false); + const [errors, setErrors] = useState>({}); + + const [formData, setFormData] = useState({ + title: "", + description: "", + job_type: "Full-Time", + salary_min: undefined, + salary_max: undefined, + experience_level: "Mid", + arm_affiliation: "nexus", + }); + + if (!user) { + return ( + +
+ + + Sign In Required + + +

+ You must be signed in to post opportunities. +

+
+ + +
+
+
+
+
+ ); + } + + const validateForm = () => { + const newErrors: Record = {}; + + if (!formData.title.trim()) { + newErrors.title = "Title is required"; + } + if (!formData.description.trim()) { + newErrors.description = "Description is required"; + } + if (!formData.arm_affiliation) { + newErrors.arm_affiliation = "Arm affiliation is required"; + } + if ( + formData.salary_min && + formData.salary_max && + formData.salary_min > formData.salary_max + ) { + newErrors.salary = "Minimum salary must be less than maximum"; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validateForm()) { + toast({ + title: "Validation Error", + description: "Please fill in all required fields correctly", + variant: "destructive", + }); + return; + } + + setIsSubmitting(true); + + try { + const newOpportunity = await createOpportunity(formData); + toast({ + title: "Success", + description: "Opportunity posted successfully!", + }); + navigate(`/opportunities/${newOpportunity.id}`); + } catch (error) { + console.error("Failed to create opportunity:", error); + toast({ + title: "Error", + description: + error instanceof Error + ? error.message + : "Failed to post opportunity", + variant: "destructive", + }); + } finally { + setIsSubmitting(false); + } + }; + + return ( + +
+ {/* Background */} +
+
+
+
+ +
+ {/* Hero Section */} +
+
+ + +
+ +

+ Post an Opportunity +

+
+

+ Share a job opening, project, or collaboration opportunity with + the AeThex community +

+
+
+ + {/* Form Section */} +
+
+ + + Opportunity Details + + +
+ {/* Title */} +
+ + + setFormData({ ...formData, title: e.target.value }) + } + className={`bg-slate-800 border-slate-600 text-white placeholder-slate-500 ${ + errors.title ? "border-red-500" : "" + }`} + /> + {errors.title && ( +

{errors.title}

+ )} +
+ + {/* Description */} +
+ +