added custom pic
BIN
public/images/projects/cloud_1.png
Normal file
|
After Width: | Height: | Size: 999 KiB |
BIN
public/images/projects/cub3d.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/images/projects/homemade_nas.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/images/projects/minishell.png
Normal file
|
After Width: | Height: | Size: 958 KiB |
BIN
public/images/projects/pong.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 627 B After Width: | Height: | Size: 627 B |
|
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 699 B |
|
Before Width: | Height: | Size: 485 B After Width: | Height: | Size: 485 B |
|
Before Width: | Height: | Size: 502 B After Width: | Height: | Size: 502 B |
|
Before Width: | Height: | Size: 724 B After Width: | Height: | Size: 724 B |
|
Before Width: | Height: | Size: 733 B After Width: | Height: | Size: 733 B |
|
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 446 B |
BIN
public/images/sites/avopieces/mookup/3-devices-black (1).png
Normal file
|
After Width: | Height: | Size: 830 KiB |
BIN
public/images/sites/avopieces/mookup/3-devices-white (1).png
Normal file
|
After Width: | Height: | Size: 821 KiB |
BIN
public/images/sites/avopieces/mookup/desktop (1).png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/images/sites/avopieces/mookup/laptop (1).png
Normal file
|
After Width: | Height: | Size: 434 KiB |
BIN
public/images/sites/avopieces/mookup/mobile-black (1).png
Normal file
|
After Width: | Height: | Size: 711 KiB |
BIN
public/images/sites/avopieces/mookup/mobile-white (1).png
Normal file
|
After Width: | Height: | Size: 237 KiB |
BIN
public/images/sites/avopieces/mookup/tablet-black (1).png
Normal file
|
After Width: | Height: | Size: 3.5 MiB |
BIN
public/images/sites/avopieces/mookup/tablet-white (1).png
Normal file
|
After Width: | Height: | Size: 5.3 MiB |
BIN
public/images/sites/avopieces/pc.png
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
BIN
public/images/sites/avopieces/tablette.png
Normal file
|
After Width: | Height: | Size: 679 KiB |
BIN
public/images/sites/avopieces/tel.png
Normal file
|
After Width: | Height: | Size: 646 KiB |
BIN
public/images/sites/etsidemain/mookup/3-devices-black.png
Normal file
|
After Width: | Height: | Size: 951 KiB |
BIN
public/images/sites/etsidemain/mookup/3-devices-white.png
Normal file
|
After Width: | Height: | Size: 941 KiB |
BIN
public/images/sites/etsidemain/mookup/desktop.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
public/images/sites/etsidemain/mookup/laptop.png
Normal file
|
After Width: | Height: | Size: 483 KiB |
BIN
public/images/sites/etsidemain/mookup/mobile-black.png
Normal file
|
After Width: | Height: | Size: 884 KiB |
BIN
public/images/sites/etsidemain/mookup/mobile-white.png
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
public/images/sites/etsidemain/mookup/tablet-black.png
Normal file
|
After Width: | Height: | Size: 4.6 MiB |
BIN
public/images/sites/etsidemain/mookup/tablet-white.png
Normal file
|
After Width: | Height: | Size: 7.0 MiB |
BIN
public/images/sites/etsidemain/pc.png
Normal file
|
After Width: | Height: | Size: 3.3 MiB |
BIN
public/images/sites/etsidemain/tablette.png
Normal file
|
After Width: | Height: | Size: 907 KiB |
BIN
public/images/sites/etsidemain/tel.png
Normal file
|
After Width: | Height: | Size: 938 KiB |
@ -9,6 +9,8 @@ import ProjectPage from "./pages/ProjectPage";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const IndexWrapper = () => <Index />;
|
||||
|
||||
const App = () => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<TooltipProvider>
|
||||
@ -16,7 +18,7 @@ const App = () => (
|
||||
<Sonner />
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Index />} />
|
||||
<Route path="/" element={<IndexWrapper />} />
|
||||
<Route path="/project/:projectId" element={<ProjectPage />} />
|
||||
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
|
||||
@ -22,12 +22,12 @@ export const Footer = () => {
|
||||
const footerLinks = [
|
||||
{
|
||||
icon: <FileText className="w-4 h-4" />,
|
||||
label: "Mentions légales",
|
||||
label: t("footer.legalNotice"),
|
||||
onClick: () => setShowLegalNotice(true)
|
||||
},
|
||||
{
|
||||
icon: <Cookie className="w-4 h-4" />,
|
||||
label: "Gestion des cookies",
|
||||
label: t("footer.cookieManagement"),
|
||||
onClick: reopenCookiePreferences
|
||||
}
|
||||
];
|
||||
@ -48,21 +48,20 @@ export const Footer = () => {
|
||||
return (
|
||||
<footer className="bg-background/50 backdrop-blur-sm border-t border-border/50">
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
|
||||
<div className="flex flex-col md:flex-row justify-between gap-8 mb-8">
|
||||
{/* Colonne 1: Logo et description */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="space-y-4"
|
||||
className="space-y-4 md:max-w-sm"
|
||||
>
|
||||
<div className="text-2xl font-bold text-gradient">
|
||||
Alexandre Pommier
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
Étudiant développeur à 42, passionné par les technologies web et systèmes.
|
||||
Créateur de solutions innovantes pour un avenir numérique.
|
||||
{t("footer.description")}
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
{socialLinks.map((link, index) => (
|
||||
@ -91,9 +90,9 @@ export const Footer = () => {
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
className="space-y-4"
|
||||
className="space-y-4 md:max-w-xs"
|
||||
>
|
||||
<h3 className="font-semibold text-lg">Navigation</h3>
|
||||
<h3 className="font-semibold text-lg">{t("footer.navigation")}</h3>
|
||||
<nav className="space-y-2">
|
||||
{["home", "projects", "skills", "contact"].map((item) => (
|
||||
<Button
|
||||
@ -120,9 +119,9 @@ export const Footer = () => {
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="space-y-4"
|
||||
className="space-y-4 md:max-w-xs"
|
||||
>
|
||||
<h3 className="font-semibold text-lg">Informations</h3>
|
||||
<h3 className="font-semibold text-lg">{t("footer.information")}</h3>
|
||||
<nav className="space-y-2">
|
||||
{footerLinks.map((link, index) => (
|
||||
<Button
|
||||
@ -151,11 +150,11 @@ export const Footer = () => {
|
||||
className="flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-muted-foreground"
|
||||
>
|
||||
<div className="text-center md:text-left">
|
||||
© {currentYear} Alexandre Pommier. Tous droits réservés.
|
||||
© {currentYear} Alexandre Pommier. {t("footer.copyright")}
|
||||
</div>
|
||||
<div className="flex items-center gap-4 text-xs">
|
||||
<div className="flex items-center gap-1">
|
||||
Construit avec
|
||||
{t("footer.builtWith")}
|
||||
<a
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
|
||||
@ -3,10 +3,36 @@ import { Button } from "@/components/ui/button";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { useLanguage } from "@/contexts/LanguageContext";
|
||||
import { motion } from "framer-motion";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
export const Header = () => {
|
||||
const { theme, toggleTheme } = useTheme();
|
||||
const { language, setLanguage, t } = useLanguage();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Check if we're on a project page
|
||||
const isProjectPage = location.pathname.startsWith('/project/');
|
||||
|
||||
const handleNavigation = (section: string) => {
|
||||
if (isProjectPage) {
|
||||
// If on project page, navigate to home with section anchor
|
||||
navigate(`/#${section}`);
|
||||
} else {
|
||||
// If on home page, scroll to section
|
||||
scrollToSection(section);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogoClick = () => {
|
||||
if (isProjectPage) {
|
||||
// If on project page, navigate to home
|
||||
navigate('/');
|
||||
} else {
|
||||
// If on home page, scroll to top
|
||||
scrollToTop();
|
||||
}
|
||||
};
|
||||
|
||||
const scrollToSection = (id: string) => {
|
||||
const element = document.getElementById(id);
|
||||
@ -30,9 +56,9 @@ export const Header = () => {
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
onClick={scrollToTop}
|
||||
onClick={handleLogoClick}
|
||||
className="text-2xl font-bold text-gradient hover:scale-105 transition-transform duration-200 cursor-pointer"
|
||||
aria-label="Retour en haut de page"
|
||||
aria-label={isProjectPage ? "Retour à l'accueil" : "Retour en haut de page"}
|
||||
>
|
||||
AP
|
||||
</motion.button>
|
||||
@ -44,7 +70,7 @@ export const Header = () => {
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.1 * i }}
|
||||
onClick={() => scrollToSection(item)}
|
||||
onClick={() => handleNavigation(item)}
|
||||
className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
{t(`nav.${item}`)}
|
||||
|
||||
@ -7,40 +7,40 @@ export const ProjectsSection = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
const projects = [
|
||||
{
|
||||
key: "avopieces",
|
||||
icon: <Scale className="w-6 h-6 text-primary" />,
|
||||
image: "/images/sites/avopieces/mookup/3-devices-white (1).png",
|
||||
},
|
||||
{
|
||||
key: "nas",
|
||||
icon: <Server className="w-6 h-6 text-primary" />,
|
||||
image: "/images/projects/nas.svg",
|
||||
image: "/images/projects/homemade_nas.png",
|
||||
},
|
||||
{
|
||||
key: "transcendence",
|
||||
icon: <Gamepad2 className="w-6 h-6 text-primary" />,
|
||||
image: "/images/projects/transcendence.svg",
|
||||
image: "/images/projects/pong.png",
|
||||
},
|
||||
{
|
||||
key: "cloud",
|
||||
icon: <Cloud className="w-6 h-6 text-primary" />,
|
||||
image: "/images/projects/cloud.svg",
|
||||
image: "/images/projects/cloud_1.png",
|
||||
},
|
||||
{
|
||||
key: "minishell",
|
||||
icon: <Terminal className="w-6 h-6 text-primary" />,
|
||||
image: "/images/projects/minishell.svg",
|
||||
},
|
||||
{
|
||||
key: "cube3d",
|
||||
icon: <Box className="w-6 h-6 text-primary" />,
|
||||
image: "/images/projects/cube3d.svg",
|
||||
image: "/images/projects/minishell.png",
|
||||
},
|
||||
{
|
||||
key: "etsidemain",
|
||||
icon: <Globe className="w-6 h-6 text-primary" />,
|
||||
image: "/images/projects/etsidemain.svg",
|
||||
image: "/images/sites/etsidemain/mookup/3-devices-white.png",
|
||||
},
|
||||
{
|
||||
key: "avopieces",
|
||||
icon: <Scale className="w-6 h-6 text-primary" />,
|
||||
image: "/images/projects/avopieces.svg",
|
||||
key: "cube3d",
|
||||
icon: <Box className="w-6 h-6 text-primary" />,
|
||||
image: "/images/projects/cub3d.png",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -59,8 +59,8 @@ export const projectsData: Record<string, ProjectData> = {
|
||||
fr: "Architecture microservices avec Docker Compose, reverse proxy Traefik automatisant les certificats Let's Encrypt, système de backup automatique avec rsync, et monitoring avec Prometheus.",
|
||||
en: "Microservices architecture with Docker Compose, Traefik reverse proxy automating Let's Encrypt certificates, automatic backup system with rsync, and monitoring with Prometheus.",
|
||||
},
|
||||
mainImage: "/images/projects/nas.svg",
|
||||
images: ["/images/projects/nas.svg"],
|
||||
mainImage: "/images/projects/homemade_nas.png",
|
||||
images: ["/images/projects/homemade_nas.png"],
|
||||
technologies: [
|
||||
{ name: "OpenMediaVault" },
|
||||
{ name: "Docker" },
|
||||
@ -113,8 +113,8 @@ export const projectsData: Record<string, ProjectData> = {
|
||||
fr: "Architecture MVC avec NestJS, WebSocket rooms pour les matchs, système de queuing Redis pour le matchmaking, JWT pour l'authentification, et Canvas HTML5 pour le rendu du jeu.",
|
||||
en: "MVC architecture with NestJS, WebSocket rooms for matches, Redis queuing system for matchmaking, JWT for authentication, and HTML5 Canvas for game rendering.",
|
||||
},
|
||||
mainImage: "/images/projects/transcendence.svg",
|
||||
images: ["/images/projects/transcendence.svg"],
|
||||
mainImage: "/images/projects/pong.png",
|
||||
images: ["/images/projects/pong.png"],
|
||||
technologies: [
|
||||
{ name: "React", icon: "⚛️" },
|
||||
{ name: "TypeScript" },
|
||||
@ -167,8 +167,8 @@ export const projectsData: Record<string, ProjectData> = {
|
||||
fr: "Docker Compose pour l'orchestration, Ansible playbooks pour l'automatisation, volumes Docker pour la persistance, et réseau bridge personnalisé pour l'isolation.",
|
||||
en: "Docker Compose for orchestration, Ansible playbooks for automation, Docker volumes for persistence, and custom bridge network for isolation.",
|
||||
},
|
||||
mainImage: "/images/projects/cloud.svg",
|
||||
images: ["/images/projects/cloud.svg"],
|
||||
mainImage: "/images/projects/cloud_1.png",
|
||||
images: ["/images/projects/cloud_1.png"],
|
||||
technologies: [
|
||||
{ name: "Docker", icon: "🐳" },
|
||||
{ name: "Ansible" },
|
||||
@ -220,8 +220,8 @@ export const projectsData: Record<string, ProjectData> = {
|
||||
fr: "Tokenizer/Lexer pour le parsing, AST pour représenter les commandes, gestion des descripteurs de fichiers pour les redirections, et table de hash pour les variables d'environnement.",
|
||||
en: "Tokenizer/Lexer for parsing, AST to represent commands, file descriptor management for redirections, and hash table for environment variables.",
|
||||
},
|
||||
mainImage: "/images/projects/minishell.svg",
|
||||
images: ["/images/projects/minishell.svg"],
|
||||
mainImage: "/images/projects/minishell.png",
|
||||
images: ["/images/projects/minishell.png"],
|
||||
technologies: [
|
||||
{ name: "C" },
|
||||
{ name: "Linux" },
|
||||
@ -272,8 +272,8 @@ export const projectsData: Record<string, ProjectData> = {
|
||||
fr: "Algorithme DDA pour le raycasting, lookup tables pour les calculs trigonométriques, buffer d'image pour le double buffering, et grille 2D pour la détection de collisions.",
|
||||
en: "DDA algorithm for raycasting, lookup tables for trigonometric calculations, image buffer for double buffering, and 2D grid for collision detection.",
|
||||
},
|
||||
mainImage: "/images/projects/cube3d.svg",
|
||||
images: ["/images/projects/cube3d.svg"],
|
||||
mainImage: "/images/projects/cub3d.png",
|
||||
images: ["/images/projects/cub3d.png"],
|
||||
technologies: [
|
||||
{ name: "C" },
|
||||
{ name: "MiniLibX" },
|
||||
@ -283,7 +283,7 @@ export const projectsData: Record<string, ProjectData> = {
|
||||
},
|
||||
etsidemain: {
|
||||
id: "etsidemain",
|
||||
title: { fr: "etsidemain.com", en: "etsidemain.com" },
|
||||
title: { fr: "Site Et si demain...", en: "Et si demain... Website" },
|
||||
shortDescription: {
|
||||
fr: "Site web vitrine pour cabinet de conseil en transformation régénérative. Design moderne et responsive avec animations CSS, formulaire de contact et optimisations SEO.",
|
||||
en: "Showcase website for regenerative transformation consulting firm. Modern responsive design with CSS animations, contact form and SEO optimizations.",
|
||||
@ -324,8 +324,17 @@ export const projectsData: Record<string, ProjectData> = {
|
||||
fr: "HTML5/CSS3 sémantique, animations avec transitions CSS et Intersection Observer, balises meta optimisées, structure de données JSON-LD, et optimisation des assets.",
|
||||
en: "Semantic HTML5/CSS3, animations with CSS transitions and Intersection Observer, optimized meta tags, JSON-LD data structure, and asset optimization.",
|
||||
},
|
||||
mainImage: "/images/projects/etsidemain.svg",
|
||||
images: ["/images/projects/etsidemain.svg"],
|
||||
mainImage: "/images/sites/etsidemain/mookup/3-devices-white.png",
|
||||
images: [
|
||||
"/images/sites/etsidemain/mookup/3-devices-white.png",
|
||||
"/images/sites/etsidemain/mookup/desktop.png",
|
||||
"/images/sites/etsidemain/mookup/laptop.png",
|
||||
"/images/sites/etsidemain/mookup/tablet-white.png",
|
||||
"/images/sites/etsidemain/mookup/mobile-white.png",
|
||||
"/images/sites/etsidemain/pc.png",
|
||||
"/images/sites/etsidemain/tablette.png",
|
||||
"/images/sites/etsidemain/tel.png"
|
||||
],
|
||||
technologies: [
|
||||
{ name: "HTML5" },
|
||||
{ name: "CSS3" },
|
||||
@ -336,7 +345,7 @@ export const projectsData: Record<string, ProjectData> = {
|
||||
},
|
||||
avopieces: {
|
||||
id: "avopieces",
|
||||
title: { fr: "avopieces.fr", en: "avopieces.fr" },
|
||||
title: { fr: "Site Avo Pièces", en: "Avo Pieces Website" },
|
||||
shortDescription: {
|
||||
fr: "Plateforme juridique intelligente pour le cabinet AvoCab, spécialisée dans les procédures de divorce. Intègre un chatbot IA analysant les documents uploadés, système de gestion de comptes client/admin, prise de RDV en ligne et vitrine du cabinet.",
|
||||
en: "Intelligent legal platform for AvoCab law firm, specialized in divorce procedures. Features AI chatbot analyzing uploaded documents, client/admin account management system, online appointment booking and law firm showcase.",
|
||||
@ -377,8 +386,17 @@ export const projectsData: Record<string, ProjectData> = {
|
||||
fr: "Architecture MERN stack, RAG (Retrieval Augmented Generation) pour le chatbot, chiffrement des données, système de roles et permissions, API RESTful, et design system cohérent.",
|
||||
en: "MERN stack architecture, RAG (Retrieval Augmented Generation) for chatbot, data encryption, roles and permissions system, RESTful API, and consistent design system.",
|
||||
},
|
||||
mainImage: "/images/projects/avopieces.svg",
|
||||
images: ["/images/projects/avopieces.svg"],
|
||||
mainImage: "/images/sites/avopieces/mookup/3-devices-white (1).png",
|
||||
images: [
|
||||
"/images/sites/avopieces/mookup/3-devices-white (1).png",
|
||||
"/images/sites/avopieces/mookup/desktop (1).png",
|
||||
"/images/sites/avopieces/mookup/laptop (1).png",
|
||||
"/images/sites/avopieces/mookup/tablet-white (1).png",
|
||||
"/images/sites/avopieces/mookup/mobile-white (1).png",
|
||||
"/images/sites/avopieces/pc.png",
|
||||
"/images/sites/avopieces/tablette.png",
|
||||
"/images/sites/avopieces/tel.png"
|
||||
],
|
||||
technologies: [
|
||||
{ name: "React", icon: "⚛️" },
|
||||
{ name: "Node.js", icon: "📗" },
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { useEffect } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { Header } from "@/components/Header";
|
||||
import { Footer } from "@/components/Footer";
|
||||
import { ScrollProgress } from "@/components/ScrollProgress";
|
||||
@ -11,6 +13,20 @@ import { LanguageProvider } from "@/contexts/LanguageContext";
|
||||
import { CookieBannerProvider } from "@/contexts/CookieBannerContext";
|
||||
|
||||
const Index = () => {
|
||||
const location = useLocation();
|
||||
|
||||
// Handle navigation with hash from project pages
|
||||
useEffect(() => {
|
||||
if (location.hash) {
|
||||
const element = document.getElementById(location.hash.substring(1));
|
||||
if (element) {
|
||||
setTimeout(() => {
|
||||
element.scrollIntoView({ behavior: "smooth" });
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}, [location.hash]);
|
||||
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<LanguageProvider>
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { useParams, useNavigate, useLocation } from "react-router-dom";
|
||||
import { useEffect } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { ArrowLeft, Github, ExternalLink, Mail } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { ScrollProgress } from "@/components/ScrollProgress";
|
||||
import { Header } from "@/components/Header";
|
||||
import { ImageGallery } from "@/components/ImageGallery";
|
||||
import { SkillBadge } from "@/components/SkillBadge";
|
||||
import { useLanguage } from "@/contexts/LanguageContext";
|
||||
@ -16,9 +18,15 @@ import { LanguageProvider } from "@/contexts/LanguageContext";
|
||||
const ProjectPageContent = () => {
|
||||
const { projectId } = useParams<{ projectId: string }>();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { language, t } = useLanguage();
|
||||
const { theme } = useTheme();
|
||||
|
||||
// Reset scroll position when projectId changes
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [projectId, location.pathname]);
|
||||
|
||||
const project = projectId ? projectsData[projectId] : null;
|
||||
|
||||
if (!project) {
|
||||
@ -38,19 +46,10 @@ const ProjectPageContent = () => {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<ScrollProgress />
|
||||
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-40 w-full border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
|
||||
<Button variant="ghost" onClick={() => navigate("/")} className="gap-2">
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
{t("project.back") || "Back"}
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
<Header />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="py-20 md:py-32">
|
||||
<section className="py-20 md:py-32 mt-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
|
||||
@ -37,11 +37,11 @@ export const translations = {
|
||||
description: "Moteur RayCaster 3D inspiré de Wolfenstein 3D, développé avec MiniLibX et algorithmes graphiques avancés.",
|
||||
},
|
||||
etsidemain: {
|
||||
title: "etsidemain.com",
|
||||
title: "Site Et si demain...",
|
||||
description: "Site web vitrine pour cabinet de conseil en transformation régénérative. Design moderne et responsive avec animations CSS, formulaire de contact et optimisations SEO.",
|
||||
},
|
||||
avopieces: {
|
||||
title: "avopieces.fr",
|
||||
title: "Site Avo Pièces",
|
||||
description: "Plateforme juridique intelligente pour le cabinet AvoCab, spécialisée dans les procédures de divorce. Intègre un chatbot IA analysant les documents uploadés, système de gestion de comptes client/admin, prise de RDV en ligne et vitrine du cabinet.",
|
||||
},
|
||||
},
|
||||
@ -58,6 +58,15 @@ export const translations = {
|
||||
email: "Email",
|
||||
github: "GitHub",
|
||||
},
|
||||
footer: {
|
||||
description: "Étudiant développeur à 42, passionné par les technologies web et systèmes. Créateur de solutions innovantes pour un avenir numérique.",
|
||||
navigation: "Navigation",
|
||||
information: "Informations",
|
||||
legalNotice: "Mentions légales",
|
||||
cookieManagement: "Gestion des cookies",
|
||||
copyright: "Tous droits réservés.",
|
||||
builtWith: "Construit avec",
|
||||
},
|
||||
project: {
|
||||
back: "Retour",
|
||||
backToHome: "Retour à l'accueil",
|
||||
@ -112,11 +121,11 @@ export const translations = {
|
||||
description: "3D RayCaster engine inspired by Wolfenstein 3D, developed with MiniLibX and advanced graphics algorithms.",
|
||||
},
|
||||
etsidemain: {
|
||||
title: "etsidemain.com",
|
||||
title: "Et si demain... Website",
|
||||
description: "Showcase website for regenerative transformation consulting firm. Modern responsive design with CSS animations, contact form and SEO optimizations.",
|
||||
},
|
||||
avopieces: {
|
||||
title: "avopieces.fr",
|
||||
title: "Avo Pieces Website",
|
||||
description: "Intelligent legal platform for AvoCab law firm, specialized in divorce procedures. Features AI chatbot analyzing uploaded documents, client/admin account management system, online appointment booking and law firm showcase.",
|
||||
},
|
||||
},
|
||||
@ -133,6 +142,15 @@ export const translations = {
|
||||
email: "Email",
|
||||
github: "GitHub",
|
||||
},
|
||||
footer: {
|
||||
description: "Developer student at 42, passionate about web technologies and systems. Creator of innovative solutions for a digital future.",
|
||||
navigation: "Navigation",
|
||||
information: "Information",
|
||||
legalNotice: "Legal Notice",
|
||||
cookieManagement: "Cookie Management",
|
||||
copyright: "All rights reserved.",
|
||||
builtWith: "Built with",
|
||||
},
|
||||
project: {
|
||||
back: "Back",
|
||||
backToHome: "Back to Home",
|
||||
|
||||
@ -8,6 +8,13 @@ export default defineConfig(({ mode }) => ({
|
||||
server: {
|
||||
host: "::",
|
||||
port: 8080,
|
||||
allowedHosts: [
|
||||
"alexandre-pommier.com",
|
||||
"www.alexandre-pommier.com",
|
||||
"localhost",
|
||||
"127.0.0.1",
|
||||
"0.0.0.0"
|
||||
]
|
||||
},
|
||||
plugins: [react(), mode === "development" && componentTagger()].filter(Boolean),
|
||||
resolve: {
|
||||
|
||||