champs search
This commit is contained in:
parent
344fccac45
commit
7a4950ea83
2 changed files with 213 additions and 102 deletions
|
@ -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,12 +26,23 @@ const JobList: React.FC = () => {
|
|||
const [jobs, setJobs] = useState<JobOffer[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [searchTerm, setSearchTerm] = useState<string>('');
|
||||
const [locationTerm, setLocationTerm] = useState<string>('');
|
||||
const [contractType, setContractType] = useState<string>('');
|
||||
const [sortBy, setSortBy] = useState<string>('publicationDate');
|
||||
const [sortOrder, setSortOrder] = useState<string>('desc');
|
||||
|
||||
useEffect(() => {
|
||||
const fetchJobs = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await axios.get<JobSearchResponse>(API_BASE_URL);
|
||||
const params = {
|
||||
keyword: searchTerm,
|
||||
location: locationTerm,
|
||||
contractType: contractType,
|
||||
sortBy: sortBy,
|
||||
sortOrder: sortOrder,
|
||||
};
|
||||
const response = await axios.get<JobSearchResponse>(API_BASE_URL, { params });
|
||||
setJobs(response.data.jobs);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
|
@ -38,8 +54,21 @@ const JobList: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
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 (
|
||||
<Container maxWidth="lg" sx={{ mt: 2, mb: 4 }}>
|
||||
|
@ -47,6 +76,83 @@ const JobList: React.FC = () => {
|
|||
Découvrez les dernières offres d'emploi
|
||||
</Typography>
|
||||
|
||||
{/* Formulaire de recherche et filtres */}
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
p: 3,
|
||||
mb: 4,
|
||||
borderRadius: '12px',
|
||||
boxShadow: '0px 4px 10px rgba(0, 0, 0, 0.05)',
|
||||
backgroundColor: 'background.paper',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={handleSearchSubmit}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: 2,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
label="Mots-clés"
|
||||
variant="outlined"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
sx={{ flexGrow: 1, minWidth: { xs: '100%', sm: '200px' } }}
|
||||
/>
|
||||
<TextField
|
||||
label="Localisation"
|
||||
variant="outlined"
|
||||
value={locationTerm}
|
||||
onChange={(e) => setLocationTerm(e.target.value)}
|
||||
sx={{ flexGrow: 1, minWidth: { xs: '100%', sm: '180px' } }}
|
||||
/>
|
||||
<Select
|
||||
value={contractType}
|
||||
onChange={(e) => setContractType(e.target.value as string)}
|
||||
displayEmpty
|
||||
sx={{ minWidth: { xs: '100%', sm: '160px' } }}
|
||||
>
|
||||
<MenuItem value="">Tous les contrats</MenuItem>
|
||||
<MenuItem value="CDI">CDI</MenuItem>
|
||||
<MenuItem value="CDD">CDD</MenuItem>
|
||||
<MenuItem value="INTERIM">Intérim</MenuItem>
|
||||
<MenuItem value="SAISONNIER">Saisonnier</MenuItem>
|
||||
<MenuItem value="STAGE">Stage</MenuItem>
|
||||
<MenuItem value="ALTERNANCE">Alternance</MenuItem>
|
||||
</Select>
|
||||
|
||||
<Select
|
||||
value={`${sortBy}:${sortOrder}`}
|
||||
onChange={handleSortChange}
|
||||
displayEmpty
|
||||
sx={{ minWidth: { xs: '100%', sm: '220px' } }}
|
||||
>
|
||||
<MenuItem value="publicationDate:desc">Plus récent</MenuItem>
|
||||
<MenuItem value="publicationDate:asc">Plus ancien</MenuItem>
|
||||
<MenuItem value="title:asc">Titre (A-Z)</MenuItem>
|
||||
<MenuItem value="title:desc">Titre (Z-A)</MenuItem>
|
||||
</Select>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
minWidth: { xs: '100%', sm: '120px' },
|
||||
py: 1.5,
|
||||
}}
|
||||
>
|
||||
Rechercher
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
{loading && (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 4 }}>
|
||||
<CircularProgress />
|
||||
|
@ -69,6 +175,7 @@ const JobList: React.FC = () => {
|
|||
<Grid container spacing={3}>
|
||||
{jobs.map((job) => (
|
||||
<Grid item xs={12} sm={6} md={4} key={job.id}>
|
||||
<Box sx={{ height: '100%' }}>
|
||||
<Card
|
||||
sx={{
|
||||
height: '100%',
|
||||
|
@ -145,6 +252,7 @@ const JobList: React.FC = () => {
|
|||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
|
|
|
@ -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,9 +27,7 @@ interface SidebarProps {
|
|||
}
|
||||
|
||||
const Sidebar: React.FC<SidebarProps> = ({ drawerWidth }) => {
|
||||
return (
|
||||
<Drawer
|
||||
sx={{
|
||||
const drawerStyles: SxProps<Theme> = {
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
'& .MuiDrawer-paper': { // Style du "papier" (fond) de la sidebar
|
||||
|
@ -40,7 +39,11 @@ const Sidebar: React.FC<SidebarProps> = ({ drawerWidth }) => {
|
|||
overflowX: 'hidden', // Empêche le débordement horizontal
|
||||
borderRadius: '0 12px 12px 0', // Bords arrondis seulement à droite
|
||||
},
|
||||
}}
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
sx={drawerStyles}
|
||||
variant="permanent" // La sidebar est toujours visible
|
||||
anchor="left" // Positionnée à gauche
|
||||
>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue