From 2ff1292bf68f82f790f6cc8f45010567c358e000 Mon Sep 17 00:00:00 2001 From: sirpiglr <49359077-sirpiglr@users.noreply.replit.com> Date: Sat, 13 Dec 2025 02:25:12 +0000 Subject: [PATCH] Add ecosystem filtering to opportunities and update posting form Adds an 'ecosystem' field to opportunities, enabling filtering on the OpportunitiesHub page and inclusion in the OpportunityPostForm. Also resolves a navigation import error in OpportunitiesHub.tsx. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9203795e-937a-4306-b81d-b4d5c78c240e Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: c9ce4106-de8c-4aae-ad20-8c89a4901395 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7c94b7a0-29c7-4f2e-94ef-44b2153872b7/9203795e-937a-4306-b81d-b4d5c78c240e/aPpJgbb Replit-Helium-Checkpoint-Created: true --- client/api/opportunities.ts | 4 ++ .../pages/opportunities/OpportunitiesHub.tsx | 46 +++++++++++++++++-- .../opportunities/OpportunityPostForm.tsx | 34 ++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/client/api/opportunities.ts b/client/api/opportunities.ts index bce837bb..6def26ef 100644 --- a/client/api/opportunities.ts +++ b/client/api/opportunities.ts @@ -14,6 +14,7 @@ export interface Opportunity { salary_max: number; experience_level: string; arm_affiliation: string; + ecosystem?: string; posted_by_id: string; aethex_creators: OpportunityPoster; status: string; @@ -40,12 +41,14 @@ export interface CreateOpportunityData { salary_max?: number; experience_level?: string; arm_affiliation: string; + ecosystem?: string; } const API_BASE = import.meta.env.VITE_API_BASE || ""; export async function getOpportunities(filters?: { arm?: string; + ecosystem?: string; search?: string; jobType?: string; experienceLevel?: string; @@ -55,6 +58,7 @@ export async function getOpportunities(filters?: { }): Promise { const params = new URLSearchParams(); if (filters?.arm) params.append("arm", filters.arm); + if (filters?.ecosystem) params.append("ecosystem", filters.ecosystem); if (filters?.search) params.append("search", filters.search); if (filters?.jobType) params.append("jobType", filters.jobType); if (filters?.experienceLevel) diff --git a/client/pages/opportunities/OpportunitiesHub.tsx b/client/pages/opportunities/OpportunitiesHub.tsx index 33b84026..4a101a50 100644 --- a/client/pages/opportunities/OpportunitiesHub.tsx +++ b/client/pages/opportunities/OpportunitiesHub.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams, useNavigate } from "react-router-dom"; import Layout from "@/components/Layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -9,7 +9,17 @@ import { OpportunityCard } from "@/components/creator-network/OpportunityCard"; import { ArmFilter } from "@/components/creator-network/ArmFilter"; import type { Opportunity } from "@/api/opportunities"; +const ECOSYSTEMS = [ + { value: "all", label: "All" }, + { value: "roblox", label: "Roblox" }, + { value: "unity", label: "Unity" }, + { value: "web", label: "Web" }, + { value: "audio", label: "Audio" }, + { value: "design", label: "Design" }, +]; + export default function OpportunitiesHub() { + const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); const [opportunities, setOpportunities] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -17,6 +27,9 @@ export default function OpportunitiesHub() { const [selectedArm, setSelectedArm] = useState( searchParams.get("arm") || undefined, ); + const [selectedEcosystem, setSelectedEcosystem] = useState( + searchParams.get("ecosystem") || "all", + ); const [page, setPage] = useState(parseInt(searchParams.get("page") || "1")); const [totalPages, setTotalPages] = useState(0); @@ -26,6 +39,7 @@ export default function OpportunitiesHub() { try { const result = await getOpportunities({ arm: selectedArm, + ecosystem: selectedEcosystem !== "all" ? selectedEcosystem : undefined, search: search || undefined, page, limit: 12, @@ -37,6 +51,7 @@ export default function OpportunitiesHub() { // Update URL params const params = new URLSearchParams(); if (selectedArm) params.set("arm", selectedArm); + if (selectedEcosystem && selectedEcosystem !== "all") params.set("ecosystem", selectedEcosystem); if (search) params.set("search", search); if (page > 1) params.set("page", String(page)); setSearchParams(params); @@ -49,7 +64,7 @@ export default function OpportunitiesHub() { }; fetchOpportunities(); - }, [selectedArm, search, page, setSearchParams]); + }, [selectedArm, selectedEcosystem, search, page, setSearchParams]); const handleSearch = (e: React.FormEvent) => { e.preventDefault(); @@ -61,6 +76,11 @@ export default function OpportunitiesHub() { setPage(1); }; + const handleEcosystemChange = (ecosystem: string) => { + setSelectedEcosystem(ecosystem); + setPage(1); + }; + return (
@@ -98,7 +118,7 @@ export default function OpportunitiesHub() {
{/* Search Bar */} -
+
+ + {/* Ecosystem Filter Tabs */} +
+ {ECOSYSTEMS.map((eco) => ( + + ))} +
@@ -151,6 +190,7 @@ export default function OpportunitiesHub() { onClick={() => { setSearch(""); setSelectedArm(undefined); + setSelectedEcosystem("all"); setPage(1); }} variant="outline" diff --git a/client/pages/opportunities/OpportunityPostForm.tsx b/client/pages/opportunities/OpportunityPostForm.tsx index efa85c1a..a789b0c5 100644 --- a/client/pages/opportunities/OpportunityPostForm.tsx +++ b/client/pages/opportunities/OpportunityPostForm.tsx @@ -37,6 +37,15 @@ const ARMS = [ { value: "nexus", label: "Nexus (Talent Marketplace)" }, ]; +const ECOSYSTEMS = [ + { value: "roblox", label: "Roblox" }, + { value: "unity", label: "Unity" }, + { value: "web", label: "Web Development" }, + { value: "audio", label: "Audio / Music" }, + { value: "design", label: "Design / Art" }, + { value: "other", label: "Other" }, +]; + export default function OpportunityPostForm() { const navigate = useNavigate(); const { user } = useAuth(); @@ -52,6 +61,7 @@ export default function OpportunityPostForm() { salary_max: undefined, experience_level: "Mid", arm_affiliation: "nexus", + ecosystem: "web", }); if (!user) { @@ -267,6 +277,30 @@ export default function OpportunityPostForm() { )} + {/* Ecosystem */} +
+ + +
+ {/* Job Type */}