Merge pull request 'UI FIX and Improvements' (#21) from sv-refactor into dev

Reviewed-on: #21
This commit is contained in:
Sahamone 2025-06-02 21:40:08 +02:00
commit 2d87e1d9b8
18 changed files with 628 additions and 203 deletions

View File

@ -6,6 +6,8 @@ import { TechFeaturesSection } from './components/sections/TechFeaturesSection';
import { ServicesSection } from './components/sections/ServicesSection';
import { AboutSection } from './components/sections/AboutSection';
import { Popup } from './components/ui/Popup';
import { ScrollToTopButton } from './components/ui/ScrollToTopButton';
import { URLS } from './config/constants';
// Define Service interface directly in App
interface Service {
@ -22,7 +24,7 @@ const App: React.FC = () => {
const services: Service[] = [
{
name: "Wiki",
url: "https://wiki.la-banquise.fr",
url: URLS.services.wiki,
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.",
@ -37,7 +39,7 @@ const App: React.FC = () => {
},
{
name: "Gitea",
url: "https://git.la-banquise.fr",
url: URLS.services.gitea,
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.",
@ -52,7 +54,7 @@ const App: React.FC = () => {
},
{
name: "Panel",
url: "https://panel.la-banquise.fr",
url: URLS.services.panel,
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.",
@ -91,6 +93,9 @@ const App: React.FC = () => {
<Footer />
{/* Bouton de retour en haut */}
<ScrollToTopButton />
{selectedService && (
<Popup service={selectedService} onClose={() => setSelectedService(null)} />
)}

View File

@ -1,45 +1,46 @@
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 sm:flex-row sm:flex-wrap justify-between max-w-6xl mx-auto gap-6 sm:gap-8">
<div className="flex-1 min-w-48 sm:min-w-64 mb-6 sm:mb-8 text-left">
<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="https://wiki.labanquise.org" 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">
<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="https://git.labanquise.org" 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">
<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="https://panel.labanquise.org" 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">
<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-48 sm:min-w-64 mb-6 sm:mb-8 text-left">
<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="https://discord.gg/labanquise" 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">
<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-48 sm:min-w-64 mb-6 sm:mb-8 text-left">
<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>
@ -50,7 +51,7 @@ export const Footer: React.FC = () => (
</a>
</li>
<li>
<a href="mailto:contact@la-banquise.fr" 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">
<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>
@ -59,7 +60,7 @@ export const Footer: React.FC = () => (
</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 La Banquise. Tous droits réservés.
© 2024 {SITE_CONFIG.name}. Tous droits réservés.
</div>
</footer>
);

View File

@ -1,4 +1,7 @@
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;
@ -6,7 +9,6 @@ interface MobileMenuProps {
}
export const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose }) => {
// Empêcher le scroll du body quand le menu mobile est ouvert
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
@ -20,31 +22,31 @@ export const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose }) => {
}, [isOpen]);
return (
<div className={`md:hidden fixed inset-0 z-40 transition-all duration-300 ${isOpen ? 'visible' : 'invisible'}`}>
{/* Overlay amélioré */}
<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 moderne */}
<div className={`absolute top-0 right-0 h-full w-80 max-w-[90vw] 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'}`}>
{/* 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 moderne */}
<div className="flex items-center justify-between p-6 border-b border-banquise-blue-lightest/20 bg-gradient-to-r from-banquise-blue-dark/50 to-transparent">
{/* 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="/src/assets/banquise.png"
src={banquiseServer}
alt="Logo"
className="h-10 w-auto relative z-10"
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-lg font-bold text-white font-heading">
La Banquise
<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>
@ -61,18 +63,14 @@ export const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose }) => {
</button>
</div>
{/* Navigation moderne */}
<div className="flex flex-col justify-center px-6 py-8 space-y-6">
{/* 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="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"
onClick={onClose}
>
<a href="#services" className={commonStyles.nav.mobileItem} onClick={onClose}>
<div className="flex items-center space-x-4">
<div className="w-10 h-10 bg-gradient-to-br from-banquise-blue to-banquise-blue-light rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform duration-200">
<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>
@ -87,13 +85,9 @@ export const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose }) => {
</svg>
</a>
<a
href="#about"
className="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"
onClick={onClose}
>
<a href="#about" className={commonStyles.nav.mobileItem} onClick={onClose}>
<div className="flex items-center space-x-4">
<div className="w-10 h-10 bg-gradient-to-br from-banquise-blue to-banquise-blue-light rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform duration-200">
<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>
@ -108,13 +102,9 @@ export const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose }) => {
</svg>
</a>
<a
href="https://discord.gg/labanquise"
className="group flex items-center p-4 text-white/90 hover:text-white no-underline rounded-2xl hover:bg-gradient-to-r hover:from-indigo-600/20 hover:to-purple-600/20 transition-all duration-300 border border-transparent hover:border-indigo-400/20"
onClick={onClose}
>
<a href={URLS.social.discord} className={commonStyles.nav.mobileItem} onClick={onClose}>
<div className="flex items-center space-x-4">
<div className="w-10 h-10 bg-gradient-to-br from-indigo-600 to-purple-600 rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform duration-200">
<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>
@ -133,10 +123,10 @@ export const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose }) => {
{/* CTA Button */}
<div className="pt-6 border-t border-banquise-blue-lightest/20">
<a
href="https://auth.la-banquise.fr"
href={URLS.services.auth}
target="_blank"
rel="noopener noreferrer"
className="w-full flex items-center justify-center bg-gradient-to-r from-banquise-blue to-banquise-blue-light text-white border-0 py-4 px-6 rounded-2xl cursor-pointer no-underline font-bold text-lg shadow-xl transition-all duration-300 hover:shadow-2xl hover:scale-105 active:scale-95 border border-banquise-blue-lightest/20"
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">
@ -147,11 +137,11 @@ export const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose }) => {
</div>
</div>
{/* Footer simplifié */}
{/* 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 La Banquise
© 2024 {SITE_CONFIG.name}
</p>
</div>
</div>

View File

@ -1,12 +1,13 @@
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);
// Gérer l'effet de scroll sur la navbar
useEffect(() => {
const handleScroll = () => {
const isScrolled = window.scrollY > 20;
@ -17,7 +18,6 @@ export const Navigation: React.FC = () => {
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// Fermer le menu mobile quand on redimensionne la fenêtre
useEffect(() => {
const handleResize = () => {
if (window.innerWidth >= 768) {
@ -36,8 +36,8 @@ export const Navigation: React.FC = () => {
? '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="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16 sm:h-18 lg:h-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">
@ -51,29 +51,23 @@ export const Navigation: React.FC = () => {
/>
</div>
<div className="hidden sm:block">
<h1 className="text-xl sm:text-2xl lg:text-3xl font-bold text-white tracking-wide font-heading">
La Banquise
<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">
Communauté Hébergement
{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="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"
>
<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="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"
>
<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>
@ -82,12 +76,12 @@ export const Navigation: React.FC = () => {
{/* Action buttons desktop */}
<div className="hidden md:flex items-center space-x-3 lg:space-x-4">
<a
href="https://discord.gg/labanquise"
href={URLS.social.discord}
target="_blank"
rel="noopener noreferrer"
className="group relative overflow-hidden px-4 lg:px-6 py-2.5 lg:py-3 bg-gradient-to-r from-indigo-600 to-purple-600 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"
className={`${commonStyles.buttons.discord} ${commonStyles.gradients.discord}`}
>
<div className="absolute inset-0 bg-gradient-to-r from-indigo-500 to-purple-500 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<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"/>
@ -97,12 +91,12 @@ export const Navigation: React.FC = () => {
</a>
<a
href="https://auth.la-banquise.fr"
href={URLS.services.auth}
target="_blank"
rel="noopener noreferrer"
className={`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 ${
className={`${commonStyles.buttons.auth} ${
scrolled
? 'bg-gradient-to-r from-banquise-blue to-banquise-blue-light border border-banquise-blue-lightest/30 hover:shadow-banquise-blue/25'
? `${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'
}`}
>

View File

@ -1,5 +1,7 @@
import React from 'react';
import { AccordionItem } from '../ui/AccordionItem';
import { URLS } from '../../config/constants';
import { commonStyles } from '../../styles/components';
interface AboutSectionProps {
openAccordion: string | null;
@ -11,18 +13,18 @@ export const AboutSection: React.FC<AboutSectionProps> = ({ openAccordion, toggl
<div className="max-w-4xl mx-auto">
{/* Header */}
<div className="text-center mb-12 sm:mb-16 md:mb-20">
<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-6 sm:mb-8 font-heading font-bold tracking-tight px-2" style={{ textShadow: '0 2px 4px rgba(0, 0, 0, 0.2)' }}>
<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="text-banquise-gray/90 text-lg sm:text-xl max-w-3xl mx-auto leading-relaxed px-2" style={{ textShadow: '0 1px 2px rgba(0, 0, 0, 0.1)' }}>
<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="text-xl sm:text-2xl font-bold text-banquise-gray mb-8 sm:mb-12 font-heading flex items-center justify-center px-2">
<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>
@ -33,11 +35,11 @@ export const AboutSection: React.FC<AboutSectionProps> = ({ openAccordion, toggl
onToggle={() => toggleAccordion("mission")}
>
<div className="space-y-4">
<p className="text-banquise-gray/90 leading-relaxed">
<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="text-banquise-gray/90 leading-relaxed">
<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">
@ -55,31 +57,31 @@ export const AboutSection: React.FC<AboutSectionProps> = ({ openAccordion, toggl
>
<div className="space-y-6">
<div className="grid gap-4">
<div className="flex items-start space-x-4 p-4 bg-banquise-blue-dark/10 rounded-xl border border-banquise-blue-lightest/20">
<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 font-bold">📚</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">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 bg-banquise-blue-dark/10 rounded-xl border border-banquise-blue-lightest/20">
<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 font-bold">🔧</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 bg-banquise-blue-dark/10 rounded-xl border border-banquise-blue-lightest/20">
<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 font-bold">🎮</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="text-banquise-gray/90 leading-relaxed mt-4">
<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>
@ -91,11 +93,11 @@ export const AboutSection: React.FC<AboutSectionProps> = ({ openAccordion, toggl
onToggle={() => toggleAccordion("community")}
>
<div className="space-y-6">
<p className="text-banquise-gray/90 leading-relaxed">
<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="bg-gradient-to-r from-banquise-blue-dark/20 to-banquise-blue/10 rounded-2xl p-6 border border-banquise-blue-lightest/30">
<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 ?
@ -107,8 +109,8 @@ export const AboutSection: React.FC<AboutSectionProps> = ({ openAccordion, toggl
</ul>
<a
href="https://discord.gg/labanquise"
className="inline-flex items-center bg-gradient-to-r from-banquise-blue to-banquise-blue-light text-white border-0 py-3 px-6 rounded-xl no-underline font-bold transition-all duration-300 hover:shadow-lg hover:-translate-y-1 hover:scale-105"
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

View File

@ -3,21 +3,28 @@ 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 animate-gentle-float backdrop-blur-sm border border-banquise-blue-lightest/30">
<img src={banquiseServer} alt="Logo La Banquise" className="w-full h-full object-contain" style={{ filter: 'drop-shadow(0 10px 25px rgba(31, 93, 137, 0.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" style={{ textShadow: '0 2px 10px rgba(0, 0, 0, 0.3)' }}>
<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" style={{ textShadow: '0 1px 4px rgba(0, 0, 0, 0.2)' }}>
<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">
<span className="text-center">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"></span>
<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>
);

View File

@ -1,15 +1,16 @@
import React from 'react';
// Define interface directly in the component file
// Declare the Service interface here
interface Service {
name: string;
url: string;
image: string;
icon: string;
description: string;
features: string[];
icon: string;
}
// Define interface directly in the component file
interface ServicesSectionProps {
services: Service[];
onServiceClick: (service: Service) => void;

View File

@ -10,7 +10,7 @@ export const TechFeaturesSection: React.FC = () => (
25+ serveurs pour répondre à vos besoins
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6 w-full">
<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">
🚀

View File

@ -1,6 +1,6 @@
import React from 'react';
// Define interface directly in the component file
// Définir l'interface localement :
interface AccordionItemProps {
title: string;
children: React.ReactNode;
@ -14,7 +14,7 @@ export const AccordionItem: React.FC<AccordionItemProps> = ({ title, children, i
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">{title}</span>
<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>

View 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>
);
};

View File

@ -1,13 +1,13 @@
import React from 'react';
import React, { useEffect } from 'react';
import { URLS } from '../../config/constants';
// Define interface directly in the component file
interface Service {
name: string;
url: string;
image: string;
icon: string;
description: string;
features: string[];
icon: string;
}
interface PopupProps {
@ -15,110 +15,127 @@ interface PopupProps {
onClose: () => void;
}
export const Popup: React.FC<PopupProps> = ({ service, onClose }) => (
<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-5xl w-full max-h-[90vh] overflow-y-auto shadow-2xl relative animate-slideUp border border-banquise-blue-lightest/20">
{/* Header */}
<div className="relative bg-gradient-to-r from-banquise-blue to-banquise-blue-light p-6 sm:p-8 text-white">
<button
onClick={onClose}
className="absolute top-4 sm:top-6 right-4 sm:right-6 bg-white/20 hover:bg-white/30 border-0 text-xl sm:text-2xl cursor-pointer text-white flex items-center justify-center w-10 h-10 sm:w-12 sm:h-12 rounded-full transition-all duration-200 backdrop-blur-sm hover:scale-110 active:scale-95"
>
×
</button>
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">
<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-5xl mt-0 mb-3 lg:mb-4 leading-tight">
{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">
<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 - Layout simplifié */}
<div className="p-6 sm:p-8">
{/* Description */}
<h3 className="text-lg sm:text-xl lg:text-2xl font-bold text-banquise-blue-dark mb-4 lg:mb-6 font-heading 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/80 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-green-500 to-green-600 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 en colonnes */}
<h3 className="text-lg sm:text-xl lg:text-2xl font-bold text-banquise-blue-dark mb-4 lg:mb-6 font-heading 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 md:grid-cols-2 lg:grid-cols-3 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 simplifié */}
<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"
{/* 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"
>
<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="https://discord.gg/labanquise" className="text-banquise-blue hover:text-banquise-blue-dark transition-colors duration-200 font-medium">Discord</a> pour obtenir du support
</p>
×
</button>
</div>
</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>
{/* Decorative elements - améliorés pour desktop */}
<div className="absolute top-0 right-0 w-24 h-24 lg:w-40 lg:h-40 bg-banquise-blue-lightest/10 rounded-full -translate-y-12 translate-x-12 lg:-translate-y-20 lg:translate-x-20 hidden sm:block"></div>
<div className="absolute bottom-0 left-0 w-16 h-16 lg:w-32 lg:h-32 bg-banquise-blue/5 rounded-full translate-y-8 -translate-x-8 lg:translate-y-16 lg:-translate-x-16 hidden sm:block"></div>
<div className="absolute top-1/2 left-0 w-20 h-20 lg:w-28 lg:h-28 bg-banquise-blue-light/5 rounded-full -translate-x-10 lg:-translate-x-14 hidden lg:block"></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>
</div>
);
);
};

View 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>
);
};

View 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;

View File

@ -50,6 +50,64 @@
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;
@ -82,3 +140,46 @@
.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 {
overflow-x: hidden;
}
/* Styles pour les popups */
.popup-content {
scrollbar-width: thin;
scrollbar-color: rgba(31, 93, 137, 0.3) transparent;
}
.popup-content::-webkit-scrollbar {
width: 6px;
}
.popup-content::-webkit-scrollbar-track {
background: transparent;
}
.popup-content::-webkit-scrollbar-thumb {
background: rgba(31, 93, 137, 0.3);
border-radius: 3px;
}
.popup-content::-webkit-scrollbar-thumb:hover {
background: rgba(31, 93, 137, 0.5);
}
/* 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;
}

View File

@ -3,6 +3,18 @@ import { createRoot } from 'react-dom/client'
import './index.css'
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(
<StrictMode>
<App />

View 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;

View 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;
}