415 lines
16 KiB
JavaScript
415 lines
16 KiB
JavaScript
document.addEventListener('DOMContentLoaded', function() {
|
|
// Fonction de notification
|
|
function showNotification(message, type = 'success') {
|
|
const notification = document.createElement('div');
|
|
notification.className = `notification ${type}`;
|
|
notification.textContent = message;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
// Animation d'entrée
|
|
setTimeout(() => {
|
|
notification.style.opacity = '1';
|
|
notification.style.transform = 'translateY(0)';
|
|
}, 10);
|
|
|
|
// Auto-suppression après 3 secondes
|
|
setTimeout(() => {
|
|
notification.style.opacity = '0';
|
|
notification.style.transform = 'translateY(-100%)';
|
|
setTimeout(() => notification.remove(), 300);
|
|
}, 3000);
|
|
}
|
|
|
|
// Gestion de la modale d'accès
|
|
const accessModal = document.getElementById('accessModal');
|
|
const cancelAccessBtn = document.getElementById('cancelAccess');
|
|
const saveAccessBtn = document.getElementById('saveAccess');
|
|
const modalStoryTitle = document.getElementById('modalStoryTitle');
|
|
let currentStoryId = null;
|
|
|
|
// Ouverture de la modale d'accès
|
|
document.querySelectorAll('.manage-access').forEach(button => {
|
|
button.addEventListener('click', async function() {
|
|
currentStoryId = this.dataset.id;
|
|
const storyTitle = this.closest('.story-item').querySelector('h2').textContent;
|
|
modalStoryTitle.textContent = storyTitle;
|
|
|
|
// Réinitialiser les checkboxes
|
|
document.querySelectorAll('input[name="user_access[]"]').forEach(checkbox => {
|
|
if (!checkbox.disabled) {
|
|
checkbox.checked = false;
|
|
}
|
|
});
|
|
|
|
try {
|
|
// Charger les autorisations actuelles
|
|
const response = await fetch(`story-edit.php?id=${currentStoryId}`);
|
|
const data = await response.text();
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(data, 'text/html');
|
|
|
|
// Extraire les données du script
|
|
const scripts = doc.querySelectorAll('script');
|
|
let accessData = [];
|
|
|
|
for (const script of scripts) {
|
|
if (script.textContent.includes('storyAccess')) {
|
|
const match = script.textContent.match(/storyAccess\s*=\s*(\[.*?\])/s);
|
|
if (match && match[1]) {
|
|
try {
|
|
accessData = JSON.parse(match[1]);
|
|
} catch (e) {
|
|
console.error('Erreur lors du parsing des données d\'accès:', e);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mettre à jour les checkboxes
|
|
accessData.forEach(userId => {
|
|
const checkbox = document.querySelector(`input[name="user_access[]"][value="${userId}"]`);
|
|
if (checkbox && !checkbox.disabled) {
|
|
checkbox.checked = true;
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement des autorisations:', error);
|
|
}
|
|
|
|
accessModal.style.display = 'block';
|
|
});
|
|
});
|
|
|
|
// Fermeture de la modale d'accès
|
|
cancelAccessBtn.addEventListener('click', function() {
|
|
accessModal.style.display = 'none';
|
|
});
|
|
|
|
// Fermeture de la modale en cliquant à l'extérieur
|
|
window.addEventListener('click', function(event) {
|
|
if (event.target === accessModal) {
|
|
accessModal.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// Sauvegarde des autorisations d'accès
|
|
saveAccessBtn.addEventListener('click', async function() {
|
|
if (!currentStoryId) return;
|
|
|
|
// Récupérer les utilisateurs sélectionnés
|
|
const selectedUsers = Array.from(document.querySelectorAll('input[name="user_access[]"]:checked'))
|
|
.map(checkbox => checkbox.value);
|
|
|
|
try {
|
|
const response = await fetch('api/story-access.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
storyId: currentStoryId,
|
|
access: selectedUsers
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Erreur lors de la mise à jour des autorisations');
|
|
}
|
|
|
|
const result = await response.json();
|
|
if (result.success) {
|
|
showNotification('Autorisations mises à jour avec succès');
|
|
accessModal.style.display = 'none';
|
|
} else {
|
|
throw new Error(result.error || 'Erreur inconnue');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Erreur:', error);
|
|
showNotification(error.message, 'error');
|
|
}
|
|
});
|
|
|
|
// Gestion du menu mobile
|
|
const navMenu = document.querySelector('.nav-menu');
|
|
|
|
function initializeTooltips() {
|
|
// Ajouter le texte des tooltips à côté des icônes pour mobile
|
|
document.querySelectorAll('.tooltip:not(.tooltip-initialized)').forEach(button => {
|
|
if (!button.querySelector('.tooltip-text')) {
|
|
const tooltip = button.getAttribute('data-tooltip');
|
|
if (tooltip) {
|
|
const tooltipSpan = document.createElement('span');
|
|
tooltipSpan.className = 'tooltip-text';
|
|
tooltipSpan.textContent = tooltip;
|
|
button.appendChild(tooltipSpan);
|
|
button.classList.add('tooltip-initialized');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Observer les changements du DOM pour initialiser les nouveaux tooltips
|
|
const tooltipObserver = new MutationObserver(function(mutations) {
|
|
mutations.forEach(function(mutation) {
|
|
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
|
|
initializeTooltips();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Initialiser les tooltips existants et commencer l'observation
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
initializeTooltips();
|
|
tooltipObserver.observe(document.body, { childList: true, subtree: true });
|
|
|
|
// Vérifier le mode mobile au chargement et au redimensionnement
|
|
function checkMobileMode() {
|
|
const isMobile = window.innerWidth <= 768;
|
|
|
|
// Gestion des conteneurs d'actions
|
|
const actionContainers = document.querySelectorAll('.story-actions, .chapter-actions, .user-actions');
|
|
actionContainers.forEach(container => {
|
|
if (isMobile) {
|
|
container.classList.add('active');
|
|
} else {
|
|
container.classList.remove('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
checkMobileMode();
|
|
window.addEventListener('resize', checkMobileMode);
|
|
});
|
|
|
|
// Créer le bouton hamburger s'il n'existe pas déjà
|
|
if (!document.querySelector('.menu-toggle')) {
|
|
const menuToggle = document.createElement('button');
|
|
menuToggle.className = 'menu-toggle';
|
|
menuToggle.innerHTML = '☰';
|
|
menuToggle.setAttribute('aria-label', 'Menu');
|
|
|
|
// Insérer le bouton avant le menu
|
|
navMenu.parentNode.insertBefore(menuToggle, navMenu);
|
|
|
|
// Ajouter le texte des tooltips à côté des icônes pour mobile
|
|
document.querySelectorAll('.nav-menu .tooltip').forEach(button => {
|
|
const tooltip = button.getAttribute('data-tooltip');
|
|
const tooltipSpan = document.createElement('span');
|
|
tooltipSpan.className = 'tooltip-text';
|
|
tooltipSpan.textContent = tooltip;
|
|
button.appendChild(tooltipSpan);
|
|
});
|
|
|
|
// Gérer les clics sur le bouton
|
|
menuToggle.addEventListener('click', function() {
|
|
navMenu.classList.toggle('active');
|
|
});
|
|
|
|
// Fermer le menu au clic en dehors
|
|
document.addEventListener('click', function(e) {
|
|
if (!navMenu.contains(e.target) && !menuToggle.contains(e.target)) {
|
|
navMenu.classList.remove('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Ajouter le texte des tooltips à côté des icônes pour mobile - pour tous les tooltips
|
|
function addTooltipText() {
|
|
document.querySelectorAll('.tooltip').forEach(button => {
|
|
// Vérifier si le tooltip-text n'existe pas déjà
|
|
if (!button.querySelector('.tooltip-text')) {
|
|
const tooltip = button.getAttribute('data-tooltip');
|
|
const tooltipSpan = document.createElement('span');
|
|
tooltipSpan.className = 'tooltip-text';
|
|
tooltipSpan.textContent = tooltip;
|
|
button.appendChild(tooltipSpan);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Appliquer à tous les tooltips du site
|
|
addTooltipText();
|
|
|
|
// Détection de la taille d'écran pour activer le mode mobile
|
|
function checkMobileMode() {
|
|
const isMobile = window.innerWidth <= 768;
|
|
|
|
// Gestion des actions pour les romans
|
|
const storyActions = document.querySelectorAll('.story-actions');
|
|
storyActions.forEach(action => {
|
|
if (isMobile) {
|
|
action.classList.add('active');
|
|
} else {
|
|
action.classList.remove('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Vérifier au chargement et au redimensionnement
|
|
checkMobileMode();
|
|
window.addEventListener('resize', checkMobileMode);
|
|
|
|
// Gestion de la suppression des romans
|
|
const storyList = document.querySelector('.stories-list');
|
|
if (storyList) {
|
|
storyList.addEventListener('click', async (e) => {
|
|
if (e.target.matches('.delete-story')) {
|
|
const storyId = e.target.dataset.id;
|
|
if (confirmDeletion(storyId)) {
|
|
await deleteStory(storyId);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Confirmation de suppression avec informations du roman
|
|
function confirmDeletion(storyId) {
|
|
const storyCard = document.querySelector(`[data-id="${storyId}"]`).closest('.story-item');
|
|
const title = storyCard.querySelector('h2').textContent;
|
|
|
|
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
|
|
async function deleteStory(storyId) {
|
|
try {
|
|
const response = await fetch('api/delete-story.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ id: storyId })
|
|
});
|
|
|
|
if (response.ok) {
|
|
const result = await response.json();
|
|
if (result.success) {
|
|
// Animation de suppression et retrait du DOM
|
|
const storyCard = document.querySelector(`[data-id="${storyId}"]`).closest('.story-item');
|
|
storyCard.style.opacity = '0';
|
|
storyCard.style.transform = 'translateX(-100%)';
|
|
setTimeout(() => {
|
|
storyCard.remove();
|
|
showNotification('Roman supprimé avec succès');
|
|
}, 300);
|
|
} else {
|
|
throw new Error(result.error || 'Erreur lors de la suppression');
|
|
}
|
|
} else {
|
|
throw new Error('Erreur serveur');
|
|
}
|
|
} catch (error) {
|
|
showNotification(error.message, 'error');
|
|
}
|
|
}
|
|
|
|
// Système de notification
|
|
function showNotification(message, type = 'success') {
|
|
const notification = document.createElement('div');
|
|
notification.className = `notification ${type}`;
|
|
notification.textContent = message;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
// Animation d'entrée
|
|
setTimeout(() => {
|
|
notification.style.opacity = '1';
|
|
notification.style.transform = 'translateY(0)';
|
|
}, 10);
|
|
|
|
// Auto-suppression après 3 secondes
|
|
setTimeout(() => {
|
|
notification.style.opacity = '0';
|
|
notification.style.transform = 'translateY(-100%)';
|
|
setTimeout(() => notification.remove(), 300);
|
|
}, 3000);
|
|
}
|
|
|
|
// Gestion du formulaire de déconnexion
|
|
const logoutForm = document.querySelector('.logout-form');
|
|
if (logoutForm) {
|
|
logoutForm.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
confirmDialog.show({
|
|
title: 'Confirmation de déconnexion',
|
|
message: 'Voulez-vous vraiment vous déconnecter ?',
|
|
confirmText: 'Se déconnecter',
|
|
onConfirm: () => {
|
|
logoutForm.submit();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// Fonction utilitaire pour détecter les changements non sauvegardés
|
|
let hasUnsavedChanges = false;
|
|
|
|
function detectUnsavedChanges() {
|
|
const forms = document.querySelectorAll('form');
|
|
forms.forEach(form => {
|
|
const initialState = new FormData(form).toString();
|
|
|
|
form.addEventListener('change', () => {
|
|
const currentState = new FormData(form).toString();
|
|
hasUnsavedChanges = initialState !== currentState;
|
|
});
|
|
});
|
|
|
|
// Avertissement avant de quitter la page avec des changements non sauvegardés
|
|
window.addEventListener('beforeunload', (e) => {
|
|
if (hasUnsavedChanges) {
|
|
e.preventDefault();
|
|
e.returnValue = '';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Style des notifications
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
.notification {
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
padding: 1rem 2rem;
|
|
border-radius: 4px;
|
|
background: white;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
opacity: 0;
|
|
transform: translateY(-100%);
|
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.notification.success {
|
|
background-color: #4caf50;
|
|
color: white;
|
|
}
|
|
|
|
.notification.error {
|
|
background-color: #f44336;
|
|
color: white;
|
|
}
|
|
|
|
.story-item {
|
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
|
|
// Initialisation des fonctionnalités
|
|
detectUnsavedChanges();
|
|
}); |