diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fb2beeb..2c80ccf 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,11 +1,9 @@ -```typescript // job/frontend/src/App.tsx import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; -import JobList from './components/JobList'; +import JobSearch from './components/JobSearch'; import JobDetail from './components/JobDetail'; -// import Navbar from './components/Navbar'; // <-- SUPPRIMER CET IMPORT -import Sidebar from './components/Sidebar'; // Garde cet import +import Sidebar from './components/Sidebar'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import CssBaseline from '@mui/material/CssBaseline'; import Box from '@mui/material/Box'; @@ -35,7 +33,7 @@ const customTheme = createTheme({ button: { textTransform: 'none' }, }, components: { - MuiAppBar: { // Optionnel : Si vous voulez garder une AppBar très minimale pour des actions spécifiques (sans navigation) + MuiAppBar: { styleOverrides: { colorPrimary: { backgroundColor: '#FFFFFF', color: '#212121', boxShadow: '0px 1px 4px rgba(0, 0, 0, 0.04)', @@ -94,28 +92,21 @@ const App: React.FC = () => { - {/* Conteneur principal flex */} - - {/* Sidebar : elle prend sa largeur fixe */} + - - {/* Contenu principal : prend l'espace restant et a une marge à gauche */} - {/* Ici seront rendues vos routes de contenu */} - } /> + } /> } /> @@ -125,5 +116,4 @@ const App: React.FC = () => { ); }; -export default App; -``` \ No newline at end of file +export default App; \ No newline at end of file diff --git a/frontend/src/components/JobDetail.tsx b/frontend/src/components/JobDetail.tsx index 24a903b..8033ccb 100644 --- a/frontend/src/components/JobDetail.tsx +++ b/frontend/src/components/JobDetail.tsx @@ -7,8 +7,11 @@ import { Button, // Bouton "Postuler" CircularProgress, // Indicateur de chargement Paper, // Pour encadrer le contenu de l'offre + IconButton, } from '@mui/material'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; // Icône pour le bouton retour +import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; +import FavoriteIcon from '@mui/icons-material/Favorite'; import type { JobOffer } from '../types'; // Use type-only import @@ -19,6 +22,7 @@ const JobDetail: React.FC = () => { const [job, setJob] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [isFavorite, setIsFavorite] = useState(false); useEffect(() => { const fetchJobDetail = async () => { @@ -27,12 +31,15 @@ const JobDetail: React.FC = () => { try { const response = await axios.get(`${API_BASE_URL}/${id}`); setJob(response.data); + // Vérifie si l'offre est dans les favoris + const favorites = JSON.parse(localStorage.getItem('jobFavorites') || '[]'); + setIsFavorite(favorites.includes(response.data.id)); } catch (err) { - console.error('Error fetching job detail:', err); + console.error('Erreur lors de la récupération des détails de l\'offre:', err); if (axios.isAxiosError(err) && err.response?.status === 404) { - setError('Job offer not found.'); + setError('Offre d\'emploi non trouvée.'); } else { - setError('Failed to load job offer details. Please try again.'); + setError('Impossible de charger les détails de l\'offre. Veuillez réessayer.'); } } finally { setLoading(false); @@ -46,7 +53,7 @@ const JobDetail: React.FC = () => { const handleApplyClick = () => { if (job?.urlOffre) { - window.open(job.urlOffre, '_blank'); // Ouvre l'URL dans un nouvel onglet + window.open(job.urlOffre, '_blank', 'noopener,noreferrer'); } else { // Utilisation d'un alert MUI ou d'un SnackBar serait mieux ici, // mais pour l'instant, gardons l'alert JS pour la simplicité. @@ -54,6 +61,21 @@ const JobDetail: React.FC = () => { } }; + const handleToggleFavorite = () => { + if (!job) return; + + let favorites = JSON.parse(localStorage.getItem('jobFavorites') || '[]'); + if (isFavorite) { + // Supprimer des favoris + favorites = favorites.filter((favId: string) => favId !== job.id); + } else { + // Ajouter aux favoris + favorites.push(job.id); + } + localStorage.setItem('jobFavorites', JSON.stringify(favorites)); + setIsFavorite(!isFavorite); + }; + if (loading) { return ( @@ -109,8 +131,28 @@ const JobDetail: React.FC = () => { borderRadius: '12px', // Bords arrondis boxShadow: '0px 4px 10px rgba(0, 0, 0, 0.05)', // Ombre douce backgroundColor: 'background.paper', // Fond blanc du Paper + position: 'relative', }}> - + {/* Bouton Favori */} + + {isFavorite ? : } + + + {job.title} diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx deleted file mode 100644 index 1169bdd..0000000 --- a/frontend/src/components/Navbar.tsx +++ /dev/null @@ -1,33 +0,0 @@ -// job/frontend/src/components/Navbar.tsx -import React from 'react'; -import { Link } from 'react-router-dom'; -import AppBar from '@mui/material/AppBar'; -import Toolbar from '@mui/material/Toolbar'; -import Typography from '@mui/material/Typography'; -import Button from '@mui/material/Button'; - -const Navbar: React.FC = () => { - return ( - - - - - JobFinder - - - - {/* FUTURE: Ajoutez d'autres boutons ici pour Favoris, Connexion, etc. */} - {/* */} - {/* */} - - - ); -}; - -export default Navbar; diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 6f18654..8f45679 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -36,58 +36,107 @@ const Sidebar: React.FC = ({ drawerWidth }) => { boxSizing: 'border-box', backgroundColor: 'background.paper', // Fond blanc du thème boxShadow: '0px 2px 8px rgba(0, 0, 0, 0.05)', // Ombre douce - borderRadius: '0 8px 8px 0', // Bords arrondis seulement à droite + borderRight: 'none', overflowX: 'hidden', // Empêche le débordement horizontal + borderRadius: '0 12px 12px 0', // Bords arrondis seulement à droite }, }} variant="permanent" // La sidebar est toujours visible anchor="left" // Positionnée à gauche > - {/* Un Toolbar pour s'aligner avec la Navbar en haut */} - {/* Peut être utilisé pour un logo ou un titre dans la sidebar si vous enlevez le titre de la Navbar */} - - {/* Laissez vide ou ajoutez un logo/titre de l'application ici pour une disposition Notion-like */} - - JobFinder Logo {/* Si vous avez un logo */} - JobFinder + {/* Section du haut de la Sidebar (similaire à Notion) */} + + + JobIA - - {/* Ligne de séparation */} + + {/* Permet le défilement si le contenu dépasse */} - + {/* Lien vers la page de recherche */} - + {/* Icône de recherche */} - + {/* FUTURS LIENS DE LA PHASE 3 */} {/* - + - + - + - + */} - + {/* Vous pouvez ajouter d'autres sections de liens ici */} diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index c9ccbd4..9c8a3c4 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -19,7 +19,6 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true },