From 246c55b0f51a0b41ae56a8379d5f4f3e2d2ba0cf Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Wed, 24 Dec 2025 11:46:12 -0300 Subject: [PATCH] feat: add advanced filters UI to jobs page Frontend: - Add salaryMin, salaryMax, currencyFilter, visaSupport state - Enable sortBy dropdown with 4 options (recent, salary_desc, salary_asc, relevance) - Add currency filter (BRL, USD, EUR, JPY) - Update api.ts jobsApi.list with new filter params - Fix employmentType query param name (was 'type') All filters now match backend API endpoints --- frontend/src/app/jobs/page.tsx | 38 +++++++++++++++++++++++++++++++--- frontend/src/lib/api.ts | 14 ++++++++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/jobs/page.tsx b/frontend/src/app/jobs/page.tsx index 4861430..74f8009 100644 --- a/frontend/src/app/jobs/page.tsx +++ b/frontend/src/app/jobs/page.tsx @@ -32,9 +32,15 @@ function JobsContent() { const [locationFilter, setLocationFilter] = useState("") const [typeFilter, setTypeFilter] = useState("all") const [workModeFilter, setWorkModeFilter] = useState("all") - const [sortBy, setSortBy] = useState("recent") // Client-side sort or ignore? Backend doesn't support sort yet generally. + const [sortBy, setSortBy] = useState("recent") const [showFilters, setShowFilters] = useState(false) + // Advanced filters + const [salaryMin, setSalaryMin] = useState("") + const [salaryMax, setSalaryMax] = useState("") + const [currencyFilter, setCurrencyFilter] = useState("all") + const [visaSupport, setVisaSupport] = useState(false) + // Pagination state const [currentPage, setCurrentPage] = useState(1) const [totalJobs, setTotalJobs] = useState(0) @@ -82,6 +88,11 @@ function JobsContent() { location: debouncedLocation || undefined, type: typeFilter === "all" ? undefined : typeFilter, workMode: workModeFilter === "all" ? undefined : workModeFilter, + salaryMin: salaryMin ? parseFloat(salaryMin) : undefined, + salaryMax: salaryMax ? parseFloat(salaryMax) : undefined, + currency: currencyFilter === "all" ? undefined : currencyFilter, + visaSupport: visaSupport || undefined, + sortBy: sortBy || undefined, }) // Transform the raw API response to frontend format @@ -121,6 +132,11 @@ function JobsContent() { setLocationFilter("") setTypeFilter("all") setWorkModeFilter("all") + setSalaryMin("") + setSalaryMax("") + setCurrencyFilter("all") + setVisaSupport(false) + setSortBy("recent") } const getTypeLabel = (type: string) => { @@ -244,13 +260,29 @@ function JobsContent() { - - {t('jobs.sort.recent')} + {t('jobs.sort.recent') || 'Mais recentes'} + {t('jobs.sort.salaryDesc') || 'Maior salário'} + {t('jobs.sort.salaryAsc') || 'Menor salário'} + {t('jobs.sort.relevance') || 'Relevância'} + + + + diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 0bbb940..0131763 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -313,15 +313,27 @@ export const jobsApi = { type?: string; workMode?: string; companyId?: string; + // Advanced filters + salaryMin?: number; + salaryMax?: number; + currency?: string; + visaSupport?: boolean; + sortBy?: string; }) => { const query = new URLSearchParams(); if (params.page) query.append("page", params.page.toString()); if (params.limit) query.append("limit", params.limit.toString()); if (params.q) query.append("q", params.q); if (params.location) query.append("location", params.location); - if (params.type) query.append("type", params.type); + if (params.type) query.append("employmentType", params.type); if (params.workMode) query.append("workMode", params.workMode); if (params.companyId) query.append("companyId", params.companyId); + // Advanced filters + if (params.salaryMin) query.append("salaryMin", params.salaryMin.toString()); + if (params.salaryMax) query.append("salaryMax", params.salaryMax.toString()); + if (params.currency) query.append("currency", params.currency); + if (params.visaSupport) query.append("visaSupport", "true"); + if (params.sortBy) query.append("sortBy", params.sortBy); return apiRequest<{ data: ApiJob[];