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).
|
||||
|
||||
🚀 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.
|
||||
|
||||
+29
@@ -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 {
|
||||
|
||||
+41
@@ -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 = `
|
||||
<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>
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user