523 lines
20 KiB
TypeScript
523 lines
20 KiB
TypeScript
import { FiUser, FiDatabase, FiShield, FiChevronDown } from 'react-icons/fi'
|
|
import { FaDiscord, FaArrowRight, FaEnvelope, FaGithub, FaNetworkWired, FaServer, FaLaptopCode, FaCloudUploadAlt, FaExternalLinkAlt } from 'react-icons/fa'
|
|
import { FiX, FiExternalLink } from 'react-icons/fi'
|
|
import './App.css'
|
|
import icebergImage from './assets/iceberg.png'
|
|
import logoImage from './assets/banquise_server.svg'
|
|
import { useEffect, useState, useMemo, useCallback, useRef } from 'react'
|
|
|
|
import aboutImage from './assets/banquise.png'
|
|
|
|
function App() {
|
|
const [selectedService, setSelectedService] = useState<number | null>(null);
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
const mobileMenuRef = useRef<HTMLDivElement>(null);
|
|
|
|
const services = useMemo(() => [
|
|
{
|
|
name: "Wiki",
|
|
url: "https://wiki.la-banquise.fr/",
|
|
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 !"
|
|
},
|
|
{
|
|
name: "Git",
|
|
url: "https://git.la-banquise.fr/",
|
|
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."
|
|
},
|
|
{
|
|
name: "Panel jeux",
|
|
url: "https://panel.la-banquise.fr/auth/login",
|
|
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."
|
|
},
|
|
], []);
|
|
|
|
const [icebergs, setIcebergs] = useState<Array<{
|
|
id: number,
|
|
x: number,
|
|
y: number,
|
|
scale: number,
|
|
rotation: number,
|
|
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) {
|
|
setReducedMotion(true);
|
|
}
|
|
}, []);
|
|
|
|
const positionIcebergs = useCallback(() => {
|
|
const newIcebergs = [];
|
|
|
|
const positions = [
|
|
{ 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 (
|
|
<div className="app-container">
|
|
<a href="#main-content" className="sr-only focus:not-sr-only">Passer au contenu principal</a>
|
|
|
|
<header>
|
|
<nav className="navbar" aria-label="Navigation principale">
|
|
<div className="navbar-left">
|
|
<img src={logoImage} alt="Logo La Banquise" className="site-logo" />
|
|
<h1 className="site-name">La Banquise</h1>
|
|
</div>
|
|
|
|
<button
|
|
className={`navbar-mobile-toggle ${mobileMenuOpen ? 'active' : ''}`}
|
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
aria-expanded={mobileMenuOpen}
|
|
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>
|
|
</main>
|
|
|
|
<footer className="footer">
|
|
<div className="footer-content">
|
|
<div className="footer-column">
|
|
<h4>La Banquise</h4>
|
|
<ul>
|
|
<li><a href="#about">À propos</a></li>
|
|
<li><a href="#services">Services</a></li>
|
|
<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>
|
|
);
|
|
}
|
|
|
|
export default App;
|