personnalisation des pop-up de confirmation/avertissement

This commit is contained in:
Esenjin 2025-02-16 13:11:50 +01:00
parent 5dc3416009
commit f9a00576dc
9 changed files with 328 additions and 41 deletions

View File

@ -186,5 +186,7 @@ $stories = Stories::getAll();
margin-bottom: var(--spacing-md); margin-bottom: var(--spacing-md);
} }
</style> </style>
<link rel="stylesheet" href="../assets/css/dialog.css">
<script src="../assets/js/dialog.js"></script>
</body> </body>
</html> </html>

View File

@ -68,5 +68,7 @@ $stories = Stories::getAll();
</main> </main>
<script src="../assets/js/admin.js"></script> <script src="../assets/js/admin.js"></script>
<link rel="stylesheet" href="../assets/css/dialog.css">
<script src="../assets/js/dialog.js"></script>
</body> </body>
</html> </html>

View File

@ -155,5 +155,7 @@ $config = Config::load();
}); });
}); });
</script> </script>
<link rel="stylesheet" href="../assets/css/dialog.css">
<script src="../assets/js/dialog.js"></script>
</body> </body>
</html> </html>

View File

@ -138,5 +138,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<button type="submit" class="button">Enregistrer les modifications</button> <button type="submit" class="button">Enregistrer les modifications</button>
</form> </form>
</main> </main>
<link rel="stylesheet" href="../assets/css/dialog.css">
<script src="../assets/js/dialog.js"></script>
</body> </body>
</html> </html>

View File

@ -253,5 +253,7 @@ function generateSlug($title) {
}); });
}); });
</script> </script>
<link rel="stylesheet" href="../assets/css/dialog.css">
<script src="../assets/js/dialog.js"></script>
</body> </body>
</html> </html>

107
assets/css/dialog.css Normal file
View File

