jobia/frontend/src/components/JobSearch.tsx
2025-05-26 17:31:00 +02:00

148 lines
5.6 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import type { JobOffer, JobSearchResponse } from '../types'; // Importe les types définis
const API_BASE_URL = 'http://localhost:3000/api/jobs'; // L'URL de votre API backend
const JobSearch: React.FC = () => {
const [jobs, setJobs] = useState<JobOffer[]>([]);
const [total, setTotal] = useState<number>(0);
const [page, setPage] = useState<number>(1);
const [limit, setLimit] = useState<number>(10);
const [keyword, setKeyword] = useState<string>('');
const [location, setLocation] = useState<string>('');
const [contractType, setContractType] = useState<string>(''); // Nouvel état pour le type de contrat
const [sortBy, setSortBy] = useState<string>('publicationDate'); // Nouvel état pour le tri par colonne
const [sortOrder, setSortOrder] = useState<string>('desc'); // Nouvel état pour l'ordre de tri (asc/desc)
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchJobs = async () => {
setLoading(true);
setError(null);
try {
const params = {
page,
limit,
keyword: keyword || undefined,
location: location || undefined,
contractType: contractType || undefined, // Ajout du paramètre contractType
sortBy: sortBy, // Ajout du paramètre sortBy
sortOrder: sortOrder, // Ajout du paramètre sortOrder
};
const response = await axios.get<JobSearchResponse>(API_BASE_URL, { params });
setJobs(response.data.jobs);
setTotal(response.data.total);
setPage(response.data.page);
setLimit(response.data.limit);
} catch (err) {
console.error('Error fetching jobs:', err);
setError('Failed to fetch job offers. Please try again later.');
} finally {
setLoading(false);
}
};
fetchJobs();
}, [page, keyword, location, contractType, sortBy, sortOrder, limit]); // Mettre à jour les dépendances
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
setPage(1); // Réinitialise à la première page lors d'une nouvelle recherche
// La fonction fetchJobs sera appelée par useEffect car les états keyword ou location auront changé.
};
const totalPages = Math.ceil(total / limit);
return (
<div className="job-search-container">
<h1>Job Offers</h1>
<form onSubmit={handleSearch}>
<input
type="text"
placeholder="Keyword (e.g., développeur)"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
/>
<input
type="text"
placeholder="Location (e.g., Paris)"
value={location}
onChange={(e) => setLocation(e.target.value)}
/>
{/* Nouveau select pour le type de contrat */}
<select value={contractType} onChange={(e) => setContractType(e.target.value)}>
<option value="">All Contract Types</option>
<option value="CDI">CDI</option>
<option value="CDD">CDD</option>
<option value="MIS">Intérim / Mission</option>
<option value="SAI">Saisonnier</option>
<option value="APP">Apprentissage</option>
<option value="PRO">Professionnalisation</option>
</select>
{/* Nouveaux selects pour le tri */}
<select value={sortBy} onChange={(e) => setSortBy(e.target.value)}>
<option value="publicationDate">Sort by Date</option>
<option value="title">Sort by Title</option>
</select>
<select value={sortOrder} onChange={(e) => setSortOrder(e.target.value)}>
<option value="desc">Descending</option>
<option value="asc">Ascending</option>
</select>
<button type="submit">Search</button>
</form>
{/* ... (le reste de votre JSX pour l'affichage des résultats et la pagination reste inchangé) ... */}
{loading && <p className="info-message">Loading job offers...</p>}
{error && <p className="info-message" style={{ color: 'red' }}>Error: {error}</p>}
{!loading && !error && jobs.length === 0 && (
<p className="info-message">No job offers found for your search criteria.</p>
)}
{!loading && !error && jobs.length > 0 && (
<div>
<p className="info-message">Total offers: {total}</p>
<div className="job-grid">
{jobs.map((job) => (
<div key={job.id} className="job-card">
<h3>{job.title}</h3>
<p><strong>Company:</strong> {job.companyName || 'N/A'}</p>
<p><strong>Location:</strong> {job.locationLabel || job.cityName || 'N/A'}</p>
<p><strong>Contract:</strong> {job.contractLabel || job.contractType || 'N/A'}</p>
<p><strong>Published:</strong> {new Date(job.publicationDate).toLocaleDateString()}</p>
<p className="description">
{job.description?.substring(0, 150)}...
</p>
</div>
))}
</div>
<div className="pagination-controls">
<button
onClick={() => setPage(prev => Math.max(1, prev - 1))}
disabled={page === 1}
>
Previous
</button>
<span>Page {page} of {totalPages}</span>
<button
onClick={() => setPage(prev => Math.min(totalPages, prev + 1))}
disabled={page === totalPages}
>
Next
</button>
</div>
</div>
)}
</div>
);
};
export default JobSearch;