From 281a37c4010c73c0b2c33662cf015731e2f89010 Mon Sep 17 00:00:00 2001 From: "fefe.clochette" Date: Mon, 4 May 2026 18:22:36 +0200 Subject: [PATCH] Toasts --- README.md | 20 +++++++---------- global.css | 29 ++++++++++++++++++++++++ header.php | 41 ++++++++++++++++++++++++++++++++++ modules/budget/views/suivi.php | 22 +++++------------- 4 files changed, 84 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 3fa3bd4..1940b3f 100644 --- a/README.md +++ b/README.md @@ -26,18 +26,13 @@ Moteur de Roadtrip : Cartographie interactive (Leaflet), itinéraires (OSRM) et Météo Intelligente : Prévisions réelles ou archives historiques (Open-Meteo). -🚀 Roadmap d'Optimisation & Debug (Incrémental) -Étape 1 : Fondations & Robustesse (Terminé ✅) -[x] Moteur AJAX : Création de pachaFetch pour gérer proprement le JSON et les erreurs PHP. +## 🚀 Roadmap d'Optimisation -[x] Correction "Action" : Sécurisation des formulaires via getAttribute('action'). - -[x] Centralisation : Création de config.php pour les IDs (Alex/Laia) et constantes globales. - -Étape 2 : UX & Mobile (En cours 🏃‍♂️) -[ ] Harmonisation Mobile : Généralisation des "Bottom Sheets" pour les modales sur smartphone. - -[ ] Z-Index Master : Audit des priorités d'affichage des menus contextuels. +- [x] **[Quickwin] Sécurisation AJAX (pachaFetch)** +- [x] **[Quickwin] Correction du piège "action"** +- [x] **[Structure] Centralisation des Constantes** +- [x] **[UX] Harmonisation Mobile** +- [x] **[UX] Toasts & Notifications modernes** Étape 3 : Performance & Backend [ ] Audit SQL : Remplacement des requêtes en boucle par des jointures. @@ -46,7 +41,8 @@ Météo Intelligente : Prévisions réelles ou archives historiques (Open-Meteo) 📌 Patch / Versioning -- **v1.1.0 :** Refonte de l'expérience mobile. Toutes les modales de l'application sont désormais des "Bottom Sheets" sur smartphone. +- **v1.2.0 :** Modernisation de l'UI. Remplacement des alertes systèmes par des Toasts et des confirmations sur-mesure. +- **v1.1.0 :** Refonte de l'expérience mobile (Bottom Sheets). v1.0.4 : Centralisation des constantes (IDs, devises) dans includes/config.php et injection dans window.CONFIG. v1.0.3 : Migration du module Calendrier vers pachaFetch. diff --git a/global.css b/global.css index 855fb1d..57f8d1e 100644 --- a/global.css +++ b/global.css @@ -323,6 +323,35 @@ body.no-scroll { margin-top: auto; } +/* Notifications Toasts */ +.pf-toast { + position: fixed; + bottom: 20px; + right: 20px; + padding: 12px 24px; + background: white; + border-radius: 12px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); + border-left: 5px solid var(--primary); + z-index: 10000; + transform: translateX(120%); + transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); + font-weight: 600; + color: var(--text-main); +} +.pf-toast.is-visible { + transform: translateX(0); +} +.pf-toast--success { + border-left-color: #10b981; +} +.pf-toast--error { + border-left-color: #ef4444; +} +.pf-toast--warning { + border-left-color: #f59e0b; +} + /* === MOBILE RESPONSIVE === */ @media (max-width: 768px) { .pf-header { diff --git a/header.php b/header.php index 9539b98..91b2226 100644 --- a/header.php +++ b/header.php @@ -151,4 +151,45 @@ $currentLang = $_SESSION['app_lang'] ?? 'fr'; throw err; } } + + /** + * UI Utility : Toasts (Notifications) + */ +function showToast(message, type = 'success') { + const toast = document.createElement('div'); + toast.className = `pf-toast pf-toast--${type}`; + toast.innerHTML = message; + document.body.appendChild(toast); + + // Animation et suppression + setTimeout(() => toast.classList.add('is-visible'), 100); + setTimeout(() => { + toast.classList.remove('is-visible'); + setTimeout(() => toast.remove(), 300); + }, 3000); +} + +/** + * UI Utility : Confirmation stylisée (Remplace confirm()) + */ +async function pachaConfirm(title, message) { + return new Promise((resolve) => { + const modal = document.createElement('div'); + modal.className = 'pf-modal open'; + modal.innerHTML = ` +
+

${title}

+

${message}

+ +
+ `; + document.body.appendChild(modal); + + document.getElementById('confirm-cancel').onclick = () => { modal.remove(); resolve(false); }; + document.getElementById('confirm-ok').onclick = () => { modal.remove(); resolve(true); }; + }); +} \ No newline at end of file diff --git a/modules/budget/views/suivi.php b/modules/budget/views/suivi.php index cda909a..8ec448f 100644 --- a/modules/budget/views/suivi.php +++ b/modules/budget/views/suivi.php @@ -819,29 +819,19 @@ window.addEventListener('click', (e) => { // --- 2. SUPPRESSION ASYNCHRONE --- async function deleteExpense(id) { - if (!confirm(window.I18N['bud_confirm_delete'] || "Confirmer ?")) return; + const confirmed = await pachaConfirm("Suppression", tr('bud_confirm_delete')); + if (!confirmed) return; const formData = new FormData(); formData.append("action", "delete_expense"); formData.append("id", id); try { - // Envoi au handler PHP que nous venons de créer en haut de suivi.php - const response = await fetch("budget.php?tab=suivi", { method: "POST", body: formData }); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - - const textResult = await response.text(); - try { - const result = JSON.parse(textResult); - if (result.success) window.location.reload(); - else alert((window.I18N['error_occured'] || 'Erreur') + " : " + (result.error || "")); - } catch (e) { - console.error("Réponse non-JSON :", textResult); - window.location.reload(); // Sécurité : on recharge quand même si le serveur dérive - } + await pachaFetch("budget.php?tab=suivi", { method: "POST", body: formData }); + showToast("Dépense supprimée !"); // + setTimeout(() => window.location.reload(), 800); } catch (err) { - console.error(err); - alert(window.I18N['bud_err_tech'] || 'Erreur technique'); + showToast("Erreur lors de la suppression", "error"); } }