Toasts
This commit is contained in:
@@ -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).
|
Météo Intelligente : Prévisions réelles ou archives historiques (Open-Meteo).
|
||||||
|
|
||||||
🚀 Roadmap d'Optimisation & Debug (Incrémental)
|
## 🚀 Roadmap d'Optimisation
|
||||||
Étape 1 : Fondations & Robustesse (Terminé ✅)
|
|
||||||
[x] Moteur AJAX : Création de pachaFetch pour gérer proprement le JSON et les erreurs PHP.
|
|
||||||
|
|
||||||
[x] Correction "Action" : Sécurisation des formulaires via getAttribute('action').
|
- [x] **[Quickwin] Sécurisation AJAX (pachaFetch)**
|
||||||
|
- [x] **[Quickwin] Correction du piège "action"**
|
||||||
[x] Centralisation : Création de config.php pour les IDs (Alex/Laia) et constantes globales.
|
- [x] **[Structure] Centralisation des Constantes**
|
||||||
|
- [x] **[UX] Harmonisation Mobile**
|
||||||
Étape 2 : UX & Mobile (En cours 🏃♂️)
|
- [x] **[UX] Toasts & Notifications modernes**
|
||||||
[ ] 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.
|
|
||||||
|
|
||||||
Étape 3 : Performance & Backend
|
Étape 3 : Performance & Backend
|
||||||
[ ] Audit SQL : Remplacement des requêtes en boucle par des jointures.
|
[ ] 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
|
📌 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.4 : Centralisation des constantes (IDs, devises) dans includes/config.php et injection dans window.CONFIG.
|
||||||
|
|
||||||
v1.0.3 : Migration du module Calendrier vers pachaFetch.
|
v1.0.3 : Migration du module Calendrier vers pachaFetch.
|
||||||
|
|||||||
+29
@@ -323,6 +323,35 @@ body.no-scroll {
|
|||||||
margin-top: auto;
|
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 === */
|
/* === MOBILE RESPONSIVE === */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.pf-header {
|
.pf-header {
|
||||||
|
|||||||
+41
@@ -151,4 +151,45 @@ $currentLang = $_SESSION['app_lang'] ?? 'fr';
|
|||||||
throw err;
|
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 = `
|
||||||
|
<div class="pf-modal-content" style="max-width: 400px; align-self: center;">
|
||||||
|
<h3 style="margin-top:0;">${title}</h3>
|
||||||
|
<p style="color:var(--text-muted);">${message}</p>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="pf-btn btn-secondary" id="confirm-cancel">${tr('btn_cancel')}</button>
|
||||||
|
<button class="pf-btn" id="confirm-ok" style="background:var(--danger);">${tr('btn_delete')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
|
document.getElementById('confirm-cancel').onclick = () => { modal.remove(); resolve(false); };
|
||||||
|
document.getElementById('confirm-ok').onclick = () => { modal.remove(); resolve(true); };
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -819,29 +819,19 @@ window.addEventListener('click', (e) => {
|
|||||||
|
|
||||||
// --- 2. SUPPRESSION ASYNCHRONE ---
|
// --- 2. SUPPRESSION ASYNCHRONE ---
|
||||||
async function deleteExpense(id) {
|
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();
|
const formData = new FormData();
|
||||||
formData.append("action", "delete_expense");
|
formData.append("action", "delete_expense");
|
||||||
formData.append("id", id);
|
formData.append("id", id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Envoi au handler PHP que nous venons de créer en haut de suivi.php
|
await pachaFetch("budget.php?tab=suivi", { method: "POST", body: formData });
|
||||||
const response = await fetch("budget.php?tab=suivi", { method: "POST", body: formData });
|
showToast("Dépense supprimée !"); //
|
||||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
setTimeout(() => window.location.reload(), 800);
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
showToast("Erreur lors de la suppression", "error");
|
||||||
alert(window.I18N['bud_err_tech'] || 'Erreur technique');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user