@ -0,0 +1,107 @@
/* dialog.css */
.confirm-dialog {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.confirm-dialog.show {
opacity: 1;
}
.confirm-dialog-content {
background-color: var(--bg-tertiary);
border-radius: var(--radius-md);
padding: var(--spacing-xl);
max-width: 90%;
width: 400px;
border: 1px solid var(--border-color);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
transform: translate(-50%, -60%);
position: absolute;
top: 50%;
left: 50%;
transition: transform 0.2s ease-in-out;
}
.confirm-dialog.show .confirm-dialog-content {
transform: translate(-50%, -50%);
}
.confirm-dialog h3 {
margin: 0 0 var(--spacing-md);
color: var(--text-primary);
font-size: 1.5rem;
}
.confirm-dialog p {
margin: 0 0 var(--spacing-xl);
color: var(--text-secondary);
line-height: 1.5;
}
.confirm-dialog-buttons {
display: flex;
justify-content: flex-end;
gap: var(--spacing-md);
}
.confirm-dialog .button {
padding: var(--spacing-sm) var(--spacing-lg);
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 1rem;
transition: all var(--transition-fast);
}
.confirm-dialog .button.confirm {
background-color: var(--accent-primary);
color: var(--text-tertiary);
}
.confirm-dialog .button.confirm:hover {
background-color: var(--accent-secondary);
}
.confirm-dialog .button.danger {
background-color: var(--error-color);
color: white;
}
.confirm-dialog .button.danger:hover {
background-color: #a33;
}
.confirm-dialog .button.cancel {
background-color: var(--bg-secondary);
color: var(--text-primary);
}
.confirm-dialog .button.cancel:hover {
background-color: var(--bg-primary);
}
@media (max-width: 480px) {
.confirm-dialog-content {
width: calc(100% - 32px);
padding: var(--spacing-lg);
}
.confirm-dialog-buttons {
flex-direction: column;
}
.confirm-dialog .button {
width: 100%;
}
}

View File

@ -16,7 +16,17 @@ document.addEventListener('DOMContentLoaded', function() {
function confirmDeletion(storyId) { function confirmDeletion(storyId) {
const storyCard = document.querySelector(`[data-id="${storyId}"]`).closest('.story-item'); const storyCard = document.querySelector(`[data-id="${storyId}"]`).closest('.story-item');
const title = storyCard.querySelector('h2').textContent; const title = storyCard.querySelector('h2').textContent;
return confirm(`Voulez-vous vraiment supprimer le roman "${title}" ? Cette action est irréversible.`);
confirmDialog.show({
title: 'Suppression du roman',
message: `Voulez-vous vraiment supprimer le roman "${title}" ? Cette action est irréversible.`,
confirmText: 'Supprimer',
confirmClass: 'danger',
onConfirm: async () => {
await deleteStory(storyId);
}
});
return false; // Pour empêcher l'exécution immédiate
} }
// Suppression d'un roman via l'API // Suppression d'un roman via l'API
@ -78,9 +88,15 @@ document.addEventListener('DOMContentLoaded', function() {
const logoutForm = document.querySelector('.logout-form'); const logoutForm = document.querySelector('.logout-form');
if (logoutForm) { if (logoutForm) {
logoutForm.addEventListener('submit', (e) => { logoutForm.addEventListener('submit', (e) => {
if (!confirm('Voulez-vous vraiment vous déconnecter ?')) { e.preventDefault();
e.preventDefault(); confirmDialog.show({
} title: 'Confirmation de déconnexion',
message: 'Voulez-vous vraiment vous déconnecter ?',
confirmText: 'Se déconnecter',
onConfirm: () => {
logoutForm.submit();
}
});
}); });
} }

145
assets/js/dialog.js Normal file
View File

@ -0,0 +1,145 @@
class ConfirmDialog {
constructor() {
this.createDialog();
}
createDialog() {
// Création du conteneur principal
this.dialog = document.createElement('div');
this.dialog.className = 'confirm-dialog';
this.dialog.style.display = 'none';
// Création du contenu
const content = document.createElement('div');
content.className = 'confirm-dialog-content';
// En-tête avec titre
this.title = document.createElement('h3');
content.appendChild(this.title);
// Message
this.message = document.createElement('p');
content.appendChild(this.message);
// Conteneur des boutons
const buttons = document.createElement('div');
buttons.className = 'confirm-dialog-buttons';
// Bouton Confirmer
this.confirmButton = document.createElement('button');
this.confirmButton.className = 'button confirm';
buttons.appendChild(this.confirmButton);
// Bouton Annuler
this.cancelButton = document.createElement('button');
this.cancelButton.className = 'button cancel';
this.cancelButton.textContent = 'Annuler';
buttons.appendChild(this.cancelButton);
content.appendChild(buttons);
this.dialog.appendChild(content);
document.body.appendChild(this.dialog);
// Fermeture au clic en dehors
this.dialog.addEventListener('click', (e) => {
if (e.target === this.dialog) {
this.hide();
}
});
// Fermeture avec Echap
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.dialog.style.display === 'block') {
this.hide();
}
});
}
show(options) {
const defaults = {
title: 'Confirmation',
message: 'Êtes-vous sûr ?',
confirmText: 'Confirmer',
confirmClass: 'confirm',
cancelText: 'Annuler',
onConfirm: () => {},
onCancel: () => {}
};
const settings = { ...defaults, ...options };
this.title.textContent = settings.title;
this.message.textContent = settings.message;
this.confirmButton.textContent = settings.confirmText;
this.confirmButton.className = `button ${settings.confirmClass}`;
// Réinitialisation des événements
const newConfirmBtn = this.confirmButton.cloneNode(true);
const newCancelBtn = this.cancelButton.cloneNode(true);
this.confirmButton.parentNode.replaceChild(newConfirmBtn, this.confirmButton);
this.cancelButton.parentNode.replaceChild(newCancelBtn, this.cancelButton);
this.confirmButton = newConfirmBtn;
this.cancelButton = newCancelBtn;
// Nouveaux gestionnaires d'événements
this.confirmButton.addEventListener('click', () => {
settings.onConfirm();
this.hide();
});
this.cancelButton.addEventListener('click', () => {
settings.onCancel();
this.hide();
});
// Affichage avec animation
this.dialog.style.display = 'block';
requestAnimationFrame(() => {
this.dialog.classList.add('show');
});
}
hide() {
this.dialog.classList.remove('show');
setTimeout(() => {
this.dialog.style.display = 'none';
}, 200);
}
}
// Création d'une instance globale
const confirmDialog = new ConfirmDialog();
// Exemple d'utilisation pour la suppression d'un chapitre
document.querySelectorAll('.delete-chapter').forEach(button => {
button.addEventListener('click', (e) => {
const chapterTitle = e.target.closest('.chapter-item').querySelector('.chapter-title').textContent;
confirmDialog.show({
title: 'Supprimer le chapitre',
message: `Voulez-vous vraiment supprimer le chapitre "${chapterTitle}" ? Cette action est irréversible.`,
confirmText: 'Supprimer',
confirmClass: 'danger',
onConfirm: () => {
// Code de suppression existant
}
});
});
});
// Exemple d'utilisation pour la suppression d'un roman
document.querySelectorAll('.delete-story').forEach(button => {
button.addEventListener('click', (e) => {
const storyTitle = e.target.closest('.story-item').querySelector('h2').textContent;
confirmDialog.show({
title: 'Supprimer le roman',
message: `Voulez-vous vraiment supprimer le roman "${storyTitle}" ? Cette action est irréversible.`,
confirmText: 'Supprimer',
confirmClass: 'danger',
onConfirm: () => {
// Code de suppression existant
}
});
});
});

View File

@ -183,11 +183,17 @@ document.addEventListener('DOMContentLoaded', function() {
if (cancelEditBtn) { if (cancelEditBtn) {
cancelEditBtn.addEventListener('click', () => { cancelEditBtn.addEventListener('click', () => {
if (hasUnsavedChanges()) { if (hasUnsavedChanges()) {
if (!confirm('Des modifications non sauvegardées seront perdues. Voulez-vous vraiment fermer ?')) { confirmDialog.show({
return; title: 'Modifications non sauvegardées',
} message: 'Des modifications non sauvegardées seront perdues. Voulez-vous vraiment fermer ?',
confirmText: 'Fermer sans sauvegarder',
onConfirm: () => {
modal.style.display = 'none';
}
});
} else {
modal.style.display = 'none';
} }
modal.style.display = 'none';
}); });
} }
@ -241,41 +247,44 @@ document.addEventListener('DOMContentLoaded', function() {
const chapterItem = target.closest('.chapter-item'); const chapterItem = target.closest('.chapter-item');
const chapterId = chapterItem.dataset.id; const chapterId = chapterItem.dataset.id;
const chapterTitle = chapterItem.querySelector('.chapter-title').textContent; const chapterTitle = chapterItem.querySelector('.chapter-title').textContent;
if (confirm(`Voulez-vous vraiment supprimer le chapitre "${chapterTitle}" ? Cette action est irréversible.`)) { confirmDialog.show({
try { title: 'Suppression du chapitre',
const response = await fetch('api/delete-chapter.php', { message: `Voulez-vous vraiment supprimer le chapitre "${chapterTitle}" ? Cette action est irréversible.`,
method: 'POST', confirmText: 'Supprimer',
headers: { confirmClass: 'danger',
'Content-Type': 'application/json', onConfirm: async () => {
}, try {
body: JSON.stringify({ const response = await fetch('api/delete-chapter.php', {
storyId: storyId, method: 'POST',
chapterId: chapterId headers: {
}) 'Content-Type': 'application/json',
}); },
body: JSON.stringify({
if (!response.ok) { storyId: storyId,
throw new Error('Erreur réseau'); chapterId: chapterId
})
});
if (!response.ok) throw new Error('Erreur réseau');
const result = await response.json();
if (result.success) {
chapterItem.style.opacity = '0';
chapterItem.style.transform = 'translateX(-100%)';
setTimeout(() => {
chapterItem.remove();
showNotification('Chapitre supprimé avec succès');
}, 300);
} else {
throw new Error(result.error || 'Erreur lors de la suppression');
}
} catch (error) {
console.error('Erreur:', error);
showNotification(error.message, 'error');
} }
const result = await response.json();
if (result.success) {
// Animation de suppression
chapterItem.style.opacity = '0';
chapterItem.style.transform = 'translateX(-100%)';
setTimeout(() => {
chapterItem.remove();
showNotification('Chapitre supprimé avec succès');
}, 300);
} else {
throw new Error(result.error || 'Erreur lors de la suppression');
}
} catch (error) {
console.error('Erreur:', error);
showNotification(error.message, 'error');
} }
} });
} }
}); });
} }