This commit is contained in:
2026-05-04 18:22:36 +02:00
parent cdd2abaa96
commit 281a37c401
4 changed files with 84 additions and 28 deletions
+8 -12
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+6 -16
View File
@@ -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');
} }
} }