fix nginx

This commit is contained in:
kinou-p 2025-10-02 18:50:04 +02:00
parent 0afde29e22
commit 1e73530afa
5 changed files with 30 additions and 78 deletions

View File

@ -7,7 +7,7 @@
<!-- DNS Prefetch & Preconnect pour les domaines tiers -->
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<!-- GTM sera chargé dynamiquement après consentement -->
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@ -33,14 +33,25 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
</noscript>
<!-- Google Tag Manager sera chargé dynamiquement après consentement pour optimiser LCP/FCP -->
<!-- Voir src/utils/gtm.ts et src/main.tsx pour l'implémentation -->
<!-- Google Tag Manager - Chargé de manière asynchrone -->
<script>
// Defer GTM loading to improve initial page load
window.addEventListener('load', function() {
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-5V6TCG4C');
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<!-- Google Tag Manager (noscript) - Chargé uniquement sans JavaScript -->
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5V6TCG4C"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>

View File

@ -11,8 +11,7 @@
"lint": "eslint .",
"preview": "vite preview",
"optimize-images": "node scripts/optimize-images.js",
"analyze": "node scripts/analyze-bundle.js",
"analyze:js": "node scripts/analyze-js-bundle.js"
"analyze": "node scripts/analyze-bundle.js"
},
"dependencies": {
"@hookform/resolvers": "^3.10.0",

View File

@ -6,30 +6,15 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { lazy, Suspense } from "react";
import { ThemeProvider } from "./contexts/ThemeContext";
// Lazy load pages et composants lourds pour de meilleures performances
// Cela réduit la quantité de JavaScript chargé initialement
// Lazy load pages and heavy components for better performance
const Index = lazy(() => import("./pages/Index"));
const ProjectPage = lazy(() => import("./pages/ProjectPage"));
const NotFound = lazy(() => import("./pages/NotFound"));
const ParticlesBackground = lazy(() => import("./components/ParticlesBackground").then(m => ({ default: m.ParticlesBackground })));
// ParticlesBackground est chargé en lazy car non critique pour le FCP/LCP
const ParticlesBackground = lazy(() =>
import("./components/ParticlesBackground").then(m => ({ default: m.ParticlesBackground }))
);
const queryClient = new QueryClient();
// Configuration QueryClient optimisée
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
gcTime: 1000 * 60 * 10, // 10 minutes
refetchOnWindowFocus: false,
retry: 1,
},
},
});
// Loading fallback component minimal
// Loading fallback component
const PageLoader = () => (
<div className="min-h-screen flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>

View File

@ -27,16 +27,16 @@ export const ProjectCard = ({ title, description, icon, image, technologies, del
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.2, delay }}
whileHover={{ y: -8, transition: { duration: 0.2 } }}
transition={{ duration: 0.5, delay }}
whileHover={{ y: -5 }}
onClick={handleClick}
className={projectId ? "cursor-pointer" : ""}
>
<Card className="h-full hover:shadow-xl transition-all duration-200 ease-out border-border/50 bg-card/50 backdrop-blur relative overflow-hidden group">
<Card className="h-full hover:shadow-lg transition-all duration-300 border-border/50 bg-card/50 backdrop-blur relative overflow-hidden group">
{/* Indicateur cliquable en bas à droite */}
{projectId && (
<div className="absolute bottom-4 right-4 w-8 h-8 rounded-full bg-primary/10 group-hover:bg-primary/20 flex items-center justify-center transition-all duration-200 ease-out group-hover:scale-125">
<ArrowRight className="w-4 h-4 text-primary group-hover:translate-x-1 transition-transform duration-200 ease-out" />
<div className="absolute bottom-4 right-4 w-8 h-8 rounded-full bg-primary/10 group-hover:bg-primary/20 flex items-center justify-center transition-all duration-300 group-hover:scale-110">
<ArrowRight className="w-4 h-4 text-primary group-hover:translate-x-0.5 transition-transform duration-300" />
</div>
)}

View File

@ -30,60 +30,17 @@ export default defineConfig(({ mode }) => ({
// Chunking optimal pour de meilleures performances
rollupOptions: {
output: {
// Split vendors pour améliorer le cache et réduire le code inutilisé
manualChunks: (id) => {
// Ignorer les node_modules non critiques
if (id.includes('node_modules')) {
// React core - bundle séparé pour un meilleur cache
if (id.includes('/react/') || id.includes('/react-dom/') || id.includes('/scheduler/')) {
return 'react-core';
}
// React Router - souvent utilisé
if (id.includes('/react-router-dom/') || id.includes('/@remix-run/')) {
return 'react-router';
}
// Radix UI - grouper tous les composants ensemble avec tree-shaking
if (id.includes('@radix-ui/')) {
return 'radix-ui';
}
// Framer Motion - animations (peut être volumineux)
if (id.includes('/framer-motion/')) {
return 'animations';
}
// Lucide React - icônes (volumineux)
if (id.includes('/lucide-react/')) {
return 'icons';
}
// tsparticles - animations de fond (optionnel)
if (id.includes('@tsparticles/') || id.includes('/tsparticles/')) {
return 'particles';
}
// TanStack Query
if (id.includes('@tanstack/')) {
return 'tanstack';
}
// Autres dépendances moins critiques
return 'vendor';
}
// Split vendors pour améliorer le cache
manualChunks: {
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
'ui-vendor': ['framer-motion', 'lucide-react'],
'particles': ['@tsparticles/react', '@tsparticles/slim', '@tsparticles/engine'],
},
// Nommer les chunks de manière cohérente pour le cache
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
},
// Optimisations supplémentaires pour le tree-shaking
treeshake: {
moduleSideEffects: 'no-external', // Pas d'effets de bord pour les modules externes
propertyReadSideEffects: false,
unknownGlobalSideEffects: false,
},
},
// Optimisation des assets
assetsInlineLimit: 4096, // Images < 4kb seront inline en base64