Merge pull request 'updating main with the current dev version' (#22) from dev into main
Reviewed-on: #22
This commit is contained in:
commit
64ec67aebd
@ -10,7 +10,7 @@
|
|||||||
<!-- Ajout des polices Google Fonts -->
|
<!-- Ajout des polices Google Fonts -->
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Dela+Gothic+One&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,522 +1,106 @@
|
|||||||
import { FiUser, FiDatabase, FiShield, FiChevronDown } from 'react-icons/fi'
|
import React, { useState } from 'react';
|
||||||
import { FaDiscord, FaArrowRight, FaEnvelope, FaGithub, FaNetworkWired, FaServer, FaLaptopCode, FaCloudUploadAlt, FaExternalLinkAlt } from 'react-icons/fa'
|
import { Navigation } from './components/layout/Navigation';
|
||||||
import { FiX, FiExternalLink } from 'react-icons/fi'
|
import { Footer } from './components/layout/Footer';
|
||||||
import './App.css'
|
import { HeroSection } from './components/sections/HeroSection';
|
||||||
import icebergImage from './assets/iceberg.png'
|
import { TechFeaturesSection } from './components/sections/TechFeaturesSection';
|
||||||
import logoImage from './assets/banquise_server.svg'
|
import { ServicesSection } from './components/sections/ServicesSection';
|
||||||
import { useEffect, useState, useMemo, useCallback, useRef } from 'react'
|
import { AboutSection } from './components/sections/AboutSection';
|
||||||
|
import { Popup } from './components/ui/Popup';
|
||||||
|
import { ScrollToTopButton } from './components/ui/ScrollToTopButton';
|
||||||
|
import { URLS } from './config/constants';
|
||||||
|
|
||||||
import aboutImage from './assets/banquise.png'
|
// Define Service interface directly in App
|
||||||
|
interface Service {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
image: string;
|
||||||
|
description: string;
|
||||||
|
features: string[];
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
|
||||||
function App() {
|
const App: React.FC = () => {
|
||||||
const [selectedService, setSelectedService] = useState<number | null>(null);
|
// Define services directly in the component with enhanced data
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
const services: Service[] = [
|
||||||
const mobileMenuRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
const services = useMemo(() => [
|
|
||||||
{
|
{
|
||||||
name: "Wiki",
|
name: "Wiki",
|
||||||
url: "https://wiki.la-banquise.fr/",
|
url: URLS.services.wiki,
|
||||||
description: "Une instance de wikijs, ou nous essayons de documenter nos projets, nos services ou encore notre infra, et aussi des petits tutoriels pour bien comprendre les outils utilises a EPITA !"
|
image: "/src/assets/iceberg.png",
|
||||||
|
icon: "📚",
|
||||||
|
description: "Notre wiki collaboratif est votre centre de documentation technique. Accédez à des guides détaillés, des tutoriels et de la documentation API pour tous nos services.",
|
||||||
|
features: [
|
||||||
|
"Documentation collaborative en temps réel",
|
||||||
|
"Guides d'installation détaillés",
|
||||||
|
"API et références techniques",
|
||||||
|
"Tutoriels pas à pas",
|
||||||
|
"Base de connaissances communautaire",
|
||||||
|
"Recherche avancée intégrée"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Git",
|
name: "Gitea",
|
||||||
url: "https://git.la-banquise.fr/",
|
url: URLS.services.gitea,
|
||||||
description: "Gitea est notre plateforme de gestion de code source, similaire à GitHub, hébergée par nos soins. Nos divers projets necessitant Git, comme par exemple ce site, sont heberges et développes grace a cet outil."
|
image: "/src/assets/iceberg.png",
|
||||||
|
icon: "🔧",
|
||||||
|
description: "Instance Gitea auto-hébergée pour la gestion de vos dépôts Git. Collaborez sur vos projets avec un contrôle total sur votre code source.",
|
||||||
|
features: [
|
||||||
|
"Dépôts Git illimités",
|
||||||
|
"Issues et pull requests",
|
||||||
|
"Actions CI/CD intégrées",
|
||||||
|
"Gestion d'équipes et permissions",
|
||||||
|
"Interface web intuitive",
|
||||||
|
"Intégration avec outils externes"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Panel jeux",
|
name: "Panel",
|
||||||
url: "https://panel.la-banquise.fr/auth/login",
|
url: URLS.services.panel,
|
||||||
description: "Interface de connection à notre panel Pterodactyl, qui vous permet de gérer vos serveurs de jeux. Celui ci sera remplace dans l ete par pelican."
|
image: "/src/assets/iceberg.png",
|
||||||
},
|
icon: "🎮",
|
||||||
], []);
|
description: "Interface de gestion centralisée pour vos serveurs de jeux. Déployez, configurez et surveillez vos serveurs gaming en quelques clics.",
|
||||||
|
features: [
|
||||||
const [icebergs, setIcebergs] = useState<Array<{
|
"Déploiement automatisé de serveurs",
|
||||||
id: number,
|
"Monitoring en temps réel",
|
||||||
x: number,
|
"Gestion des ressources système",
|
||||||
y: number,
|
"Interface d'administration web",
|
||||||
scale: number,
|
"Support multi-jeux",
|
||||||
rotation: number,
|
"Sauvegarde automatique des données"
|
||||||
service: typeof services[0],
|
]
|
||||||
floatClass: string
|
|
||||||
}>>([])
|
|
||||||
|
|
||||||
const [reducedMotion, setReducedMotion] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
||||||
setReducedMotion(prefersReducedMotion);
|
|
||||||
|
|
||||||
const startTime = performance.now();
|
|
||||||
let count = 0;
|
|
||||||
while (performance.now() - startTime < 5) {
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
|
];
|
||||||
|
|
||||||
if (count < 1000) {
|
const [selectedService, setSelectedService] = useState<Service | null>(null);
|
||||||
setReducedMotion(true);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const positionIcebergs = useCallback(() => {
|
// Inline accordion logic
|
||||||
const newIcebergs = [];
|
const [openAccordion, setOpenAccordion] = useState<string | null>(null);
|
||||||
|
const toggleAccordion = (title: string) => {
|
||||||
const positions = [
|
setOpenAccordion(openAccordion === title ? null : title);
|
||||||
{ x: 25, y: 35, scale: 0.95, rotation: 0 },
|
|
||||||
{ x: 50, y: 25, scale: 1.1, rotation: 0 },
|
|
||||||
{ x: 75, y: 35, scale: 0.95, rotation: 0 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const floatClasses = ['float-1', 'float-2', 'float-3', 'float-4', 'float-5'];
|
|
||||||
|
|
||||||
for (let i = 0; i < services.length; i++) {
|
|
||||||
const position = positions[i % positions.length];
|
|
||||||
|
|
||||||
newIcebergs.push({
|
|
||||||
id: i,
|
|
||||||
x: position.x,
|
|
||||||
y: position.y,
|
|
||||||
scale: position.scale,
|
|
||||||
rotation: position.rotation,
|
|
||||||
service: services[i],
|
|
||||||
floatClass: reducedMotion ? '' : floatClasses[i % floatClasses.length]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return newIcebergs;
|
|
||||||
}, [services, reducedMotion]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIcebergs(positionIcebergs());
|
|
||||||
}, [positionIcebergs]);
|
|
||||||
|
|
||||||
const renderBubbles = useMemo(() => {
|
|
||||||
if (reducedMotion) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bubbles">
|
|
||||||
<div className="bubble"></div>
|
|
||||||
<div className="bubble"></div>
|
|
||||||
<div className="bubble"></div>
|
|
||||||
<div className="bubble"></div>
|
|
||||||
<div className="bubble"></div>
|
|
||||||
<div className="bubble"></div>
|
|
||||||
<div className="bubble"></div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}, [reducedMotion]);
|
|
||||||
|
|
||||||
const handleIcebergClick = (event: React.MouseEvent, serviceId: number) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setSelectedService(serviceId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClosePopup = () => {
|
|
||||||
setSelectedService(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleOutsideClick = (event: MouseEvent) => {
|
|
||||||
if (mobileMenuRef.current && !mobileMenuRef.current.contains(event.target as Node)) {
|
|
||||||
setMobileMenuOpen(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (mobileMenuOpen) {
|
|
||||||
document.addEventListener('mousedown', handleOutsideClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('mousedown', handleOutsideClick);
|
|
||||||
};
|
|
||||||
}, [mobileMenuOpen]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (mobileMenuOpen) {
|
|
||||||
document.body.style.overflow = 'hidden';
|
|
||||||
} else {
|
|
||||||
document.body.style.overflow = 'auto';
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.body.style.overflow = 'auto';
|
|
||||||
};
|
|
||||||
}, [mobileMenuOpen]);
|
|
||||||
|
|
||||||
const [activeAccordion, setActiveAccordion] = useState<number | null>(null);
|
|
||||||
|
|
||||||
// FAQ items for the about section
|
|
||||||
const faqItems = useMemo(() => [
|
|
||||||
{
|
|
||||||
question: "Qui sommes-nous ?",
|
|
||||||
answer: (
|
|
||||||
<>
|
|
||||||
<p>La Banquise est une association étudiante de l'EPITA, dont l'objectif est de former les epiteens a diverses notions de reseau et d'hebergement de servcies.
|
|
||||||
Fondée a la rentree 2022, notre asso permet à ceux qui le souhaitent de se former sur des technologies d'hébergement et de réseau, sur nos serveurs, a dispositon des etudiants.</p>
|
|
||||||
<p>Notre équipe est composée d'étudiants passionnés par l'informatique, le réseau et le partage de connaissances. Nous mettons notre expertise au service des étudiants et des associations de l'EPITA.</p>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Comment candidater pour rejoindre La Banquise ?",
|
|
||||||
answer: (
|
|
||||||
<>
|
|
||||||
<p>Pour nous rejoindre, rien de plus simple :</p>
|
|
||||||
<br/>
|
|
||||||
<ul>
|
|
||||||
<li>Rejoignez notre serveur Discord</li>
|
|
||||||
<li>Donnez votre login EPITA dans un ticket, ou expliquer votre situation/besoins.</li>
|
|
||||||
<li>Un moderateur vous mettra le role necessaire pour acceder au salons avec tout nos projets sur discord !</li>
|
|
||||||
</ul>
|
|
||||||
<br/>
|
|
||||||
<p>Si vous etes motivé.e, peu importe votre niveau technique actuel, n'hesitez pas a venir nous poser des question ou venir demander des ressources pour un projet !</p>
|
|
||||||
<a href="https://discord.com/invite/QQWwzX5ptY" className="accordion-cta" target="_blank" rel="noopener noreferrer">
|
|
||||||
Rejoindre notre Discord <FaExternalLinkAlt className="accordion-cta-icon" />
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Quels sont nos objectifs ?",
|
|
||||||
answer: (
|
|
||||||
<>
|
|
||||||
<p>Nos principaux objectifs sont :</p>
|
|
||||||
<ul>
|
|
||||||
<li>Former les étudiants aux technologies d'hébergement et de réseau</li>
|
|
||||||
<li>Fournir des services informatiques de qualité aux étudiants et associations de l'EPITA</li>
|
|
||||||
<li>Promouvoir le partage de connaissances et l'entraide</li>
|
|
||||||
<li>Créer un environnement d'apprentissage pratique par l'expérience</li>
|
|
||||||
<li>Maintenir une infrastructure robuste et sécurisée</li>
|
|
||||||
</ul>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Comment utiliser nos services ?",
|
|
||||||
answer: (
|
|
||||||
<>
|
|
||||||
<p>Pour accéder à nos services :</p>
|
|
||||||
<ol>
|
|
||||||
<li>Connectez-vous au service souhaité avec vos identifiants, pour le moment crees par un admin</li>
|
|
||||||
<li>Consultez notre Wiki pour obtenir de l'aide sur l'utilisation de chaque service !</li>
|
|
||||||
</ol>
|
|
||||||
<p>Si vous rencontrez des difficultés, n'hésitez pas à demander de l'aide sur notre Discord.</p>
|
|
||||||
<a href="https://auth.la-banquise.fr/" className="accordion-cta" target="_blank" rel="noopener noreferrer">
|
|
||||||
Se Connecter <FaExternalLinkAlt className="accordion-cta-icon" />
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Comment contribuer a un projet ?",
|
|
||||||
answer: (
|
|
||||||
<>
|
|
||||||
<p>Il existe plusieurs façons de contribuer aux projets La Banquise :</p>
|
|
||||||
<ul>
|
|
||||||
<li>Deployer des services</li>
|
|
||||||
<li>Experimenter et documenter le fonctionnement de technologies (k8s, openstack...)</li>
|
|
||||||
<li>Aider a gerer les ressources de l'asso</li>
|
|
||||||
<li>Contribuer au code source de nos projets via Gitea</li>
|
|
||||||
<li>Rédiger ou améliorer la documentation sur notre Wiki</li>
|
|
||||||
<li>Proposer de nouvelles idées de services ou d'améliorations</li>
|
|
||||||
<li>Aider d'autres utilisateurs sur notre Discord</li>
|
|
||||||
</ul>
|
|
||||||
<p>Toutes les contributions sont les bienvenues, même les plus modestes !</p>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
], []);
|
|
||||||
|
|
||||||
const toggleAccordion = (index: number) => {
|
|
||||||
setActiveAccordion(activeAccordion === index ? null : index);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app-container">
|
<div className="flex flex-col min-h-screen w-full">
|
||||||
<a href="#main-content" className="sr-only focus:not-sr-only">Passer au contenu principal</a>
|
<Navigation />
|
||||||
|
|
||||||
<header>
|
<main className="flex-1 flex flex-col overflow-x-hidden overflow-y-auto">
|
||||||
<nav className="navbar" aria-label="Navigation principale">
|
<div className="relative flex-1 bg-ocean-gradient w-full min-h-screen flex flex-col justify-start items-center overflow-x-hidden">
|
||||||
<div className="navbar-left">
|
|
||||||
<img src={logoImage} alt="Logo La Banquise" className="site-logo" />
|
|
||||||
<h1 className="site-name">La Banquise</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
<HeroSection />
|
||||||
className={`navbar-mobile-toggle ${mobileMenuOpen ? 'active' : ''}`}
|
<TechFeaturesSection />
|
||||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
<ServicesSection services={services} onServiceClick={setSelectedService} />
|
||||||
aria-expanded={mobileMenuOpen}
|
<AboutSection openAccordion={openAccordion} toggleAccordion={toggleAccordion} />
|
||||||
aria-label="Menu de navigation"
|
|
||||||
>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className={`navbar-right ${mobileMenuOpen ? 'mobile-active' : ''}`}
|
|
||||||
ref={mobileMenuRef}
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
href="https://discord.com/invite/QQWwzX5ptY"
|
|
||||||
className="discord-button"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
aria-label="Rejoindre notre Discord"
|
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
|
||||||
>
|
|
||||||
<FaDiscord className="discord-icon" aria-hidden="true" />
|
|
||||||
<span>Discord</span>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://auth.la-banquise.fr/"
|
|
||||||
className="login-button"
|
|
||||||
aria-label="Se connecter à votre compte"
|
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
|
||||||
>
|
|
||||||
<FiUser className="login-icon" aria-hidden="true" />
|
|
||||||
<span>Se connecter</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className={`mobile-menu-overlay ${mobileMenuOpen ? 'active' : ''}`}
|
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
|
||||||
></div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main id="main-content" className="content">
|
|
||||||
<div className="ocean" role="region" aria-label="Services La Banquise">
|
|
||||||
{renderBubbles}
|
|
||||||
|
|
||||||
<section className="page-section hero-section">
|
|
||||||
<div className="hero-tech-elements">
|
|
||||||
<div className="tech-element tech-element-1"><FaServer /></div>
|
|
||||||
<div className="tech-element tech-element-2"><FaLaptopCode /></div>
|
|
||||||
<div className="tech-element tech-element-3"><FaNetworkWired /></div>
|
|
||||||
<div className="tech-element tech-element-4"><FaCloudUploadAlt /></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="hero-logo-container">
|
|
||||||
<img src={logoImage} alt="Logo La Banquise" className="hero-logo" />
|
|
||||||
</div>
|
|
||||||
<h2 className="hero-title">Association La Banquise</h2>
|
|
||||||
<p className="hero-subtitle">
|
|
||||||
Association d'hébergement et lab réseau pour tous les étudiants et associations de l'EPITA !
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
<a href="https://discord.com/invite/QQWwzX5ptY" className="cta-button" target="_blank" rel="noopener noreferrer">
|
|
||||||
Notre Discord
|
|
||||||
<FaArrowRight className="cta-icon" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="page-section tech-features-section">
|
|
||||||
<div className="section-divider"></div>
|
|
||||||
<h2 className="section-title">Notre infrastructure</h2>
|
|
||||||
<p className="section-subtitle">
|
|
||||||
25+ serveurs pour répondre à vos besoins
|
|
||||||
</p>
|
|
||||||
<p className="section-subtitle">
|
|
||||||
Un local a EPITA Lyon, prochainement a Paris ?
|
|
||||||
</p>
|
|
||||||
<div className="tech-features-grid">
|
|
||||||
<div className="tech-feature-card">
|
|
||||||
<div className="tech-feature-icon">
|
|
||||||
<FaServer />
|
|
||||||
</div>
|
|
||||||
<h3 className="tech-feature-title">Serveurs performants</h3>
|
|
||||||
<p className="tech-feature-description">
|
|
||||||
Infrastructure optimisée pour assurer des performances élevées et une disponibilité maximale de vos applications
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="tech-feature-card">
|
|
||||||
<div className="tech-feature-icon">
|
|
||||||
<FiDatabase />
|
|
||||||
</div>
|
|
||||||
<h3 className="tech-feature-title">Stockage sécurisé</h3>
|
|
||||||
<p className="tech-feature-description">
|
|
||||||
Solutions de stockage distribuées avec redondance pour garantir l'intégrité et la durabilité de vos données
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="tech-feature-card">
|
|
||||||
<div className="tech-feature-icon">
|
|
||||||
<FaNetworkWired />
|
|
||||||
</div>
|
|
||||||
<h3 className="tech-feature-title">Réseau optimisé</h3>
|
|
||||||
<p className="tech-feature-description">
|
|
||||||
Architecture réseau à haute disponibilité avec une faible latence pour vos applications critiques
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="tech-feature-card">
|
|
||||||
<div className="tech-feature-icon">
|
|
||||||
<FiShield />
|
|
||||||
</div>
|
|
||||||
<h3 className="tech-feature-title">Sécurité renforcée</h3>
|
|
||||||
<p className="tech-feature-description">
|
|
||||||
Protection contre les menaces avec systèmes de sécurité modernes et mises à jour régulières
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="page-section services-section" id="services">
|
|
||||||
<div className="section-divider"></div>
|
|
||||||
<h2 className="section-title">Nos services</h2>
|
|
||||||
<p className="section-subtitle">
|
|
||||||
Explorez notre écosystème de services conçus pour répondre à vos besoins.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="icebergs-container"
|
|
||||||
role="list"
|
|
||||||
aria-label="Liste des services disponibles"
|
|
||||||
>
|
|
||||||
{icebergs.map((iceberg) => (
|
|
||||||
<a
|
|
||||||
key={iceberg.id}
|
|
||||||
href={iceberg.service.url}
|
|
||||||
className={`iceberg ${iceberg.floatClass}`}
|
|
||||||
style={{
|
|
||||||
transform: `rotate(${iceberg.rotation}deg) scale(${iceberg.scale}) translateZ(0)`,
|
|
||||||
}}
|
|
||||||
role="listitem"
|
|
||||||
aria-label={`En savoir plus sur ${iceberg.service.name}`}
|
|
||||||
onClick={(e) => handleIcebergClick(e, iceberg.id)}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={icebergImage}
|
|
||||||
alt=""
|
|
||||||
className="iceberg-image"
|
|
||||||
loading="lazy"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<div className="service-name">{iceberg.service.name}</div>
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="page-section about-section" id="about">
|
|
||||||
<div className="section-divider"></div>
|
|
||||||
<h2 className="section-title">À propos de nous</h2>
|
|
||||||
<p className="section-subtitle">
|
|
||||||
Découvrez notre mission et posez-nous vos questions
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="about-container">
|
|
||||||
<div className="about-image-container">
|
|
||||||
<img src={aboutImage} alt="Logo La Banquise" className="about-logo" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="about-intro">
|
|
||||||
La Banquise est une association étudiante dédiée à l'hébergement de services et à la formation sur les technologies réseau, au service de la communauté EPITA.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="accordion-group" role="region" aria-label="Foire aux questions">
|
|
||||||
{faqItems.map((item, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className={`accordion-item ${activeAccordion === index ? 'active' : ''}`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="accordion-header"
|
|
||||||
onClick={() => toggleAccordion(index)}
|
|
||||||
role="button"
|
|
||||||
aria-expanded={activeAccordion === index}
|
|
||||||
aria-controls={`accordion-content-${index}`}
|
|
||||||
tabIndex={0}
|
|
||||||
onKeyPress={(e) => {
|
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
|
||||||
toggleAccordion(index);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.question}
|
|
||||||
<FiChevronDown className="accordion-toggle-icon" aria-hidden="true" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="accordion-content"
|
|
||||||
id={`accordion-content-${index}`}
|
|
||||||
role="region"
|
|
||||||
>
|
|
||||||
{item.answer}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{selectedService !== null && (
|
|
||||||
<div className="popup-overlay" onClick={handleClosePopup} role="dialog" aria-modal="true" aria-labelledby="popup-title">
|
|
||||||
<div className="popup-content" onClick={(e) => e.stopPropagation()}>
|
|
||||||
<button className="popup-close" onClick={handleClosePopup} aria-label="Fermer">
|
|
||||||
<FiX />
|
|
||||||
</button>
|
|
||||||
<h3 id="popup-title" className="popup-title">{services[selectedService].name}</h3>
|
|
||||||
<p className="popup-description">{services[selectedService].description}</p>
|
|
||||||
<a
|
|
||||||
href={services[selectedService].url}
|
|
||||||
className="popup-button"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<FiExternalLink className="popup-button-icon" />
|
|
||||||
<span>Accéder au service</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="waves" aria-hidden="true">
|
|
||||||
<div className="wave wave1"></div>
|
|
||||||
<div className="wave wave2"></div>
|
|
||||||
<div className="wave wave3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer className="footer">
|
<Footer />
|
||||||
<div className="footer-content">
|
|
||||||
<div className="footer-column">
|
{/* Bouton de retour en haut */}
|
||||||
<h4>La Banquise</h4>
|
<ScrollToTopButton />
|
||||||
<ul>
|
|
||||||
<li><a href="#about">À propos</a></li>
|
{selectedService && (
|
||||||
<li><a href="#services">Services</a></li>
|
<Popup service={selectedService} onClose={() => setSelectedService(null)} />
|
||||||
<li><a href="https://wiki.la-banquise.fr/en/home">Documentation</a></li>
|
)}
|
||||||
<li><a href="#contact">Contact</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="footer-column">
|
|
||||||
<h4>Services</h4>
|
|
||||||
<ul>
|
|
||||||
{services.map((service, index) => (
|
|
||||||
<li key={index}><a href={service.url}>{service.name}</a></li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="footer-column">
|
|
||||||
<h4>Communauté</h4>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://discord.com/invite/QQWwzX5ptY" target="_blank" rel="noopener noreferrer">Discord</a></li>
|
|
||||||
<li><a href="https://git.la-banquise.fr/" target="_blank" rel="noopener noreferrer">Gitea</a></li>
|
|
||||||
<li><a href="mailto:contact@la-banquise.fr"><FaEnvelope style={{marginRight: '0.5rem'}} /> contact@la-banquise.fr</a></li>
|
|
||||||
<li><a href="https://github.com/la-banquise" target="_blank" rel="noopener noreferrer"><FaGithub style={{marginRight: '0.5rem'}} /> GitHub</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="footer-bottom">
|
|
||||||
<p>© {new Date().getFullYear()} La Banquise. Tous droits réservés.</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
66
banquise-website/src/components/layout/Footer.tsx
Normal file
66
banquise-website/src/components/layout/Footer.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { URLS, SITE_CONFIG } from '../../config/constants';
|
||||||
|
|
||||||
|
export const Footer: React.FC = () => (
|
||||||
|
<footer className="bg-banquise-blue-dark text-white py-12 sm:py-16 md:py-20 px-4 sm:px-6 md:px-8 relative z-10 border-t border-banquise-blue-lightest/20 w-full box-border">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between max-w-6xl mx-auto gap-6 sm:gap-8">
|
||||||
|
<div className="flex-1 min-w-0 mb-6 sm:mb-8 md:mb-0 text-left">
|
||||||
|
<h4 className="text-lg sm:text-xl mb-6 sm:mb-8 text-banquise-blue-lightest relative pb-3 sm:pb-4 after:content-[''] after:absolute after:bottom-0 after:left-0 after:w-12 after:h-0.5 after:bg-gradient-to-r after:from-banquise-blue-lightest after:to-banquise-blue">
|
||||||
|
Services
|
||||||
|
</h4>
|
||||||
|
<ul className="list-none p-0 m-0 space-y-2 sm:space-y-3">
|
||||||
|
<li>
|
||||||
|
<a href={URLS.services.wiki} className="text-banquise-gray/80 no-underline transition-all duration-200 inline-flex items-center hover:text-banquise-gray hover:translate-x-1 text-sm sm:text-base">
|
||||||
|
Wiki
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href={URLS.services.gitea} className="text-banquise-gray/80 no-underline transition-all duration-200 inline-flex items-center hover:text-banquise-gray hover:translate-x-1 text-sm sm:text-base">
|
||||||
|
Gitea
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href={URLS.services.panel} className="text-banquise-gray/80 no-underline transition-all duration-200 inline-flex items-center hover:text-banquise-gray hover:translate-x-1 text-sm sm:text-base">
|
||||||
|
Panel
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 min-w-0 mb-6 sm:mb-8 md:mb-0 text-left">
|
||||||
|
<h4 className="text-lg sm:text-xl mb-6 sm:mb-8 text-banquise-blue-lightest relative pb-3 sm:pb-4 after:content-[''] after:absolute after:bottom-0 after:left-0 after:w-12 after:h-0.5 after:bg-gradient-to-r after:from-banquise-blue-lightest after:to-banquise-blue">
|
||||||
|
Communauté
|
||||||
|
</h4>
|
||||||
|
<ul className="list-none p-0 m-0 space-y-2 sm:space-y-3">
|
||||||
|
<li>
|
||||||
|
<a href={URLS.social.discord} className="text-banquise-gray/80 no-underline transition-all duration-200 inline-flex items-center hover:text-banquise-gray hover:translate-x-1 text-sm sm:text-base">
|
||||||
|
Discord
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 min-w-0 mb-6 sm:mb-8 md:mb-0 text-left">
|
||||||
|
<h4 className="text-lg sm:text-xl mb-6 sm:mb-8 text-banquise-blue-lightest relative pb-3 sm:pb-4 after:content-[''] after:absolute after:bottom-0 after:left-0 after:w-12 after:h-0.5 after:bg-gradient-to-r after:from-banquise-blue-lightest after:to-banquise-blue">
|
||||||
|
Support
|
||||||
|
</h4>
|
||||||
|
<ul className="list-none p-0 m-0 space-y-2 sm:space-y-3">
|
||||||
|
<li>
|
||||||
|
<a href="#" className="text-banquise-gray/80 no-underline transition-all duration-200 inline-flex items-center hover:text-banquise-gray hover:translate-x-1 text-sm sm:text-base">
|
||||||
|
Documentation
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href={URLS.contact.email} className="text-banquise-gray/80 no-underline transition-all duration-200 inline-flex items-center hover:text-banquise-gray hover:translate-x-1 text-sm sm:text-base">
|
||||||
|
Contact
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t border-banquise-blue-lightest/20 pt-6 sm:pt-8 mt-8 sm:mt-12 text-center text-xs sm:text-sm text-banquise-gray/70 max-w-6xl mx-auto">
|
||||||
|
© 2024 {SITE_CONFIG.name}. Tous droits réservés.
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
154
banquise-website/src/components/layout/MobileMenu.tsx
Normal file
154
banquise-website/src/components/layout/MobileMenu.tsx
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import banquiseServer from '../../assets/banquise_server.svg'
|
||||||
|
import { URLS, SITE_CONFIG } from '../../config/constants';
|
||||||
|
import { commonStyles } from '../../styles/components';
|
||||||
|
|
||||||
|
interface MobileMenuProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose }) => {
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = 'unset';
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = 'unset';
|
||||||
|
};
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`md:hidden fixed inset-0 z-[100] transition-all duration-300 ${isOpen ? 'visible' : 'invisible'}`}>
|
||||||
|
{/* Overlay */}
|
||||||
|
<div
|
||||||
|
className={`absolute inset-0 bg-gradient-to-br from-black/70 via-banquise-blue-dark/50 to-black/70 backdrop-blur-md transition-opacity duration-300 ${isOpen ? 'opacity-100' : 'opacity-0'}`}
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Menu mobile */}
|
||||||
|
<div className={`absolute top-0 right-0 h-full w-72 max-w-[85vw] bg-gradient-to-b from-banquise-blue-dark via-banquise-blue-dark/98 to-banquise-blue-dark/95 backdrop-blur-2xl shadow-2xl transition-transform duration-300 border-l border-banquise-blue-lightest/20 ${isOpen ? 'translate-x-0' : 'translate-x-full'}`}>
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between p-4 sm:p-6 pt-6 sm:pt-8 border-b border-banquise-blue-lightest/20 bg-gradient-to-r from-banquise-blue-dark/50 to-transparent">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute inset-0 bg-banquise-blue-light/20 rounded-full blur-md"></div>
|
||||||
|
<img
|
||||||
|
src={banquiseServer}
|
||||||
|
alt="Logo"
|
||||||
|
className="h-8 sm:h-10 w-auto relative z-10"
|
||||||
|
style={{ filter: 'drop-shadow(0 0 8px rgba(168, 218, 255, 0.6))' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className={`text-base sm:text-lg font-bold text-white ${commonStyles.text.heading}`}>
|
||||||
|
{SITE_CONFIG.name}
|
||||||
|
</span>
|
||||||
|
<p className="text-banquise-blue-lightest/70 text-xs">Menu Navigation</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="group relative p-3 bg-white/10 hover:bg-white/20 rounded-xl transition-all duration-200 hover:scale-105 active:scale-95"
|
||||||
|
onClick={onClose}
|
||||||
|
aria-label="Fermer"
|
||||||
|
>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" className="text-white">
|
||||||
|
<path d="M15 5L5 15M5 5L15 15" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Navigation */}
|
||||||
|
<div className="flex flex-col justify-start px-6 py-8 space-y-6 overflow-y-auto" style={{ height: 'calc(100vh - 120px)' }}>
|
||||||
|
|
||||||
|
{/* Navigation Links */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<a href="#services" className={commonStyles.nav.mobileItem} onClick={onClose}>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className={`${commonStyles.icons.small} ${commonStyles.gradients.primaryBr} group-hover:scale-110 transition-transform duration-200`}>
|
||||||
|
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold text-lg">Nos Services</span>
|
||||||
|
<p className="text-white/60 text-sm">Découvrir notre offre</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svg className="w-5 h-5 ml-auto opacity-50 group-hover:opacity-100 group-hover:translate-x-1 transition-all duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="#about" className={commonStyles.nav.mobileItem} onClick={onClose}>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className={`${commonStyles.icons.small} ${commonStyles.gradients.primaryBr} group-hover:scale-110 transition-transform duration-200`}>
|
||||||
|
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold text-lg">À propos</span>
|
||||||
|
<p className="text-white/60 text-sm">En savoir plus sur nous</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svg className="w-5 h-5 ml-auto opacity-50 group-hover:opacity-100 group-hover:translate-x-1 transition-all duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href={URLS.social.discord} className={commonStyles.nav.mobileItem} onClick={onClose}>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className={`${commonStyles.icons.small} ${commonStyles.gradients.discord} group-hover:scale-110 transition-transform duration-200`}>
|
||||||
|
<svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.211.375-.445.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold text-lg">Discord</span>
|
||||||
|
<p className="text-white/60 text-sm">Rejoindre la communauté</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svg className="w-5 h-5 ml-auto opacity-50 group-hover:opacity-100 group-hover:translate-x-1 transition-all duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* CTA Button */}
|
||||||
|
<div className="pt-6 border-t border-banquise-blue-lightest/20">
|
||||||
|
<a
|
||||||
|
href={URLS.services.auth}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={`w-full ${commonStyles.buttons.primary} ${commonStyles.gradients.primary} py-4 px-6 text-lg shadow-xl border border-banquise-blue-lightest/20`}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<svg className="w-6 h-6 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
|
</svg>
|
||||||
|
Se connecter
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<div className="absolute bottom-6 left-6 right-6">
|
||||||
|
<div className="text-center py-4 border-t border-banquise-blue-lightest/20">
|
||||||
|
<p className="text-white/50 text-sm">
|
||||||
|
© 2024 {SITE_CONFIG.name}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Effet glassmorphism */}
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-banquise-blue-dark/20 pointer-events-none"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
147
banquise-website/src/components/layout/Navigation.tsx
Normal file
147
banquise-website/src/components/layout/Navigation.tsx
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { MobileMenu } from './MobileMenu';
|
||||||
|
import banquiseServer from '/src/assets/banquise_server.svg'
|
||||||
|
import { URLS, SITE_CONFIG } from '../../config/constants';
|
||||||
|
import { commonStyles } from '../../styles/components';
|
||||||
|
|
||||||
|
export const Navigation: React.FC = () => {
|
||||||
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
|
const [scrolled, setScrolled] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
const isScrolled = window.scrollY > 20;
|
||||||
|
setScrolled(isScrolled);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleResize = () => {
|
||||||
|
if (window.innerWidth >= 768) {
|
||||||
|
setMobileMenuOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
return () => window.removeEventListener('resize', handleResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<nav className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
|
||||||
|
scrolled
|
||||||
|
? 'bg-banquise-blue-dark/98 backdrop-blur-xl shadow-2xl border-b border-banquise-blue-lightest/30'
|
||||||
|
: 'bg-banquise-blue-dark/95 backdrop-blur-lg shadow-xl border-b border-banquise-blue-lightest/20'
|
||||||
|
}`}>
|
||||||
|
<div className={commonStyles.layout.container}>
|
||||||
|
<div className="flex justify-between items-center h-16 sm:h-18 lg:h-20 px-4 sm:px-6 lg:px-8">
|
||||||
|
|
||||||
|
{/* Logo section */}
|
||||||
|
<div className="flex items-center space-x-3 sm:space-x-4 group">
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-banquise-blue-light/20 to-banquise-blue/20 rounded-full blur-lg opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
|
<img
|
||||||
|
src={banquiseServer}
|
||||||
|
alt="Logo La Banquise"
|
||||||
|
className="h-10 sm:h-12 lg:h-14 w-auto relative z-10 transition-transform duration-300 group-hover:scale-110"
|
||||||
|
style={{ filter: 'drop-shadow(0 0 12px rgba(168, 218, 255, 0.4))' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="hidden sm:block">
|
||||||
|
<h1 className={`text-xl sm:text-2xl lg:text-3xl font-bold text-white tracking-wide ${commonStyles.text.heading}`}>
|
||||||
|
{SITE_CONFIG.name}
|
||||||
|
</h1>
|
||||||
|
<p className="text-banquise-blue-lightest/80 text-xs lg:text-sm font-medium">
|
||||||
|
{SITE_CONFIG.tagline}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Navigation links desktop */}
|
||||||
|
<div className="hidden md:flex items-center space-x-1 lg:space-x-2">
|
||||||
|
<a href="#services" className={commonStyles.nav.link}>
|
||||||
|
<span className="relative z-10">Services</span>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-banquise-blue-light/20 to-banquise-blue/20 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="#about" className={commonStyles.nav.link}>
|
||||||
|
<span className="relative z-10">À propos</span>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-banquise-blue-light/20 to-banquise-blue/20 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Action buttons desktop */}
|
||||||
|
<div className="hidden md:flex items-center space-x-3 lg:space-x-4">
|
||||||
|
<a
|
||||||
|
href={URLS.social.discord}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={`${commonStyles.buttons.discord} ${commonStyles.gradients.discord}`}
|
||||||
|
>
|
||||||
|
<div className={`absolute inset-0 ${commonStyles.gradients.discordHover} opacity-0 group-hover:opacity-100 transition-opacity duration-300`}></div>
|
||||||
|
<div className="relative z-10 flex items-center space-x-2">
|
||||||
|
<svg className="w-4 h-4 lg:w-5 lg:h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.211.375-.445.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z"/>
|
||||||
|
</svg>
|
||||||
|
<span>Discord</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href={URLS.services.auth}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={`${commonStyles.buttons.auth} ${
|
||||||
|
scrolled
|
||||||
|
? `${commonStyles.gradients.primary} border border-banquise-blue-lightest/30 hover:shadow-banquise-blue/25`
|
||||||
|
: 'bg-gradient-to-r from-banquise-blue-light to-banquise-blue border-2 border-white/20 hover:shadow-banquise-blue-light/25'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className={`absolute inset-0 transition-opacity duration-300 opacity-0 group-hover:opacity-100 ${
|
||||||
|
scrolled
|
||||||
|
? 'bg-gradient-to-r from-banquise-blue-light to-banquise-blue'
|
||||||
|
: 'bg-gradient-to-r from-white/10 to-banquise-blue-lightest/20'
|
||||||
|
}`}></div>
|
||||||
|
<div className="relative z-10 flex items-center space-x-2">
|
||||||
|
<svg className="w-4 h-4 lg:w-5 lg:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
|
</svg>
|
||||||
|
<span className="hidden lg:inline">Connexion</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile menu button */}
|
||||||
|
<button
|
||||||
|
className="md:hidden relative p-3 rounded-xl bg-white/10 hover:bg-white/20 transition-all duration-300 hover:scale-105 active:scale-95 group"
|
||||||
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||||
|
aria-label={mobileMenuOpen ? "Fermer le menu" : "Ouvrir le menu"}
|
||||||
|
aria-expanded={mobileMenuOpen}
|
||||||
|
>
|
||||||
|
<div className="w-6 h-6 relative">
|
||||||
|
<span className={`absolute block w-6 h-0.5 bg-white transition-all duration-300 ${mobileMenuOpen ? 'rotate-45 top-3' : 'top-1'}`}></span>
|
||||||
|
<span className={`absolute block w-6 h-0.5 bg-white transition-all duration-300 top-3 ${mobileMenuOpen ? 'opacity-0 scale-0' : 'opacity-100'}`}></span>
|
||||||
|
<span className={`absolute block w-6 h-0.5 bg-white transition-all duration-300 ${mobileMenuOpen ? '-rotate-45 top-3' : 'top-5'}`}></span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Glassmorphism effect bar */}
|
||||||
|
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-banquise-blue-lightest/30 to-transparent"></div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Spacer pour compenser la navbar fixed */}
|
||||||
|
<div className="h-16 sm:h-18 lg:h-20"></div>
|
||||||
|
|
||||||
|
{/* Menu mobile */}
|
||||||
|
<MobileMenu
|
||||||
|
isOpen={mobileMenuOpen}
|
||||||
|
onClose={() => setMobileMenuOpen(false)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
124
banquise-website/src/components/sections/AboutSection.tsx
Normal file
124
banquise-website/src/components/sections/AboutSection.tsx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { AccordionItem } from '../ui/AccordionItem';
|
||||||
|
import { URLS } from '../../config/constants';
|
||||||
|
import { commonStyles } from '../../styles/components';
|
||||||
|
|
||||||
|
interface AboutSectionProps {
|
||||||
|
openAccordion: string | null;
|
||||||
|
toggleAccordion: (title: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AboutSection: React.FC<AboutSectionProps> = ({ openAccordion, toggleAccordion }) => (
|
||||||
|
<section id="about" className="relative bg-gradient-to-b from-banquise-blue-dark/15 to-banquise-blue-dark/20 backdrop-blur-lg py-16 sm:py-20 md:py-24 px-4 sm:px-6 md:px-8 z-2 border-t border-banquise-blue-lightest/20 w-full box-border">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12 sm:mb-16 md:mb-20">
|
||||||
|
<div className={commonStyles.layout.divider}></div>
|
||||||
|
<h2 className={`${commonStyles.text.headingXl} mb-6 sm:mb-8 px-2`} style={{ textShadow: '0 2px 4px rgba(0, 0, 0, 0.2)' }}>
|
||||||
|
À Propos de La Banquise
|
||||||
|
</h2>
|
||||||
|
<p className={`${commonStyles.text.muted} text-lg sm:text-xl max-w-3xl mx-auto px-2`} style={{ textShadow: '0 1px 2px rgba(0, 0, 0, 0.1)' }}>
|
||||||
|
Une communauté passionnée qui propose des services d'hébergement et des outils collaboratifs pour les développeurs et les gamers.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* FAQ Section */}
|
||||||
|
<div className="space-y-4 sm:space-y-6">
|
||||||
|
<h3 className={`${commonStyles.text.headingLg} mb-8 sm:mb-12 flex items-center justify-center px-2`}>
|
||||||
|
<span className="text-2xl sm:text-3xl mr-3">❓</span>
|
||||||
|
<span className="text-center">Questions Fréquentes</span>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<AccordionItem
|
||||||
|
title="🎯 Notre Mission"
|
||||||
|
isOpen={openAccordion === "mission"}
|
||||||
|
onToggle={() => toggleAccordion("mission")}
|
||||||
|
>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<p className={commonStyles.text.muted}>
|
||||||
|
Former les étudiants au déploiment et a la gestion d'une infra, et de maitriser des technologies entreprise grade.
|
||||||
|
Cela permet de fournir une plateforme stable et accessible pour héberger vos projets, partager vos connaissances et jouer ensemble !
|
||||||
|
</p>
|
||||||
|
<p className={commonStyles.text.muted}>
|
||||||
|
Nous croyons en la puissance de la collaboration et mettons à disposition des outils modernes pour faciliter le travail en équipe.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-2 mt-4">
|
||||||
|
<span className="bg-banquise-blue/20 text-banquise-blue-light px-3 py-1 rounded-full text-sm font-medium">Collaboration</span>
|
||||||
|
<span className="bg-banquise-blue/20 text-banquise-blue-light px-3 py-1 rounded-full text-sm font-medium">Innovation</span>
|
||||||
|
<span className="bg-banquise-blue/20 text-banquise-blue-light px-3 py-1 rounded-full text-sm font-medium">Accessibilité</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionItem>
|
||||||
|
|
||||||
|
<AccordionItem
|
||||||
|
title="🛠️ Nos Services"
|
||||||
|
isOpen={openAccordion === "services"}
|
||||||
|
onToggle={() => toggleAccordion("services")}
|
||||||
|
>
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<div className={`flex items-start space-x-4 p-4 ${commonStyles.gradients.card} rounded-xl ${commonStyles.cards.base}`}>
|
||||||
|
<div className={`${commonStyles.icons.small} ${commonStyles.gradients.primaryBr} font-bold`}>📚</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-banquise-gray mb-1">Wiki</h4>
|
||||||
|
<p className="text-banquise-gray/80 text-sm">Documentation collaborative et guides détaillés</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`flex items-start space-x-4 p-4 ${commonStyles.gradients.card} rounded-xl ${commonStyles.cards.base}`}>
|
||||||
|
<div className={`${commonStyles.icons.small} ${commonStyles.gradients.primaryBr} font-bold`}>🔧</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-banquise-gray mb-1">Gitea</h4>
|
||||||
|
<p className="text-banquise-gray/80 text-sm">Gestion de versions Git auto-hébergée</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`flex items-start space-x-4 p-4 ${commonStyles.gradients.card} rounded-xl ${commonStyles.cards.base}`}>
|
||||||
|
<div className={`${commonStyles.icons.small} ${commonStyles.gradients.primaryBr} font-bold`}>🎮</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-banquise-gray mb-1">Panel de Jeux</h4>
|
||||||
|
<p className="text-banquise-gray/80 text-sm">Interface de gestion pour serveurs de jeux</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className={`${commonStyles.text.muted} mt-4`}>
|
||||||
|
Tous nos services sont maintenus avec soin et régulièrement mis à jour pour garantir une expérience optimale.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</AccordionItem>
|
||||||
|
|
||||||
|
<AccordionItem
|
||||||
|
title="🤝 Rejoindre l'association"
|
||||||
|
isOpen={openAccordion === "community"}
|
||||||
|
onToggle={() => toggleAccordion("community")}
|
||||||
|
>
|
||||||
|
<div className="space-y-6">
|
||||||
|
<p className={commonStyles.text.muted}>
|
||||||
|
Rejoignez notre serveur Discord pour rejoindre l'asso, échanger avec nous, obtenir de l'aide et rester informé des dernières nouveautés !
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className={`${commonStyles.cards.base} bg-gradient-to-r from-banquise-blue-dark/20 to-banquise-blue/10 rounded-2xl p-6`}>
|
||||||
|
<h4 className="font-semibold text-banquise-gray mb-3 flex items-center">
|
||||||
|
<span className="text-xl mr-2">💬</span>
|
||||||
|
Comment rejoindre l'asso ?
|
||||||
|
</h4>
|
||||||
|
<ul className="space-y-2 text-banquise-gray/80 text-sm mb-6">
|
||||||
|
<li className="flex items-center"><span className="text-banquise-blue-light mr-2">•</span> Creez un ticket banquise</li>
|
||||||
|
<li className="flex items-center"><span className="text-banquise-blue-light mr-2">•</span> Donnez votre login EPITA ou expliquez votre situation</li>
|
||||||
|
<li className="flex items-center"><span className="text-banquise-blue-light mr-2">•</span> Un moderateur validera votre demande et vous donnera acces aux salons discord de l'asso !</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href={URLS.social.discord}
|
||||||
|
className={`${commonStyles.buttons.primary} ${commonStyles.gradients.primary} py-3 px-6 rounded-xl`}
|
||||||
|
>
|
||||||
|
<span className="mr-3 text-lg">🚀</span>
|
||||||
|
Rejoindre Discord
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
30
banquise-website/src/components/sections/HeroSection.tsx
Normal file
30
banquise-website/src/components/sections/HeroSection.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import banquiseServer from '/src/assets/banquise_server.svg'
|
||||||
|
|
||||||
|
export const HeroSection: React.FC = () => (
|
||||||
|
<section className="min-h-[calc(80vh-72px)] flex flex-col justify-center items-center text-center py-12 sm:py-16 md:py-20 w-full max-w-6xl mx-auto px-4 sm:px-6 md:px-8 relative z-3">
|
||||||
|
<div className="mb-8 sm:mb-10 md:mb-12 w-32 h-32 sm:w-40 sm:h-40 md:w-48 md:h-48 rounded-full bg-gradient-to-br from-banquise-blue-dark/20 to-banquise-blue/10 p-4 sm:p-5 md:p-6 shadow-2xl backdrop-blur-sm border border-banquise-blue-lightest/30 relative group">
|
||||||
|
<img
|
||||||
|
src={banquiseServer}
|
||||||
|
alt="Logo La Banquise"
|
||||||
|
className="w-full h-full object-contain relative z-10 transition-transform duration-300 group-hover:scale-110"
|
||||||
|
style={{
|
||||||
|
filter: 'drop-shadow(0 10px 25px rgba(31, 93, 137, 0.3))'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 className="text-banquise-gray text-3xl sm:text-4xl md:text-5xl lg:text-6xl mb-6 sm:mb-7 md:mb-8 font-extrabold leading-tight max-w-4xl font-heading px-2 relative z-10" style={{ textShadow: '0 2px 10px rgba(0, 0, 0, 0.3)' }}>
|
||||||
|
Bienvenue sur La Banquise
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p className="text-banquise-gray text-lg sm:text-xl md:text-2xl mb-8 sm:mb-10 md:mb-12 max-w-3xl font-normal opacity-90 leading-relaxed px-2 relative z-10" style={{ textShadow: '0 1px 4px rgba(0, 0, 0, 0.2)' }}>
|
||||||
|
Association d'hébergement et lab réseau pour tous les étudiants et associations de l'EPITA !
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a href="#services" className="inline-flex items-center justify-center bg-gradient-to-r from-banquise-gray to-white text-banquise-blue-dark border-0 py-4 sm:py-5 px-8 sm:px-10 md:px-12 rounded-2xl text-base sm:text-lg font-bold no-underline shadow-xl transition-all duration-300 min-w-48 sm:min-w-56 md:min-w-64 hover:-translate-y-2 hover:shadow-2xl hover:scale-105 backdrop-blur-sm border border-banquise-blue-lightest/20 mx-4 group relative z-10">
|
||||||
|
<span className="text-center text-banquise-blue-dark">Découvrir nos services</span>
|
||||||
|
<span className="ml-2 sm:ml-3 text-lg sm:text-xl transition-transform duration-300 group-hover:translate-x-1 text-banquise-blue-dark">→</span>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
);
|
63
banquise-website/src/components/sections/ServicesSection.tsx
Normal file
63
banquise-website/src/components/sections/ServicesSection.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
// Declare the Service interface here
|
||||||
|
interface Service {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
image: string;
|
||||||
|
icon: string;
|
||||||
|
description: string;
|
||||||
|
features: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define interface directly in the component file
|
||||||
|
interface ServicesSectionProps {
|
||||||
|
services: Service[];
|
||||||
|
onServiceClick: (service: Service) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ServicesSection: React.FC<ServicesSectionProps> = ({ services, onServiceClick }) => (
|
||||||
|
<section id="services" className="relative z-2 py-12 sm:py-16 md:py-20 w-full max-w-6xl mx-auto px-4 sm:px-6 md:px-8">
|
||||||
|
<div className="w-20 h-1 bg-gradient-to-r from-banquise-blue-lightest to-banquise-blue mx-auto mb-6 sm:mb-8 rounded-full"></div>
|
||||||
|
<h2 className="text-banquise-gray text-2xl sm:text-3xl md:text-4xl mb-4 sm:mb-6 text-center font-heading font-bold tracking-tight px-2" style={{ textShadow: '0 2px 4px rgba(0, 0, 0, 0.2)' }}>
|
||||||
|
Nos Services
|
||||||
|
</h2>
|
||||||
|
<p className="text-banquise-gray text-lg sm:text-xl opacity-90 mb-12 sm:mb-14 md:mb-16 max-w-4xl text-center mx-auto leading-relaxed px-2" style={{ textShadow: '0 1px 3px rgba(0, 0, 0, 0.2)' }}>
|
||||||
|
Cliquez sur un service pour découvrir toutes ses fonctionnalités
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 sm:gap-8 w-full">
|
||||||
|
{services.map((service) => (
|
||||||
|
<div
|
||||||
|
key={service.name}
|
||||||
|
className="group relative bg-gradient-to-br from-banquise-blue-dark/10 to-banquise-blue-dark/5 backdrop-blur-lg rounded-2xl p-6 sm:p-8 border border-banquise-blue-lightest/30 transition-all duration-300 cursor-pointer hover:-translate-y-4 hover:shadow-2xl hover:border-banquise-blue-lightest/50 hover:from-banquise-blue-dark/15 hover:to-banquise-blue-dark/8 active:scale-95"
|
||||||
|
onClick={() => onServiceClick(service)}
|
||||||
|
>
|
||||||
|
{/* Icon */}
|
||||||
|
<div className="mb-6 sm:mb-8 w-20 h-20 sm:w-24 sm:h-24 bg-gradient-to-br from-banquise-blue to-banquise-blue-light rounded-2xl flex items-center justify-center text-3xl sm:text-4xl shadow-lg group-hover:scale-110 transition-transform duration-300 mx-auto">
|
||||||
|
{service.icon}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Service name */}
|
||||||
|
<h3 className="text-xl sm:text-2xl font-bold text-banquise-gray mb-4 sm:mb-6 font-heading text-center group-hover:text-banquise-blue-lightest transition-colors duration-300">
|
||||||
|
{service.name}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{/* Short teaser description */}
|
||||||
|
<p className="text-banquise-gray/80 leading-relaxed mb-6 sm:mb-8 text-center text-sm sm:text-base">
|
||||||
|
{service.description.split('.')[0]}.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<div className="flex items-center justify-center text-banquise-blue-light font-bold group-hover:text-banquise-blue-lightest transition-colors duration-300 text-sm sm:text-base">
|
||||||
|
<span className="text-center">Découvrir toutes les fonctionnalités</span>
|
||||||
|
<span className="ml-2 text-lg transition-transform duration-300 group-hover:translate-x-2">→</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Subtle hover effect */}
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-br from-banquise-blue-lightest/10 to-banquise-blue/5 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const TechFeaturesSection: React.FC = () => (
|
||||||
|
<section className="py-12 sm:py-16 md:py-20 relative z-2 w-full max-w-6xl mx-auto px-4 sm:px-6 md:px-8">
|
||||||
|
<div className="w-20 h-1 bg-gradient-to-r from-banquise-blue-lightest to-banquise-blue mx-auto mb-6 sm:mb-8 rounded-full"></div>
|
||||||
|
<h2 className="text-banquise-gray text-2xl sm:text-3xl md:text-4xl mb-4 sm:mb-6 text-center font-heading font-bold tracking-tight px-2" style={{ textShadow: '0 2px 4px rgba(0, 0, 0, 0.2)' }}>
|
||||||
|
Notre Infrastructure
|
||||||
|
</h2>
|
||||||
|
<p className="text-banquise-gray text-lg sm:text-xl opacity-90 mb-12 sm:mb-14 md:mb-16 max-w-4xl text-center mx-auto leading-relaxed px-2" style={{ textShadow: '0 1px 3px rgba(0, 0, 0, 0.2)' }}>
|
||||||
|
25+ serveurs pour répondre à vos besoins
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4 sm:gap-6 w-full">
|
||||||
|
<div className="bg-gradient-to-br from-banquise-blue-dark/10 to-banquise-blue-dark/5 backdrop-blur-lg rounded-2xl p-6 sm:p-8 flex flex-col items-center text-center transition-all duration-300 border border-banquise-blue-lightest/30 hover:-translate-y-3 hover:from-banquise-blue-dark/15 hover:to-banquise-blue-dark/8 hover:shadow-xl hover:border-banquise-blue-lightest/50 group">
|
||||||
|
<div className="text-3xl sm:text-4xl mb-4 sm:mb-6 text-white bg-gradient-to-br from-banquise-blue to-banquise-blue-light w-16 h-16 sm:w-20 sm:h-20 flex items-center justify-center rounded-2xl shadow-lg group-hover:scale-110 transition-transform duration-300">
|
||||||
|
🚀
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg sm:text-xl mb-3 sm:mb-4 text-banquise-gray font-heading font-semibold group-hover:text-banquise-blue-lightest transition-colors duration-300">Serveurs performants</h3>
|
||||||
|
<p className="text-banquise-gray/80 leading-relaxed text-sm">Infrastructure optimisée pour assurer des performances élevées et une disponibilité maximale de vos applications</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-gradient-to-br from-banquise-blue-dark/10 to-banquise-blue-dark/5 backdrop-blur-lg rounded-2xl p-6 sm:p-8 flex flex-col items-center text-center transition-all duration-300 border border-banquise-blue-lightest/30 hover:-translate-y-3 hover:from-banquise-blue-dark/15 hover:to-banquise-blue-dark/8 hover:shadow-xl hover:border-banquise-blue-lightest/50 group">
|
||||||
|
<div className="text-3xl sm:text-4xl mb-4 sm:mb-6 text-white bg-gradient-to-br from-banquise-blue to-banquise-blue-light w-16 h-16 sm:w-20 sm:h-20 flex items-center justify-center rounded-2xl shadow-lg group-hover:scale-110 transition-transform duration-300">
|
||||||
|
💾
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg sm:text-xl mb-3 sm:mb-4 text-banquise-gray font-heading font-semibold group-hover:text-banquise-blue-lightest transition-colors duration-300">Stockage sécurisé</h3>
|
||||||
|
<p className="text-banquise-gray/80 leading-relaxed text-sm">Solutions de stockage distribuées avec redondance pour garantir l'intégrité et la durabilité de vos données</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-gradient-to-br from-banquise-blue-dark/10 to-banquise-blue-dark/5 backdrop-blur-lg rounded-2xl p-6 sm:p-8 flex flex-col items-center text-center transition-all duration-300 border border-banquise-blue-lightest/30 hover:-translate-y-3 hover:from-banquise-blue-dark/15 hover:to-banquise-blue-dark/8 hover:shadow-xl hover:border-banquise-blue-lightest/50 group">
|
||||||
|
<div className="text-3xl sm:text-4xl mb-4 sm:mb-6 text-white bg-gradient-to-br from-banquise-blue to-banquise-blue-light w-16 h-16 sm:w-20 sm:h-20 flex items-center justify-center rounded-2xl shadow-lg group-hover:scale-110 transition-transform duration-300">
|
||||||
|
🌐
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg sm:text-xl mb-3 sm:mb-4 text-banquise-gray font-heading font-semibold group-hover:text-banquise-blue-lightest transition-colors duration-300">Réseau optimisé</h3>
|
||||||
|
<p className="text-banquise-gray/80 leading-relaxed text-sm">Architecture réseau à haute disponibilité avec une faible latence pour vos applications critiques</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-gradient-to-br from-banquise-blue-dark/10 to-banquise-blue-dark/5 backdrop-blur-lg rounded-2xl p-6 sm:p-8 flex flex-col items-center text-center transition-all duration-300 border border-banquise-blue-lightest/30 hover:-translate-y-3 hover:from-banquise-blue-dark/15 hover:to-banquise-blue-dark/8 hover:shadow-xl hover:border-banquise-blue-lightest/50 group">
|
||||||
|
<div className="text-3xl sm:text-4xl mb-4 sm:mb-6 text-white bg-gradient-to-br from-banquise-blue to-banquise-blue-light w-16 h-16 sm:w-20 sm:h-20 flex items-center justify-center rounded-2xl shadow-lg group-hover:scale-110 transition-transform duration-300">
|
||||||
|
🛡️
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg sm:text-xl mb-3 sm:mb-4 text-banquise-gray font-heading font-semibold group-hover:text-banquise-blue-lightest transition-colors duration-300">Sécurité renforcée</h3>
|
||||||
|
<p className="text-banquise-gray/80 leading-relaxed text-sm">Protection contre les menaces avec systèmes de sécurité modernes et mises à jour régulières</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
28
banquise-website/src/components/ui/AccordionItem.tsx
Normal file
28
banquise-website/src/components/ui/AccordionItem.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
// Définir l'interface localement :
|
||||||
|
interface AccordionItemProps {
|
||||||
|
title: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
isOpen: boolean;
|
||||||
|
onToggle: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AccordionItem: React.FC<AccordionItemProps> = ({ title, children, isOpen, onToggle }) => (
|
||||||
|
<div className={`bg-gradient-to-br from-banquise-blue-dark/15 to-banquise-blue-dark/5 backdrop-blur-lg rounded-2xl overflow-hidden border border-banquise-blue-lightest/30 transition-all duration-300 shadow-sm ${isOpen ? 'shadow-xl border-banquise-blue-lightest/50 scale-[1.01]' : ''} hover:shadow-lg hover:border-banquise-blue-lightest/40`}>
|
||||||
|
<div
|
||||||
|
className="p-4 sm:p-6 md:p-8 cursor-pointer flex items-center justify-between font-semibold text-banquise-gray transition-all duration-200 text-base sm:text-lg select-none hover:bg-banquise-blue-dark/10 active:bg-banquise-blue-dark/15"
|
||||||
|
onClick={onToggle}
|
||||||
|
>
|
||||||
|
<span className="flex items-center flex-1 mr-4 font-heading">{title}</span>
|
||||||
|
<span className={`text-xl sm:text-2xl transition-transform duration-300 text-banquise-blue-lightest flex-shrink-0 ${isOpen ? 'rotate-180' : ''}`}>
|
||||||
|
▼
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={`transition-all duration-500 overflow-hidden ${isOpen ? 'max-h-[1000px] pb-4 px-4 sm:pb-6 sm:px-6 md:pb-8 md:px-8' : 'max-h-0'}`}>
|
||||||
|
<div className="text-banquise-gray/90 leading-relaxed text-sm sm:text-base">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
146
banquise-website/src/components/ui/ParallaxBackground.tsx
Normal file
146
banquise-website/src/components/ui/ParallaxBackground.tsx
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export const ParallaxBackground: React.FC = () => {
|
||||||
|
const [scrollY, setScrollY] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
setScrollY(window.scrollY);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Éléments flottants avec différentes vitesses de parallaxe
|
||||||
|
const floatingElements = [
|
||||||
|
// Serveurs et équipements
|
||||||
|
{ icon: '🖥️', x: 10, y: 20, speed: 0.3, size: 'text-2xl', opacity: 0.1 },
|
||||||
|
{ icon: '🖲️', x: 85, y: 15, speed: 0.2, size: 'text-xl', opacity: 0.08 },
|
||||||
|
{ icon: '⚙️', x: 75, y: 45, speed: 0.4, size: 'text-3xl', opacity: 0.12 },
|
||||||
|
{ icon: '🔧', x: 15, y: 60, speed: 0.25, size: 'text-lg', opacity: 0.06 },
|
||||||
|
{ icon: '💾', x: 90, y: 70, speed: 0.35, size: 'text-2xl', opacity: 0.1 },
|
||||||
|
|
||||||
|
// Code et développement
|
||||||
|
{ icon: '<>', x: 30, y: 35, speed: 0.15, size: 'text-xl', opacity: 0.08, isText: true },
|
||||||
|
{ icon: '{ }', x: 60, y: 25, speed: 0.28, size: 'text-2xl', opacity: 0.1, isText: true },
|
||||||
|
{ icon: '#!/bin', x: 5, y: 80, speed: 0.2, size: 'text-sm', opacity: 0.06, isText: true },
|
||||||
|
{ icon: 'git', x: 80, y: 85, speed: 0.32, size: 'text-lg', opacity: 0.08, isText: true },
|
||||||
|
|
||||||
|
// Réseau et connectivité
|
||||||
|
{ icon: '🌐', x: 45, y: 10, speed: 0.22, size: 'text-2xl', opacity: 0.09 },
|
||||||
|
{ icon: '🔗', x: 25, y: 75, speed: 0.18, size: 'text-xl', opacity: 0.07 },
|
||||||
|
{ icon: '📡', x: 70, y: 55, speed: 0.26, size: 'text-lg', opacity: 0.08 },
|
||||||
|
|
||||||
|
// Sécurité
|
||||||
|
{ icon: '🔒', x: 55, y: 40, speed: 0.3, size: 'text-xl', opacity: 0.09 },
|
||||||
|
{ icon: '🛡️', x: 35, y: 65, speed: 0.24, size: 'text-2xl', opacity: 0.1 },
|
||||||
|
{ icon: '🔑', x: 85, y: 30, speed: 0.16, size: 'text-lg', opacity: 0.07 },
|
||||||
|
|
||||||
|
// Données et stockage
|
||||||
|
{ icon: '💿', x: 20, y: 45, speed: 0.28, size: 'text-xl', opacity: 0.08 },
|
||||||
|
{ icon: '📊', x: 65, y: 75, speed: 0.22, size: 'text-2xl', opacity: 0.09 },
|
||||||
|
{ icon: '📈', x: 40, y: 20, speed: 0.34, size: 'text-lg', opacity: 0.07 },
|
||||||
|
|
||||||
|
// Éléments techniques supplémentaires
|
||||||
|
{ icon: 'sudo', x: 12, y: 90, speed: 0.19, size: 'text-sm', opacity: 0.06, isText: true },
|
||||||
|
{ icon: 'SSH', x: 78, y: 12, speed: 0.31, size: 'text-base', opacity: 0.08, isText: true },
|
||||||
|
{ icon: 'API', x: 92, y: 50, speed: 0.27, size: 'text-lg', opacity: 0.09, isText: true },
|
||||||
|
{ icon: 'TCP', x: 8, y: 30, speed: 0.23, size: 'text-base', opacity: 0.07, isText: true },
|
||||||
|
{ icon: 'HTTP', x: 50, y: 80, speed: 0.29, size: 'text-sm', opacity: 0.06, isText: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 pointer-events-none overflow-hidden z-0">
|
||||||
|
{/* Grille de fond subtile */}
|
||||||
|
<div className="absolute inset-0 opacity-[0.02]">
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 bg-gradient-to-br from-banquise-blue-dark/20 to-transparent"
|
||||||
|
style={{
|
||||||
|
backgroundImage: `
|
||||||
|
linear-gradient(rgba(31, 93, 137, 0.03) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(31, 93, 137, 0.03) 1px, transparent 1px)
|
||||||
|
`,
|
||||||
|
backgroundSize: '50px 50px',
|
||||||
|
transform: `translateY(${scrollY * 0.1}px)`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Particules de code flottantes */}
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
{floatingElements.map((element, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`absolute ${element.size} font-mono select-none transition-all duration-1000 ease-out`}
|
||||||
|
style={{
|
||||||
|
left: `${element.x}%`,
|
||||||
|
top: `${element.y}%`,
|
||||||
|
transform: `translateY(${scrollY * element.speed}px) rotate(${scrollY * 0.01}deg)`,
|
||||||
|
opacity: element.opacity,
|
||||||
|
color: element.isText ? '#a8daff' : 'inherit',
|
||||||
|
textShadow: element.isText ? '0 0 10px rgba(168, 218, 255, 0.3)' : 'none',
|
||||||
|
filter: 'blur(0.5px)',
|
||||||
|
animation: `float-${index % 3} ${6 + (index % 4)}s ease-in-out infinite`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{element.icon}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Lignes de connexion animées */}
|
||||||
|
<svg
|
||||||
|
className="absolute inset-0 w-full h-full opacity-[0.03]"
|
||||||
|
style={{
|
||||||
|
transform: `translateY(${scrollY * 0.2}px)`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="lineGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" stopColor="#a8daff" stopOpacity="0" />
|
||||||
|
<stop offset="50%" stopColor="#a8daff" stopOpacity="0.5" />
|
||||||
|
<stop offset="100%" stopColor="#a8daff" stopOpacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
{/* Lignes de connexion entre les éléments */}
|
||||||
|
{[...Array(8)].map((_, i) => (
|
||||||
|
<line
|
||||||
|
key={i}
|
||||||
|
x1={`${10 + i * 12}%`}
|
||||||
|
y1={`${20 + i * 8}%`}
|
||||||
|
x2={`${30 + i * 15}%`}
|
||||||
|
y2={`${40 + i * 12}%`}
|
||||||
|
stroke="url(#lineGradient)"
|
||||||
|
strokeWidth="1"
|
||||||
|
className="animate-pulse"
|
||||||
|
style={{
|
||||||
|
animationDelay: `${i * 0.5}s`,
|
||||||
|
animationDuration: `${3 + i}s`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{/* Cercles de données en mouvement */}
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
{[...Array(6)].map((_, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="absolute rounded-full border border-banquise-blue-lightest/5 animate-ping"
|
||||||
|
style={{
|
||||||
|
left: `${15 + i * 15}%`,
|
||||||
|
top: `${25 + i * 12}%`,
|
||||||
|
width: `${40 + i * 20}px`,
|
||||||
|
height: `${40 + i * 20}px`,
|
||||||
|
transform: `translateY(${scrollY * (0.1 + i * 0.05)}px)`,
|
||||||
|
animationDelay: `${i * 1.2}s`,
|
||||||
|
animationDuration: `${4 + i}s`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
141
banquise-website/src/components/ui/Popup.tsx
Normal file
141
banquise-website/src/components/ui/Popup.tsx
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { URLS } from '../../config/constants';
|
||||||
|
|
||||||
|
interface Service {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
image: string;
|
||||||
|
icon: string;
|
||||||
|
description: string;
|
||||||
|
features: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PopupProps {
|
||||||
|
service: Service;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Popup: React.FC<PopupProps> = ({ service, onClose }) => {
|
||||||
|
// Empêcher le scroll du body quand la popup est ouverte
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = 'unset';
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black/60 flex justify-center items-center z-50 p-4 backdrop-blur-md animate-fadeIn">
|
||||||
|
<div className="bg-white text-banquise-blue-dark rounded-3xl max-w-4xl w-full max-h-[90vh] shadow-2xl relative animate-slideUp border border-banquise-blue-lightest/20 overflow-hidden">
|
||||||
|
|
||||||
|
{/* Bouton de fermeture fixe au-dessus du contenu */}
|
||||||
|
<div className="absolute top-4 right-4 z-50">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="bg-white/90 hover:bg-white border border-banquise-blue/20 text-xl cursor-pointer text-banquise-blue-dark flex items-center justify-center w-10 h-10 sm:w-12 sm:h-12 rounded-full transition-all duration-200 hover:scale-110 active:scale-95 shadow-lg backdrop-blur-sm"
|
||||||
|
aria-label="Fermer la popup"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contenu avec scroll vertical uniquement */}
|
||||||
|
<div className="overflow-y-auto overflow-x-hidden max-h-[90vh] popup-content">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="relative bg-gradient-to-r from-banquise-blue to-banquise-blue-light p-6 sm:p-8 text-white pr-16 sm:pr-20">
|
||||||
|
<div className="flex flex-col lg:flex-row items-center lg:items-start mb-4">
|
||||||
|
<div className="w-16 h-16 sm:w-20 sm:h-20 lg:w-24 lg:h-24 bg-white/20 rounded-3xl flex items-center justify-center text-3xl sm:text-4xl lg:text-5xl mb-4 lg:mb-0 lg:mr-8 backdrop-blur-sm">
|
||||||
|
{service.icon}
|
||||||
|
</div>
|
||||||
|
<div className="text-center lg:text-left flex-1">
|
||||||
|
<h2 className="font-heading text-2xl sm:text-3xl lg:text-4xl mt-0 mb-3 lg:mb-4 leading-tight font-bold text-white">
|
||||||
|
{service.name}
|
||||||
|
</h2>
|
||||||
|
<div className="text-white/90 text-base sm:text-lg lg:text-xl font-medium">
|
||||||
|
Service d'hébergement professionnel
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 lg:mt-6 flex flex-wrap gap-2 justify-center lg:justify-start">
|
||||||
|
<span className="bg-white/20 text-white px-3 py-1 rounded-full text-sm font-medium backdrop-blur-sm">Haute disponibilité</span>
|
||||||
|
<span className="bg-white/20 text-white px-3 py-1 rounded-full text-sm font-medium backdrop-blur-sm">Open Source</span>
|
||||||
|
<span className="bg-white/20 text-white px-3 py-1 rounded-full text-sm font-medium backdrop-blur-sm">Communautaire</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content - Forcer le fond blanc */}
|
||||||
|
<div className="p-6 sm:p-8 bg-white">
|
||||||
|
{/* Description */}
|
||||||
|
<h3 className="text-xl sm:text-2xl lg:text-3xl mb-4 lg:mb-6 text-banquise-blue-dark font-heading font-bold flex items-center">
|
||||||
|
<span className="text-xl sm:text-2xl lg:text-3xl mr-3">📋</span>
|
||||||
|
Description détaillée
|
||||||
|
</h3>
|
||||||
|
<div className="bg-gradient-to-br from-banquise-blue/5 to-banquise-blue-light/5 rounded-2xl p-4 lg:p-6 border border-banquise-blue/10 mb-8">
|
||||||
|
<p className="text-banquise-blue-dark/90 leading-relaxed text-base sm:text-lg lg:text-xl mb-4">
|
||||||
|
{service.description}
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-6">
|
||||||
|
<div className="flex items-center p-3 bg-white/60 rounded-xl border border-banquise-blue/10">
|
||||||
|
<div className="w-10 h-10 bg-gradient-to-br from-banquise-blue to-banquise-blue-light rounded-lg flex items-center justify-center text-white mr-3">
|
||||||
|
✓
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="font-semibold text-banquise-blue-dark text-sm">99.9% Uptime</div>
|
||||||
|
<div className="text-banquise-blue-dark/70 text-xs">Disponibilité garantie</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center p-3 bg-white/60 rounded-xl border border-banquise-blue/10">
|
||||||
|
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center text-white mr-3">
|
||||||
|
🔒
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="font-semibold text-banquise-blue-dark text-sm">Sécurisé</div>
|
||||||
|
<div className="text-banquise-blue-dark/70 text-xs">SSL & Backups</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Fonctionnalités */}
|
||||||
|
<h3 className="text-xl sm:text-2xl lg:text-3xl mb-4 lg:mb-6 text-banquise-blue-dark font-heading font-bold flex items-center">
|
||||||
|
<span className="text-xl sm:text-2xl lg:text-3xl mr-3">⚡</span>
|
||||||
|
Fonctionnalités principales
|
||||||
|
</h3>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-8">
|
||||||
|
{service.features.map((feature, index) => (
|
||||||
|
<div key={index} className="flex items-start bg-banquise-blue/5 rounded-xl p-4 border border-banquise-blue/10 hover:bg-banquise-blue/10 transition-colors duration-200 group">
|
||||||
|
<div className="w-6 h-6 bg-gradient-to-br from-banquise-blue to-banquise-blue-light rounded-full flex items-center justify-center mr-3 mt-0.5 flex-shrink-0 group-hover:scale-110 transition-transform duration-200">
|
||||||
|
<div className="w-2 h-2 bg-white rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
<span className="text-banquise-blue-dark/90 font-medium text-sm lg:text-base leading-relaxed">{feature}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Call to action */}
|
||||||
|
<div className="pt-6 lg:pt-8 border-t border-banquise-blue/10">
|
||||||
|
<a
|
||||||
|
href={service.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="w-full inline-flex items-center justify-center bg-gradient-to-r from-banquise-blue to-banquise-blue-light text-white border-0 py-4 px-6 sm:px-8 rounded-2xl cursor-pointer no-underline font-bold tracking-wide shadow-lg transition-all duration-300 hover:shadow-xl hover:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-banquise-blue-light text-base lg:text-lg hover:scale-[1.02] active:scale-95"
|
||||||
|
>
|
||||||
|
<span className="mr-3 text-xl lg:text-2xl">🚀</span>
|
||||||
|
<span>Accéder à {service.name}</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<p className="text-center text-sm text-banquise-blue-dark/60 mt-4">
|
||||||
|
Besoin d'aide ? Rejoignez notre <a href={URLS.social.discord} className="text-banquise-blue hover:text-banquise-blue-dark transition-colors duration-200 font-medium">Discord</a> pour obtenir du support
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Decorative elements */}
|
||||||
|
<div className="absolute top-0 right-0 w-16 h-16 sm:w-24 sm:h-24 lg:w-32 lg:h-32 bg-banquise-blue-lightest/10 rounded-full -translate-y-8 translate-x-8 sm:-translate-y-12 sm:translate-x-12 lg:-translate-y-16 lg:translate-x-16 hidden sm:block pointer-events-none"></div>
|
||||||
|
<div className="absolute bottom-0 left-0 w-12 h-12 sm:w-16 sm:h-16 lg:w-24 lg:h-24 bg-banquise-blue/5 rounded-full translate-y-6 -translate-x-6 sm:translate-y-8 sm:-translate-x-8 lg:translate-y-12 lg:-translate-x-12 hidden sm:block pointer-events-none"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
51
banquise-website/src/components/ui/ScrollToTopButton.tsx
Normal file
51
banquise-website/src/components/ui/ScrollToTopButton.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
export const ScrollToTopButton: React.FC = () => {
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const toggleVisibility = () => {
|
||||||
|
// Afficher le bouton après avoir scrollé 300px
|
||||||
|
setIsVisible(window.scrollY > 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', toggleVisibility);
|
||||||
|
return () => window.removeEventListener('scroll', toggleVisibility);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const scrollToTop = () => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={scrollToTop}
|
||||||
|
className={`fixed bottom-6 right-6 z-50 w-12 h-12 sm:w-14 sm:h-14 bg-gradient-to-r from-banquise-blue to-banquise-blue-light text-white rounded-full shadow-lg hover:shadow-xl transition-all duration-300 flex items-center justify-center group border border-banquise-blue-lightest/30 backdrop-blur-sm ${
|
||||||
|
isVisible
|
||||||
|
? 'opacity-100 translate-y-0 scale-100'
|
||||||
|
: 'opacity-0 translate-y-4 scale-95 pointer-events-none'
|
||||||
|
}`}
|
||||||
|
aria-label="Retour en haut de page"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="w-5 h-5 sm:w-6 sm:h-6 transition-transform duration-300 group-hover:-translate-y-0.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2.5}
|
||||||
|
d="M7 11l5-5m0 0l5 5m-5-5v12"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{/* Effet de lueur au hover */}
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-banquise-blue-light to-banquise-blue rounded-full opacity-0 group-hover:opacity-75 transition-opacity duration-300 blur-sm"></div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
20
banquise-website/src/config/constants.ts
Normal file
20
banquise-website/src/config/constants.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export const URLS = {
|
||||||
|
services: {
|
||||||
|
wiki: "https://wiki.la-banquise.fr",
|
||||||
|
gitea: "https://git.la-banquise.fr",
|
||||||
|
panel: "https://panel.la-banquise.fr",
|
||||||
|
auth: "https://auth.la-banquise.fr"
|
||||||
|
},
|
||||||
|
social: {
|
||||||
|
discord: "https://discord.gg/labanquise"
|
||||||
|
},
|
||||||
|
contact: {
|
||||||
|
email: "mailto:contact@la-banquise.fr"
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const SITE_CONFIG = {
|
||||||
|
name: "La Banquise",
|
||||||
|
description: "Association d'hébergement et lab réseau pour tous les étudiants et associations de l'EPITA",
|
||||||
|
tagline: "Communauté • Hébergement"
|
||||||
|
} as const;
|
@ -2,102 +2,184 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
/* Système typographique moderne */
|
/* Variables CSS pour les polices */
|
||||||
:root {
|
:root {
|
||||||
--font-heading: 'Space Grotesk', sans-serif;
|
--font-heading: 'Dela Gothic One', sans-serif;
|
||||||
--font-body: 'Inter', system-ui, sans-serif;
|
--font-body: 'Roboto', sans-serif;
|
||||||
|
|
||||||
font-family: var(--font-body);
|
|
||||||
line-height: 1.5;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
color-scheme: light dark;
|
|
||||||
color: rgba(255, 255, 255, 0.87);
|
|
||||||
background-color: #242424;
|
|
||||||
|
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hiérarchie typographique */
|
/* Parallax animations */
|
||||||
h1, h2, h3, h4, h5, h6, .site-name, .welcome-title {
|
@keyframes parallax-slow {
|
||||||
font-family: var(--font-heading);
|
0% { transform: translateY(0px); }
|
||||||
font-weight: 600;
|
100% { transform: translateY(-50px); }
|
||||||
line-height: 1.2;
|
|
||||||
letter-spacing: -0.025em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
@keyframes parallax-medium {
|
||||||
font-size: 3.2em;
|
0% { transform: translateY(0px); }
|
||||||
line-height: 1.1;
|
100% { transform: translateY(-80px); }
|
||||||
font-weight: 700;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
@keyframes parallax-fast {
|
||||||
font-size: 2.4em;
|
0% { transform: translateY(0px); }
|
||||||
font-weight: 600;
|
100% { transform: translateY(-120px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
@keyframes parallax-very-slow {
|
||||||
font-size: 1.8em;
|
0% { transform: translateY(0px); }
|
||||||
font-weight: 600;
|
100% { transform: translateY(-20px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
p, span, div, a, button, input {
|
/* Floating animations with different speeds */
|
||||||
font-family: var(--font-body);
|
@keyframes float-slow {
|
||||||
|
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||||||
|
50% { transform: translateY(-20px) rotate(5deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
@keyframes float-medium {
|
||||||
font-weight: 500;
|
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||||||
color: #646cff;
|
50% { transform: translateY(-15px) rotate(-3deg); }
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
@keyframes float-fast {
|
||||||
color: #535bf2;
|
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||||||
|
50% { transform: translateY(-10px) rotate(3deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes float-very-slow {
|
||||||
|
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||||||
|
50% { transform: translateY(-30px) rotate(-2deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations pour les éléments flottants */
|
||||||
|
@keyframes float-0 {
|
||||||
|
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||||||
|
50% { transform: translateY(-10px) rotate(2deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float-1 {
|
||||||
|
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||||||
|
33% { transform: translateY(-8px) rotate(-1deg); }
|
||||||
|
66% { transform: translateY(-15px) rotate(1deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float-2 {
|
||||||
|
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||||||
|
25% { transform: translateY(-12px) rotate(1deg); }
|
||||||
|
75% { transform: translateY(-6px) rotate(-2deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation pour l'élément principal du hero */
|
||||||
|
@keyframes gentle-float {
|
||||||
|
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||||||
|
50% { transform: translateY(-15px) rotate(1deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-gentle-float {
|
||||||
|
animation: gentle-float 6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Effet de lueur pour les éléments techniques */
|
||||||
|
@keyframes glow-pulse {
|
||||||
|
0%, 100% {
|
||||||
|
text-shadow: 0 0 5px rgba(168, 218, 255, 0.3),
|
||||||
|
0 0 10px rgba(168, 218, 255, 0.2),
|
||||||
|
0 0 15px rgba(168, 218, 255, 0.1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
text-shadow: 0 0 10px rgba(168, 218, 255, 0.4),
|
||||||
|
0 0 20px rgba(168, 218, 255, 0.3),
|
||||||
|
0 0 30px rgba(168, 218, 255, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation des lignes de connexion */
|
||||||
|
@keyframes data-flow {
|
||||||
|
0% { stroke-dasharray: 0, 100; }
|
||||||
|
50% { stroke-dasharray: 50, 100; }
|
||||||
|
100% { stroke-dasharray: 100, 100; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive pour les animations */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.animate-gentle-float,
|
||||||
|
.animate-ping,
|
||||||
|
.animate-pulse {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply animations */
|
||||||
|
.animate-parallax-slow {
|
||||||
|
animation: parallax-slow 20s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-parallax-medium {
|
||||||
|
animation: parallax-medium 15s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-parallax-fast {
|
||||||
|
animation: parallax-fast 10s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-parallax-very-slow {
|
||||||
|
animation: parallax-very-slow 30s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-float-slow {
|
||||||
|
animation: float-slow 8s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-float-medium {
|
||||||
|
animation: float-medium 6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-float-fast {
|
||||||
|
animation: float-fast 4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-float-very-slow {
|
||||||
|
animation: float-very-slow 12s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Amélioration du scroll */
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empêcher le scroll horizontal global */
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
overflow-x: hidden;
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
min-width: 320px;
|
|
||||||
min-height: 100vh;
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
/* Styles pour les popups */
|
||||||
border-radius: 8px;
|
.popup-content {
|
||||||
border: 1px solid transparent;
|
scrollbar-width: thin;
|
||||||
padding: 0.6em 1.2em;
|
scrollbar-color: rgba(31, 93, 137, 0.3) transparent;
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: var(--font-body);
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: border-color 0.25s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
.popup-content::-webkit-scrollbar {
|
||||||
border-color: #646cff;
|
width: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:focus,
|
.popup-content::-webkit-scrollbar-track {
|
||||||
button:focus-visible {
|
background: transparent;
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
.popup-content::-webkit-scrollbar-thumb {
|
||||||
:root {
|
background: rgba(31, 93, 137, 0.3);
|
||||||
color: #213547;
|
border-radius: 3px;
|
||||||
background-color: #ffffff;
|
}
|
||||||
}
|
|
||||||
a:hover {
|
.popup-content::-webkit-scrollbar-thumb:hover {
|
||||||
color: #747bff;
|
background: rgba(31, 93, 137, 0.5);
|
||||||
}
|
}
|
||||||
button {
|
|
||||||
background-color: #f9f9f9;
|
/* Animation pour le bouton scroll to top */
|
||||||
}
|
@keyframes bounce-up {
|
||||||
|
0%, 100% { transform: translateY(0px); }
|
||||||
|
50% { transform: translateY(-4px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-to-top:hover {
|
||||||
|
animation: bounce-up 0.6s ease-in-out;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,18 @@ import { createRoot } from 'react-dom/client'
|
|||||||
import './index.css'
|
import './index.css'
|
||||||
import App from './App.tsx'
|
import App from './App.tsx'
|
||||||
|
|
||||||
|
// Ajouter les métadonnées SEO
|
||||||
|
document.title = 'La Banquise - Hébergement et Communauté Tech';
|
||||||
|
const metaDescription = document.createElement('meta');
|
||||||
|
metaDescription.name = 'description';
|
||||||
|
metaDescription.content = 'Association d\'hébergement et lab réseau pour tous les étudiants et associations de l\'EPITA. Services Wiki, Gitea, Panel de jeux.';
|
||||||
|
document.head.appendChild(metaDescription);
|
||||||
|
|
||||||
|
const metaViewport = document.createElement('meta');
|
||||||
|
metaViewport.name = 'viewport';
|
||||||
|
metaViewport.content = 'width=device-width, initial-scale=1.0';
|
||||||
|
document.head.appendChild(metaViewport);
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
63
banquise-website/src/styles/components.ts
Normal file
63
banquise-website/src/styles/components.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
export const commonStyles = {
|
||||||
|
// Gradients
|
||||||
|
gradients: {
|
||||||
|
primary: "bg-gradient-to-r from-banquise-blue to-banquise-blue-light",
|
||||||
|
primaryBr: "bg-gradient-to-br from-banquise-blue to-banquise-blue-light",
|
||||||
|
card: "bg-gradient-to-br from-banquise-blue-dark/10 to-banquise-blue-dark/5",
|
||||||
|
cardHover: "hover:from-banquise-blue-dark/15 hover:to-banquise-blue-dark/8",
|
||||||
|
discord: "bg-gradient-to-r from-indigo-600 to-purple-600",
|
||||||
|
discordHover: "hover:from-indigo-500 hover:to-purple-500"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
buttons: {
|
||||||
|
primary: "inline-flex items-center justify-center font-bold text-white border-0 rounded-2xl transition-all duration-300 hover:shadow-xl hover:-translate-y-1 hover:scale-105 active:scale-95",
|
||||||
|
discord: "group relative overflow-hidden px-4 lg:px-6 py-2.5 lg:py-3 text-white font-semibold text-sm lg:text-base rounded-xl transition-all duration-300 hover:shadow-xl hover:shadow-indigo-500/25 hover:-translate-y-1 hover:scale-105",
|
||||||
|
auth: "group relative overflow-hidden px-4 lg:px-6 py-2.5 lg:py-3 text-white font-semibold text-sm lg:text-base rounded-xl transition-all duration-300 hover:shadow-xl hover:-translate-y-1 hover:scale-105"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Cards
|
||||||
|
cards: {
|
||||||
|
base: "backdrop-blur-lg rounded-2xl border border-banquise-blue-lightest/30 transition-all duration-300",
|
||||||
|
hover: "hover:shadow-xl hover:border-banquise-blue-lightest/50",
|
||||||
|
interactive: "cursor-pointer hover:-translate-y-4 hover:shadow-2xl active:scale-95"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Text - Hiérarchie améliorée
|
||||||
|
text: {
|
||||||
|
heading: "font-heading font-bold tracking-tight",
|
||||||
|
// Titres principaux de section
|
||||||
|
headingXl: "text-3xl sm:text-4xl md:text-5xl text-banquise-gray font-heading font-bold tracking-tight",
|
||||||
|
headingLg: "text-2xl sm:text-3xl md:text-4xl text-banquise-gray font-heading font-bold tracking-tight",
|
||||||
|
headingMd: "text-xl sm:text-2xl md:text-3xl text-banquise-blue-dark font-heading font-bold tracking-tight",
|
||||||
|
headingSm: "text-lg sm:text-xl md:text-2xl text-banquise-blue-dark font-heading font-semibold tracking-tight",
|
||||||
|
// Sous-titres
|
||||||
|
subheading: "text-base sm:text-lg md:text-xl text-banquise-gray/90 font-medium leading-relaxed",
|
||||||
|
// Corps de texte
|
||||||
|
body: "text-sm sm:text-base md:text-lg text-banquise-blue-dark/90 leading-relaxed",
|
||||||
|
description: "text-banquise-gray/80 leading-relaxed",
|
||||||
|
muted: "text-banquise-gray/90 leading-relaxed",
|
||||||
|
// Texte sur fond sombre
|
||||||
|
lightHeading: "text-banquise-blue-lightest font-heading font-bold tracking-tight",
|
||||||
|
lightBody: "text-white/90 leading-relaxed"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
layout: {
|
||||||
|
section: "py-12 sm:py-16 md:py-20 w-full max-w-6xl mx-auto px-4 sm:px-6 md:px-8",
|
||||||
|
container: "max-w-6xl mx-auto",
|
||||||
|
divider: "w-20 h-1 bg-gradient-to-r from-banquise-blue-lightest to-banquise-blue mx-auto mb-6 sm:mb-8 rounded-full"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Icons and decorative elements
|
||||||
|
icons: {
|
||||||
|
base: "w-16 h-16 sm:w-20 sm:h-20 lg:w-24 lg:h-24 rounded-2xl flex items-center justify-center text-3xl sm:text-4xl lg:text-5xl shadow-lg",
|
||||||
|
small: "w-10 h-10 rounded-lg flex items-center justify-center text-white"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
nav: {
|
||||||
|
link: "px-4 lg:px-6 py-2.5 lg:py-3 text-white/90 hover:text-white font-medium text-sm lg:text-base rounded-xl transition-all duration-300 hover:bg-white/10 hover:backdrop-blur-sm relative group",
|
||||||
|
mobileItem: "group flex items-center p-4 text-white/90 hover:text-white no-underline rounded-2xl hover:bg-gradient-to-r hover:from-banquise-blue/20 hover:to-banquise-blue-light/20 transition-all duration-300 border border-transparent hover:border-banquise-blue-lightest/20"
|
||||||
|
}
|
||||||
|
} as const;
|
15
banquise-website/src/types/index.ts
Normal file
15
banquise-website/src/types/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export interface Service {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
image: string;
|
||||||
|
description: string;
|
||||||
|
features: string[];
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AccordionItemProps {
|
||||||
|
title: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
isOpen: boolean;
|
||||||
|
onToggle: () => void;
|
||||||
|
}
|
@ -7,11 +7,109 @@ export default {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
// Vous pouvez personnaliser vos couleurs ici
|
banquise: {
|
||||||
|
blue: '#40B4FF',
|
||||||
|
'blue-dark': '#1F5D89',
|
||||||
|
'blue-light': '#69B7E2',
|
||||||
|
'blue-lightest': '#A5F0FF',
|
||||||
|
gray: '#F6F6F6',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
// Personnalisation des polices
|
heading: ['Dela Gothic One', 'sans-serif'],
|
||||||
}
|
body: ['Roboto', 'sans-serif'],
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
'float': 'float 6s ease-in-out infinite',
|
||||||
|
'float-1': 'float1 5s ease-in-out infinite',
|
||||||
|
'float-2': 'float2 6s ease-in-out infinite',
|
||||||
|
'float-3': 'float3 7s ease-in-out infinite',
|
||||||
|
'wave': 'wave 10s linear infinite',
|
||||||
|
'wave-reverse': 'waveReverse 15s linear infinite',
|
||||||
|
'wave-1': 'wave 20s linear infinite',
|
||||||
|
'wave-2': 'waveReverse 15s linear infinite',
|
||||||
|
'wave-3': 'wave 12s linear infinite',
|
||||||
|
'rise': 'rise 10s infinite ease-in',
|
||||||
|
'tech-float': 'tech-float 10s ease-in-out infinite',
|
||||||
|
'gentle-float': 'gentle-float 6s ease-in-out infinite',
|
||||||
|
'fadeIn': 'fadeIn 0.2s ease-out',
|
||||||
|
'slideUp': 'slideUp 0.3s ease-out',
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
float: {
|
||||||
|
'0%, 100%': { transform: 'translateY(0)' },
|
||||||
|
'50%': { transform: 'translateY(-10px)' },
|
||||||
|
},
|
||||||
|
float1: {
|
||||||
|
'0%, 100%': { transform: 'translateY(0)' },
|
||||||
|
'50%': { transform: 'translateY(-15px)' },
|
||||||
|
},
|
||||||
|
float2: {
|
||||||
|
'0%, 100%': { transform: 'translateY(0)' },
|
||||||
|
'50%': { transform: 'translateY(-20px)' },
|
||||||
|
},
|
||||||
|
float3: {
|
||||||
|
'0%, 100%': { transform: 'translateY(0)' },
|
||||||
|
'50%': { transform: 'translateY(-10px)' },
|
||||||
|
},
|
||||||
|
'tech-float': {
|
||||||
|
'0%, 100%': { transform: 'translateY(0) rotate(0deg)', opacity: '0.15' },
|
||||||
|
'50%': { transform: 'translateY(-20px) rotate(10deg)', opacity: '0.25' },
|
||||||
|
},
|
||||||
|
'gentle-float': {
|
||||||
|
'0%, 100%': { transform: 'translateY(0)' },
|
||||||
|
'50%': { transform: 'translateY(-10px)' },
|
||||||
|
},
|
||||||
|
wave: {
|
||||||
|
'0%': { backgroundPosition: '0' },
|
||||||
|
'100%': { backgroundPosition: '1200px' },
|
||||||
|
},
|
||||||
|
waveReverse: {
|
||||||
|
'0%': { backgroundPosition: '1200px' },
|
||||||
|
'100%': { backgroundPosition: '0' },
|
||||||
|
},
|
||||||
|
rise: {
|
||||||
|
'0%': {
|
||||||
|
bottom: '-100px',
|
||||||
|
transform: 'translateX(0)',
|
||||||
|
opacity: '0.8',
|
||||||
|
},
|
||||||
|
'50%': {
|
||||||
|
transform: 'translateX(40px)',
|
||||||
|
opacity: '0.4',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
bottom: '1080px',
|
||||||
|
transform: 'translateX(-40px)',
|
||||||
|
opacity: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fadeIn: {
|
||||||
|
from: { opacity: '0' },
|
||||||
|
to: { opacity: '1' },
|
||||||
|
},
|
||||||
|
slideUp: {
|
||||||
|
from: { transform: 'translateY(30px)', opacity: '0' },
|
||||||
|
to: { transform: 'translateY(0)', opacity: '1' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
backdropBlur: {
|
||||||
|
'xs': '2px',
|
||||||
|
},
|
||||||
|
backgroundImage: {
|
||||||
|
'wave-pattern': "url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1200 120\" preserveAspectRatio=\"none\"><path d=\"M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113-14.29,1200,52.47V0Z\" opacity=\".25\" fill=\"%231F5D89\"/><path d=\"M0,0V15.81C13,36.92,27.64,56.86,47.69,72.05,99.41,111.27,165,111,224.58,91.58c31.15-10.15,60.09-26.07,89.67-39.8,40.92-19,84.73-46,130.83-49.67,36.26-2.85,70.9,9.42,98.6,31.56,31.77,25.39,62.32,62,103.63,73,40.44,10.79,81.35-6.69,119.13-24.28s75.16-39,116.92-43.05c59.73-5.85,113.28,22.88,168.9,38.84,30.2,8.66,59,6.17,87.09-7.5,22.43-10.89,48-26.93,60.65-49.24V0Z\" opacity=\".5\" fill=\"%231F5D89\"/><path d=\"M0,0V5.63C149.93,59,314.09,71.32,475.83,42.57c43-7.64,84.23-20.12,127.61-26.46,59-8.63,112.48,12.24,165.56,35.4C827.93,77.22,886,95.24,951.2,90c86.53-7,172.46-45.71,248.8-84.81V0Z\" fill=\"%231F5D89\"/></svg>')",
|
||||||
|
'ocean-gradient': 'linear-gradient(180deg, #40B4FF 0%, #69B7E2 50%, #1F5D89 100%)',
|
||||||
|
},
|
||||||
|
maxHeight: {
|
||||||
|
'0': '0',
|
||||||
|
'1000': '1000px',
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
'72': '18rem',
|
||||||
|
'80': '20rem',
|
||||||
|
'88': '22rem',
|
||||||
|
'96': '24rem',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
pkgs.mkShell {
|
pkgs.mkShell {
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
pkgs.nodejs_23
|
pkgs.nodejs_24
|
||||||
pkgs.nodePackages.tailwindcss
|
pkgs.nodePackages.tailwindcss
|
||||||
pkgs.nodePackages.postcss
|
pkgs.nodePackages.postcss
|
||||||
pkgs.nodePackages.autoprefixer
|
pkgs.nodePackages.autoprefixer
|
||||||
|
Loading…
x
Reference in New Issue
Block a user