diff --git a/frontend/src/components/JobList.tsx b/frontend/src/components/JobList.tsx index 4320907..dc1da45 100644 --- a/frontend/src/components/JobList.tsx +++ b/frontend/src/components/JobList.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { Container, Typography, @@ -10,6 +10,11 @@ import { Button, Box, Chip, + TextField, + Select, + MenuItem, + Paper, + SelectChangeEvent, } from '@mui/material'; import { Link } from 'react-router-dom'; import axios from 'axios'; @@ -21,25 +26,49 @@ const JobList: React.FC = () => { const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [searchTerm, setSearchTerm] = useState(''); + const [locationTerm, setLocationTerm] = useState(''); + const [contractType, setContractType] = useState(''); + const [sortBy, setSortBy] = useState('publicationDate'); + const [sortOrder, setSortOrder] = useState('desc'); + + const fetchJobs = async () => { + try { + setLoading(true); + const params = { + keyword: searchTerm, + location: locationTerm, + contractType: contractType, + sortBy: sortBy, + sortOrder: sortOrder, + }; + const response = await axios.get(API_BASE_URL, { params }); + setJobs(response.data.jobs); + setError(null); + } catch (err) { + console.error("Erreur lors de la récupération des offres:", err); + setError("Impossible de charger les offres pour le moment. Veuillez réessayer plus tard."); + setJobs([]); + } finally { + setLoading(false); + } + }; useEffect(() => { - const fetchJobs = async () => { - try { - setLoading(true); - const response = await axios.get(API_BASE_URL); - setJobs(response.data.jobs); - setError(null); - } catch (err) { - console.error("Erreur lors de la récupération des offres:", err); - setError("Impossible de charger les offres pour le moment. Veuillez réessayer plus tard."); - setJobs([]); - } finally { - setLoading(false); - } - }; - fetchJobs(); - }, []); + }, [searchTerm, locationTerm, contractType, sortBy, sortOrder]); + + const handleSearchSubmit = (e: React.FormEvent) => { + e.preventDefault(); + fetchJobs(); + }; + + const handleSortChange = (event: React.ChangeEvent<{ value: unknown }>) => { + const value = event.target.value as string; + const [newSortBy, newSortOrder] = value.split(':'); + setSortBy(newSortBy); + setSortOrder(newSortOrder); + }; return ( @@ -47,6 +76,83 @@ const JobList: React.FC = () => { Découvrez les dernières offres d'emploi + {/* Formulaire de recherche et filtres */} + + + setSearchTerm(e.target.value)} + sx={{ flexGrow: 1, minWidth: { xs: '100%', sm: '200px' } }} + /> + setLocationTerm(e.target.value)} + sx={{ flexGrow: 1, minWidth: { xs: '100%', sm: '180px' } }} + /> + + + + + + + + {loading && ( @@ -69,82 +175,84 @@ const JobList: React.FC = () => { {jobs.map((job) => ( - - - - {job.title} - - - - {job.companyName || 'Entreprise non spécifiée'} - + + + + + {job.title} + + + + {job.companyName || 'Entreprise non spécifiée'} + - - {job.contractType && ( - - )} - {job.locationLabel && ( - - )} - + + {job.contractType && ( + + )} + {job.locationLabel && ( + + )} + - - {job.description} - - - - - - - + {job.description} + + + + + + + + ))} diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 8f45679..90d94cb 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -13,6 +13,7 @@ import { Divider, // Séparateur visuel Box // Conteneur générique } from '@mui/material'; +import type { SxProps, Theme } from '@mui/material/styles'; // Importation des icônes import SearchIcon from '@mui/icons-material/Search'; @@ -26,21 +27,23 @@ interface SidebarProps { } const Sidebar: React.FC = ({ drawerWidth }) => { + const drawerStyles: SxProps = { + width: drawerWidth, + flexShrink: 0, + '& .MuiDrawer-paper': { // Style du "papier" (fond) de la sidebar + width: drawerWidth, + boxSizing: 'border-box', + backgroundColor: 'background.paper', // Fond blanc du thème + boxShadow: '0px 2px 8px rgba(0, 0, 0, 0.05)', // Ombre douce + borderRight: 'none', + overflowX: 'hidden', // Empêche le débordement horizontal + borderRadius: '0 12px 12px 0', // Bords arrondis seulement à droite + }, + }; + return (