Restructuration majeure du projet: migration de flutt vers app, ajout de l'API et mise à jour du site web
This commit is contained in:
58
web/src/components/CookieConsent.svelte
Normal file
58
web/src/components/CookieConsent.svelte
Normal file
@@ -0,0 +1,58 @@
|
||||
<script>
|
||||
import { onMount, createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
// Fonction pour accepter les cookies
|
||||
function acceptCookies() {
|
||||
// Créer un cookie qui expire dans 2 jours
|
||||
const expiryDate = new Date();
|
||||
expiryDate.setDate(expiryDate.getDate() + 2);
|
||||
|
||||
// Définir le cookie avec la date d'expiration
|
||||
document.cookie = `geosector_cookies_accepted=true; expires=${expiryDate.toUTCString()}; path=/; SameSite=Lax`;
|
||||
|
||||
// Informer le composant parent que l'utilisateur a accepté
|
||||
dispatch('consent', { accepted: true });
|
||||
}
|
||||
|
||||
// Fonction pour refuser les cookies
|
||||
function refuseCookies() {
|
||||
// Définir un cookie de refus qui expire dans 2 jours aussi
|
||||
const expiryDate = new Date();
|
||||
expiryDate.setDate(expiryDate.getDate() + 2);
|
||||
|
||||
// Définir le cookie avec la date d'expiration
|
||||
document.cookie = `geosector_cookies_refused=true; expires=${expiryDate.toUTCString()}; path=/; SameSite=Lax`;
|
||||
|
||||
// Informer le composant parent que l'utilisateur a refusé
|
||||
dispatch('consent', { accepted: false });
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4">
|
||||
<div class="cookie-modal bg-white rounded-lg shadow-xl p-6 max-w-md w-full">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-4">Gestion des cookies</h2>
|
||||
|
||||
<p class="text-gray-600 mb-6">
|
||||
Nous utilisons des cookies pour suivre de façon anonyme l'activité sur ce site.
|
||||
Ces informations nous aident à améliorer votre expérience utilisateur.
|
||||
Les données sont collectées une fois tous les 2 jours. Vous pouvez les accepter ou les refuser.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-end">
|
||||
<button
|
||||
on:click={refuseCookies}
|
||||
class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-semibold py-2 px-4 rounded transition-colors"
|
||||
>
|
||||
Refuser
|
||||
</button>
|
||||
<button
|
||||
on:click={acceptCookies}
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded transition-colors"
|
||||
>
|
||||
Accepter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
103
web/src/components/Footer.svelte
Normal file
103
web/src/components/Footer.svelte
Normal file
@@ -0,0 +1,103 @@
|
||||
<script>
|
||||
// Fonction pour gérer la navigation et faire défiler la page vers le haut
|
||||
function handleNavigation(hash) {
|
||||
window.location.hash = hash;
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<footer class="bg-gray-800 text-white py-8">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div>
|
||||
<div class="flex items-center mb-4">
|
||||
<img src="/images/icon-geosector.svg" alt="Geosector" class="h-8 mr-3" />
|
||||
<h3 class="text-xl font-bold">Geosector</h3>
|
||||
</div>
|
||||
<p class="text-gray-300 mb-6">Une application puissante et intuitive, pour une gestion efficace de vos distributions.</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<p class="text-gray-300 font-semibold">Téléchargez notre application :</p>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<a href="https://apps.apple.com/store" class="inline-flex items-center bg-black text-white rounded-lg px-4 py-2 hover:bg-gray-700 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M17.5615 12.5514C17.5615 9.17603 20.3113 7.76353 20.4318 7.69478C18.9985 5.58978 16.7857 5.29103 16.0032 5.26978C14.1546 5.07353 12.3675 6.34603 11.432 6.34603C10.4753 6.34603 9.0315 5.29103 7.4663 5.32103C5.4392 5.35103 3.5495 6.51978 2.5292 8.32603C0.4292 11.9997 2.0047 17.3989 4.0197 20.2014C5.029 21.5785 6.2018 23.1104 7.739 23.0483C9.2232 22.9768 9.769 22.0924 11.5667 22.0924C13.3432 22.0924 13.856 23.0483 15.4107 23.006C17.011 22.9768 18.0313 21.6318 19.01 20.2439C20.171 18.6273 20.6447 17.0429 20.666 16.9489C20.6235 16.9277 17.5933 15.7166 17.5615 12.5514Z"
|
||||
/>
|
||||
<path d="M14.2855 3.77006C15.0787 2.80256 15.6037 1.48381 15.4407 0.144806C14.3157 0.187306 12.876 0.912306 12.0402 1.85856C11.295 2.69631 10.6542 4.06256 10.8385 5.35631C12.1002 5.44381 13.45 4.72381 14.2855 3.77006Z" />
|
||||
</svg>
|
||||
App Store
|
||||
</a>
|
||||
<a href="https://play.google.com/store" class="inline-flex items-center bg-black text-white rounded-lg px-4 py-2 hover:bg-gray-700 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.5231 8.74664L14.4231 6.41914L2.33563 18.5066C2.52813 18.9391 2.91063 19.2826 3.40563 19.3441L17.5231 8.74664Z" />
|
||||
<path d="M22.0002 11.3651C22.0002 10.7066 21.6602 10.0988 21.0692 9.76762L18.6927 8.31262L15.3302 10.8338L19.0327 13.5388L21.0402 12.3666C21.6367 12.0363 22.0002 11.7313 22.0002 11.3651Z" />
|
||||
<path d="M2.00031 4.68721V19.3134C2.00031 19.6454 2.10531 19.9077 2.27406 20.1097L14.8628 7.52096L2.93156 0.572206C2.38406 0.906706 2.00031 1.71046 2.00031 2.59046V4.68721Z" />
|
||||
<path d="M14.4234 6.41895L14.7697 6.18945L3.81094 0.141445C3.55969 0.0294453 3.28469 -0.0145547 3.01719 0.00294531L14.4234 6.41895Z" />
|
||||
</svg>
|
||||
Play Store
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-4">Liens rapides</h3>
|
||||
<ul class="space-y-2">
|
||||
<li class="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
<a href="#accueil" class="text-gray-300 hover:text-white transition-colors" on:click|preventDefault={() => handleNavigation('accueil')}>Accueil</a>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
<a href="#fonctionnalites" class="text-gray-300 hover:text-white transition-colors" on:click|preventDefault={() => handleNavigation('fonctionnalites')}>Fonctionnalités</a>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<a href="#contact" class="text-gray-300 hover:text-white transition-colors" on:click|preventDefault={() => handleNavigation('contact')}>Contact</a>
|
||||
</li>
|
||||
<li class="mt-4 pt-4 border-t border-gray-700"></li>
|
||||
<li class="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<a href="#mentions-legales" class="text-gray-300 hover:text-white transition-colors" on:click|preventDefault={() => handleNavigation('mentions-legales')}>Mentions légales</a>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
<a href="#politique-confidentialite" class="text-gray-300 hover:text-white transition-colors" on:click|preventDefault={() => handleNavigation('politique-confidentialite')}>Politique de confidentialité</a>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
<a href="#conditions-utilisation" class="text-gray-300 hover:text-white transition-colors" on:click|preventDefault={() => handleNavigation('conditions-utilisation')}>Conditions d'utilisation</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-4">Contact</h3>
|
||||
<address class="not-italic text-gray-300">
|
||||
<p>Email: contactgeosector@gmail.com</p>
|
||||
<p>Téléphone: +33 (0)7 69 09 17 06</p>
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-gray-700 mt-8 pt-8 text-gray-400">
|
||||
<div class="flex justify-between items-center">
|
||||
<p>© {new Date().getFullYear()} Geosector. Tous droits réservés.</p>
|
||||
<a href="https://d6soft.fr" class="text-blue-400 hover:text-blue-300 font-medium transition-colors" target="_blank" rel="noopener noreferrer">Conception D6SOFT</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
173
web/src/components/Header.svelte
Normal file
173
web/src/components/Header.svelte
Normal file
@@ -0,0 +1,173 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
// État actif pour la navigation
|
||||
let activePage = window.location.hash.slice(1) || 'accueil';
|
||||
let mobileMenuOpen = false;
|
||||
let baseAppUrl = '';
|
||||
|
||||
// Déterminer les URLs en fonction de l'environnement
|
||||
function getEnvironmentUrls() {
|
||||
const hostname = window.location.hostname;
|
||||
let appPrefix = '';
|
||||
|
||||
if (hostname === 'dev.geosector.fr' || hostname.includes('localhost')) {
|
||||
appPrefix = 'dapp';
|
||||
} else if (hostname === 'rec.geosector.fr') {
|
||||
appPrefix = 'rapp';
|
||||
} else {
|
||||
// Production ou autres environnements
|
||||
appPrefix = 'app';
|
||||
}
|
||||
|
||||
// Construire l'URL de base de l'application
|
||||
const domainParts = hostname.split('.');
|
||||
if (domainParts.length >= 2) {
|
||||
// Remplacer le sous-domaine ou ajouter un nouveau préfixe
|
||||
const domain = domainParts.slice(Math.max(domainParts.length - 2, 0)).join('.');
|
||||
return `https://${appPrefix}.${domain}`;
|
||||
}
|
||||
|
||||
// Fallback pour localhost ou cas non prévus
|
||||
return `https://${appPrefix}.geosector.fr`;
|
||||
}
|
||||
|
||||
// Fonction pour changer de page
|
||||
function changePage(page) {
|
||||
activePage = page;
|
||||
window.history.pushState({}, '', `/${page}`);
|
||||
closeMobileMenu();
|
||||
}
|
||||
|
||||
// Fonctions pour le menu mobile
|
||||
function toggleMobileMenu() {
|
||||
mobileMenuOpen = !mobileMenuOpen;
|
||||
}
|
||||
|
||||
function closeMobileMenu() {
|
||||
mobileMenuOpen = false;
|
||||
}
|
||||
|
||||
// Écouter les événements popstate pour mettre à jour la page active
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('popstate', () => {
|
||||
activePage = window.location.pathname.slice(1) || 'accueil';
|
||||
});
|
||||
}
|
||||
|
||||
// Initialiser les URLs et configurer les événements
|
||||
onMount(() => {
|
||||
// Déterminer l'URL de base de l'application
|
||||
baseAppUrl = getEnvironmentUrls();
|
||||
|
||||
const handleClickOutside = (event) => {
|
||||
const mobileMenu = document.getElementById('mobile-menu');
|
||||
const burgerButton = document.getElementById('burger-button');
|
||||
|
||||
if (mobileMenuOpen && mobileMenu && burgerButton &&
|
||||
!mobileMenu.contains(event.target) &&
|
||||
!burgerButton.contains(event.target)) {
|
||||
closeMobileMenu();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<header class="text-white bg-transparent backdrop-blur-sm z-50 relative">
|
||||
<div class="container mx-auto px-4 py-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-3">
|
||||
<a href="/accueil" on:click|preventDefault={() => changePage('accueil')}>
|
||||
<img src="/images/Logo-geosector-horizontal.svg" alt="Geosector" class="h-12" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Burger menu pour mobile -->
|
||||
<button id="burger-button" class="xl:hidden z-50" on:click={toggleMobileMenu}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-[#002C66]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
{#if mobileMenuOpen}
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
{:else}
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
{/if}
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Menu desktop -->
|
||||
<div class="hidden xl:flex items-center gap-6">
|
||||
<nav>
|
||||
<ul class="flex space-x-6">
|
||||
<li>
|
||||
<a href="/accueil" class={`text-[#002C66] hover:text-blue-500 transition-colors ${activePage === 'accueil' ? 'font-bold border-b-2 border-[#002C66]' : ''}`} on:click|preventDefault={() => changePage('accueil')}> Accueil </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/fonctionnalites" class={`text-[#002C66] hover:text-blue-500 transition-colors ${activePage === 'fonctionnalites' ? 'font-bold border-b-2 border-[#002C66]' : ''}`} on:click|preventDefault={() => changePage('fonctionnalites')}> Fonctionnalités </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/contact" class={`text-[#002C66] hover:text-blue-500 transition-colors ${activePage === 'contact' ? 'font-bold border-b-2 border-[#002C66]' : ''}`} on:click|preventDefault={() => changePage('contact')}> Contact </a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="flex gap-3 items-center">
|
||||
<a href="{baseAppUrl}/login/user" class="border-2 border-[#4CAF50] text-[#4CAF50] hover:bg-[#4CAF50] hover:text-white font-medium py-1.5 px-3 rounded-full transition-colors" on:click={() => { sessionStorage.setItem('loginType', 'user'); }}> Connexion Utilisateur </a>
|
||||
<a href="{baseAppUrl}/login" class="border-2 border-red-600 text-red-600 hover:bg-red-600 hover:text-white font-medium py-1.5 px-3 rounded-full transition-colors" on:click={() => { sessionStorage.setItem('loginType', 'admin'); }}> Connexion Administrateur </a>
|
||||
<a href="{baseAppUrl}/register" class="bg-[#E3170A] hover:bg-red-700 text-white font-medium py-2 px-4 rounded-full transition-colors"> S'inscrire </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Menu mobile -->
|
||||
<div id="mobile-menu" class={`fixed top-0 right-0 h-screen w-4/5 max-w-xs bg-white shadow-lg transform transition-transform duration-300 ease-in-out z-40 ${mobileMenuOpen ? 'translate-x-0' : 'translate-x-full'}`}>
|
||||
<div class="p-6">
|
||||
<div class="flex justify-end mb-8">
|
||||
<button on:click={closeMobileMenu} class="text-gray-600" aria-label="Fermer le menu">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav class="mb-8">
|
||||
<ul class="space-y-4">
|
||||
<li>
|
||||
<a href="/accueil" class={`block text-lg text-[#002C66] hover:text-blue-500 transition-colors ${activePage === 'accueil' ? 'font-bold' : ''}`} on:click|preventDefault={() => changePage('accueil')}> Accueil </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/fonctionnalites" class={`block text-lg text-[#002C66] hover:text-blue-500 transition-colors ${activePage === 'fonctionnalites' ? 'font-bold' : ''}`} on:click|preventDefault={() => changePage('fonctionnalites')}> Fonctionnalités </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/contact" class={`block text-lg text-[#002C66] hover:text-blue-500 transition-colors ${activePage === 'contact' ? 'font-bold' : ''}`} on:click|preventDefault={() => changePage('contact')}> Contact </a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="space-y-4">
|
||||
<a href="{baseAppUrl}/login/user" class="block w-full border-2 border-[#4CAF50] text-[#4CAF50] hover:bg-[#4CAF50] hover:text-white font-medium py-2 px-4 rounded-full transition-colors text-center mb-3" on:click={() => { sessionStorage.setItem('loginType', 'user'); }}> Connexion Utilisateur </a>
|
||||
<a href="{baseAppUrl}/login" class="block w-full border-2 border-red-600 text-red-600 hover:bg-red-600 hover:text-white font-medium py-2 px-4 rounded-full transition-colors text-center mb-3" on:click={() => { sessionStorage.setItem('loginType', 'admin'); }}> Connexion Administrateur </a>
|
||||
<a href="{baseAppUrl}/register" class="block w-full bg-[#E3170A] hover:bg-red-700 text-white font-medium py-2 px-4 rounded-full transition-colors text-center"> S'inscrire </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Overlay pour le fond lorsque le menu mobile est ouvert -->
|
||||
{#if mobileMenuOpen}
|
||||
<div
|
||||
class="fixed inset-0 bg-black bg-opacity-50 z-30"
|
||||
on:click={closeMobileMenu}
|
||||
on:keydown={(e) => e.key === 'Escape' && closeMobileMenu()}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Fermer le menu"
|
||||
></div>
|
||||
{/if}
|
||||
</header>
|
||||
Reference in New Issue
Block a user