"use client" import { useSearchParams } from "next/navigation" import { useEffect, useState, useMemo, Suspense } from "react" import { Navbar } from "@/components/navbar" import { Footer } from "@/components/footer" import { JobCard } from "@/components/job-card" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Badge } from "@/components/ui/badge" import { Card, CardContent } from "@/components/ui/card" import { PageSkeleton } from "@/components/loading-skeletons" import { mockJobs } from "@/lib/mock-data" import { jobsApi, transformApiJobToFrontend } from "@/lib/api" import { useDebounce } from "@/hooks/use-utils" import { useTranslation } from "@/lib/i18n" import { Search, MapPin, Briefcase, SlidersHorizontal, X, ArrowUpDown } from "lucide-react" import { motion, AnimatePresence } from "framer-motion" import type { Job } from "@/lib/types" function JobsContent() { const { t } = useTranslation() const [jobs, setJobs] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [searchTerm, setSearchTerm] = useState("") const [locationFilter, setLocationFilter] = useState("all") const [typeFilter, setTypeFilter] = useState("all") const [workModeFilter, setWorkModeFilter] = useState("all") const [sortBy, setSortBy] = useState("recent") const [showFilters, setShowFilters] = useState(false) const searchParams = useSearchParams() useEffect(() => { const tech = searchParams.get("tech") const q = searchParams.get("q") const type = searchParams.get("type") if (tech || q) { setSearchTerm(tech || q || "") setShowFilters(true) // Show filters if searching } if (type === "remoto") { setWorkModeFilter("remote") setShowFilters(true) } }, [searchParams]) const [currentPage, setCurrentPage] = useState(1) const ITEMS_PER_PAGE = 10 useEffect(() => { let isMounted = true const fetchJobs = async () => { setLoading(true) setError(null) try { // Fetch many jobs to allow client-side filtering and pagination const response = await jobsApi.list({ limit: 1000, page: 1 }) const mappedJobs = response.data.map(transformApiJobToFrontend) if (isMounted) { setJobs(mappedJobs) } } catch (err) { console.error("Erro ao buscar vagas", err) if (isMounted) { setError(t('jobs.error')) setJobs(mockJobs) } } finally { if (isMounted) { setLoading(false) } } } fetchJobs() return () => { isMounted = false } }, []) // Debounce search term para otimizar performance const debouncedSearchTerm = useDebounce(searchTerm, 300) // Reset page when filters change useEffect(() => { setCurrentPage(1) }, [debouncedSearchTerm, locationFilter, typeFilter, workModeFilter, sortBy]) // Extrair valores únicos para os filtros const uniqueLocations = useMemo(() => { const locations = jobs.map(job => job.location) return Array.from(new Set(locations)) }, [jobs]) const uniqueTypes = useMemo(() => { const types = jobs.map(job => job.type) return Array.from(new Set(types)) }, [jobs]) const uniqueWorkModes = useMemo(() => { const modes = jobs.map(job => job.workMode).filter(Boolean) as string[] return Array.from(new Set(modes)) }, [jobs]) const filteredAndSortedJobs = useMemo(() => { let filtered = jobs.filter((job) => { const matchesSearch = job.title.toLowerCase().includes(debouncedSearchTerm.toLowerCase()) || job.company.toLowerCase().includes(debouncedSearchTerm.toLowerCase()) || job.description.toLowerCase().includes(debouncedSearchTerm.toLowerCase()) const matchesLocation = locationFilter === "all" || job.location.includes(locationFilter) const matchesType = typeFilter === "all" || job.type === typeFilter const matchesWorkMode = workModeFilter === "all" || job.workMode === workModeFilter return matchesSearch && matchesLocation && matchesType && matchesWorkMode }) // Ordenação switch (sortBy) { case "recent": filtered.sort((a, b) => new Date(b.postedAt).getTime() - new Date(a.postedAt).getTime()) break case "title": filtered.sort((a, b) => a.title.localeCompare(b.title)) break case "company": filtered.sort((a, b) => a.company.localeCompare(b.company)) break case "location": filtered.sort((a, b) => a.location.localeCompare(b.location)) break default: break } return filtered }, [debouncedSearchTerm, locationFilter, typeFilter, workModeFilter, sortBy, jobs]) // Pagination Logic const totalPages = Math.ceil(filteredAndSortedJobs.length / ITEMS_PER_PAGE) const paginatedJobs = filteredAndSortedJobs.slice( (currentPage - 1) * ITEMS_PER_PAGE, currentPage * ITEMS_PER_PAGE ) const hasActiveFilters = searchTerm || locationFilter !== "all" || typeFilter !== "all" || workModeFilter !== "all" const clearFilters = () => { setSearchTerm("") setLocationFilter("all") setTypeFilter("all") setWorkModeFilter("all") } return ( <> {/* Hero Section */}
{t('jobs.title')} {loading ? t('jobs.loading') : t('jobs.subtitle', { count: jobs.length })}
{/* Search and Filters Section */}
{/* Main Search Bar */}
setSearchTerm(e.target.value)} className="pl-10 h-12" />
{/* Advanced Filters */} {showFilters && (
{hasActiveFilters && ( )}
)}
{/* Results Summary */}
{t('jobs.pagination.showing', { from: (currentPage - 1) * ITEMS_PER_PAGE + 1, to: Math.min(currentPage * ITEMS_PER_PAGE, filteredAndSortedJobs.length), total: filteredAndSortedJobs.length })} {hasActiveFilters && (
Filtros ativos: {searchTerm && ( "{searchTerm}" )} {locationFilter !== "all" && ( {locationFilter} )} {typeFilter !== "all" && ( {typeFilter} )} {workModeFilter !== "all" && ( {workModeFilter === "remote" ? "Remoto" : workModeFilter === "hybrid" ? "Híbrido" : workModeFilter === "onsite" ? "Presencial" : workModeFilter} )}
)}
{/* Jobs Grid */}
{error && (
{error}
)} {loading ? (
{t('jobs.loading')}
) : paginatedJobs.length > 0 ? (
{paginatedJobs.map((job, index) => ( ))} {/* Pagination Controls */} {totalPages > 1 && (
{currentPage} / {totalPages}
)}
) : (

{t('jobs.noResults.title')}

{t('jobs.noResults.desc')}

)}
) } export default function VagasPage() { return (
}>
) }