From b6ad6e77e27a5a49b5685913993636cb9d26528b Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Mon, 22 Dec 2025 19:02:05 -0300 Subject: [PATCH] Fetch admin jobs for dashboard list --- frontend/src/app/dashboard/jobs/page.tsx | 160 +++++++++++++++++------ 1 file changed, 123 insertions(+), 37 deletions(-) diff --git a/frontend/src/app/dashboard/jobs/page.tsx b/frontend/src/app/dashboard/jobs/page.tsx index 1859b19..6977072 100644 --- a/frontend/src/app/dashboard/jobs/page.tsx +++ b/frontend/src/app/dashboard/jobs/page.tsx @@ -1,6 +1,6 @@ "use client" -import { useState } from "react" +import { useEffect, useMemo, useState } from "react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" @@ -19,22 +19,86 @@ import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Plus, Search, Edit, Trash2, Eye } from "lucide-react" -import { mockJobs } from "@/lib/mock-data" +import { adminJobsApi, transformApiJobToFrontend, type AdminJob } from "@/lib/api" + +type AdminJobRow = ReturnType & { + status?: string + applicationsCount?: number +} export default function AdminJobsPage() { const [searchTerm, setSearchTerm] = useState("") - const [jobs, setJobs] = useState(mockJobs) + const [jobs, setJobs] = useState([]) const [isDialogOpen, setIsDialogOpen] = useState(false) - const companyOptions = Array.from(new Set(mockJobs.map((job) => job.company))).sort() + const [isLoading, setIsLoading] = useState(true) + const [errorMessage, setErrorMessage] = useState(null) - const filteredJobs = jobs.filter( - (job) => - job.title.toLowerCase().includes(searchTerm.toLowerCase()) || - job.company.toLowerCase().includes(searchTerm.toLowerCase()), + useEffect(() => { + const loadJobs = async () => { + try { + setIsLoading(true) + setErrorMessage(null) + const jobsData = await adminJobsApi.list({ limit: 100 }) + setJobs(jobsData.data ?? []) + } catch (error) { + console.error("Failed to load jobs:", error) + setErrorMessage("Unable to load jobs right now.") + setJobs([]) + } finally { + setIsLoading(false) + } + } + + loadJobs() + }, []) + + const jobRows = useMemo( + () => + jobs.map((job) => { + const mapped = transformApiJobToFrontend(job) + const applicationsCount = + typeof (job as { applicationsCount?: number }).applicationsCount === "number" + ? (job as { applicationsCount?: number }).applicationsCount + : 0 + return { + ...mapped, + status: job.status, + applicationsCount, + } + }), + [jobs], + ) + + const companyOptions = useMemo( + () => Array.from(new Set(jobRows.map((job) => job.company))).sort(), + [jobRows], + ) + + const filteredJobs = useMemo( + () => + jobRows.filter( + (job) => + job.title.toLowerCase().includes(searchTerm.toLowerCase()) || + job.company.toLowerCase().includes(searchTerm.toLowerCase()), + ), + [jobRows, searchTerm], + ) + + const activeJobs = useMemo( + () => + jobs.filter((job) => + ["published", "open", "active"].includes(job.status?.toLowerCase() ?? ""), + ).length, + [jobs], + ) + + const totalApplications = useMemo( + () => jobRows.reduce((sum, job) => sum + (job.applicationsCount ?? 0), 0), + [jobRows], ) const handleDeleteJob = (id: string) => { - setJobs(jobs.filter((job) => job.id !== id)) + setJobs((prevJobs) => prevJobs.filter((job) => String(job.id) !== id)) } return ( @@ -146,19 +210,21 @@ export default function AdminJobsPage() { Active jobs - {jobs.length} + {activeJobs} Applications - {"436"} + {totalApplications} Conversion rate - 12% + + {jobs.length > 0 ? Math.round((activeJobs / jobs.length) * 100) : 0}% + @@ -192,33 +258,53 @@ export default function AdminJobsPage() { - {filteredJobs.map((job) => ( - - {job.title} - {job.company} - {job.location} - - {job.type} - - {((job.id.charCodeAt(0) * 7) % 50) + 10} - - Active - - -
- - - -
+ {isLoading ? ( + + + Loading jobs... - ))} + ) : errorMessage ? ( + + + {errorMessage} + + + ) : filteredJobs.length === 0 ? ( + + + No jobs found. + + + ) : ( + filteredJobs.map((job) => ( + + {job.title} + {job.company} + {job.location} + + {job.type} + + {job.applicationsCount ?? 0} + + {job.status ?? "Active"} + + +
+ + + +
+
+
+ )) + )}