Spaces:
Sleeping
Sleeping
// Admin JavaScript for the backend management interface | |
document.addEventListener('DOMContentLoaded', function() { | |
// Initialize theme | |
initTheme(); | |
// Setup dashboard functionality | |
setupDashboardCards(); | |
// Setup admin forms | |
setupMatiereForm(); | |
setupSousCategorieForm(); | |
setupTexteForm(); | |
// Setup content block editor | |
setupContentBlockEditor(); | |
// Setup image management | |
setupImageUploader(); | |
setupImageGallery(); | |
// Setup theme toggle | |
setupThemeToggle(); | |
}); | |
// Initialize theme based on user preference | |
function initTheme() { | |
const userPreference = localStorage.getItem('theme') || 'light'; | |
document.documentElement.setAttribute('data-theme', userPreference); | |
// Update theme icon | |
updateThemeIcon(userPreference); | |
} | |
// Setup theme toggle functionality | |
function setupThemeToggle() { | |
const themeToggle = document.getElementById('theme-toggle'); | |
if (!themeToggle) return; | |
themeToggle.addEventListener('click', function() { | |
const currentTheme = document.documentElement.getAttribute('data-theme'); | |
const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; | |
// Update theme attribute | |
document.documentElement.setAttribute('data-theme', newTheme); | |
// Save preference to localStorage | |
localStorage.setItem('theme', newTheme); | |
// Update icon | |
updateThemeIcon(newTheme); | |
// Send theme preference to server | |
saveThemePreference(newTheme); | |
}); | |
} | |
// Update the theme toggle icon based on current theme | |
function updateThemeIcon(theme) { | |
const themeToggle = document.getElementById('theme-toggle'); | |
if (!themeToggle) return; | |
// Update icon based on theme | |
if (theme === 'dark') { | |
themeToggle.innerHTML = '<i class="fas fa-sun"></i>'; | |
themeToggle.setAttribute('title', 'Activer le mode clair'); | |
} else { | |
themeToggle.innerHTML = '<i class="fas fa-moon"></i>'; | |
themeToggle.setAttribute('title', 'Activer le mode sombre'); | |
} | |
} | |
// Save theme preference to server | |
function saveThemePreference(theme) { | |
const formData = new FormData(); | |
formData.append('theme', theme); | |
fetch('/set_theme', { | |
method: 'POST', | |
body: formData | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
console.log('Theme preference saved:', data); | |
}) | |
.catch(error => { | |
console.error('Error saving theme preference:', error); | |
}); | |
} | |
// Setup dashboard cards with hover effects | |
function setupDashboardCards() { | |
const dashboardCards = document.querySelectorAll('.admin-card'); | |
dashboardCards.forEach(card => { | |
card.addEventListener('mouseenter', function() { | |
this.style.transform = 'translateY(-5px)'; | |
this.style.boxShadow = 'var(--hover-shadow)'; | |
this.style.transition = 'transform 0.3s ease, box-shadow 0.3s ease'; | |
}); | |
card.addEventListener('mouseleave', function() { | |
this.style.transform = 'translateY(0)'; | |
this.style.boxShadow = 'var(--shadow)'; | |
}); | |
}); | |
} | |
// Setup matiere form functionality | |
function setupMatiereForm() { | |
// Show edit form when edit button is clicked | |
const editButtons = document.querySelectorAll('.edit-matiere-btn'); | |
editButtons.forEach(button => { | |
button.addEventListener('click', function() { | |
const matiereId = this.getAttribute('data-id'); | |
const matiereName = this.getAttribute('data-name'); | |
const matiereColor = this.getAttribute('data-color'); | |
const editForm = document.getElementById('edit-matiere-form'); | |
if (editForm) { | |
const idInput = editForm.querySelector('input[name="matiere_id"]'); | |
const nameInput = editForm.querySelector('input[name="nom"]'); | |
const colorInput = editForm.querySelector('input[name="color_code"]'); | |
idInput.value = matiereId; | |
nameInput.value = matiereName; | |
colorInput.value = matiereColor; | |
// Show the edit form | |
document.getElementById('add-matiere-section').classList.add('d-none'); | |
document.getElementById('edit-matiere-section').classList.remove('d-none'); | |
// Scroll to edit form | |
editForm.scrollIntoView({ behavior: 'smooth' }); | |
} | |
}); | |
}); | |
// Cancel edit button | |
const cancelEditButton = document.getElementById('cancel-edit-matiere'); | |
if (cancelEditButton) { | |
cancelEditButton.addEventListener('click', function(e) { | |
e.preventDefault(); | |
document.getElementById('add-matiere-section').classList.remove('d-none'); | |
document.getElementById('edit-matiere-section').classList.add('d-none'); | |
}); | |
} | |
// Color picker preview | |
const colorPickers = document.querySelectorAll('input[type="color"]'); | |
colorPickers.forEach(picker => { | |
picker.addEventListener('input', function() { | |
// Find adjacent preview element or create one | |
let preview = this.nextElementSibling; | |
if (!preview || !preview.classList.contains('color-preview')) { | |
preview = document.createElement('span'); | |
preview.className = 'color-preview'; | |
preview.style.display = 'inline-block'; | |
preview.style.width = '24px'; | |
preview.style.height = '24px'; | |
preview.style.borderRadius = '50%'; | |
preview.style.marginLeft = '10px'; | |
this.parentNode.insertBefore(preview, this.nextSibling); | |
} | |
preview.style.backgroundColor = this.value; | |
}); | |
// Trigger once to initialize | |
const event = new Event('input'); | |
picker.dispatchEvent(event); | |
}); | |
} | |
// Setup sous categorie form functionality | |
function setupSousCategorieForm() { | |
// Show edit form when edit button is clicked | |
const editButtons = document.querySelectorAll('.edit-sous-categorie-btn'); | |
editButtons.forEach(button => { | |
button.addEventListener('click', function() { | |
const sousCategorieId = this.getAttribute('data-id'); | |
const sousCategorieName = this.getAttribute('data-name'); | |
const matiereId = this.getAttribute('data-matiere-id'); | |
const editForm = document.getElementById('edit-sous-categorie-form'); | |
if (editForm) { | |
const idInput = editForm.querySelector('input[name="sous_categorie_id"]'); | |
const nameInput = editForm.querySelector('input[name="nom"]'); | |
const matiereSelect = editForm.querySelector('select[name="matiere_id"]'); | |
idInput.value = sousCategorieId; | |
nameInput.value = sousCategorieName; | |
matiereSelect.value = matiereId; | |
// Show the edit form | |
document.getElementById('add-sous-categorie-section').classList.add('d-none'); | |
document.getElementById('edit-sous-categorie-section').classList.remove('d-none'); | |
// Scroll to edit form | |
editForm.scrollIntoView({ behavior: 'smooth' }); | |
} | |
}); | |
}); | |
// Cancel edit button | |
const cancelEditButton = document.getElementById('cancel-edit-sous-categorie'); | |
if (cancelEditButton) { | |
cancelEditButton.addEventListener('click', function(e) { | |
e.preventDefault(); | |
document.getElementById('add-sous-categorie-section').classList.remove('d-none'); | |
document.getElementById('edit-sous-categorie-section').classList.add('d-none'); | |
}); | |
} | |
// Matiere select filter | |
const matiereFilterSelect = document.getElementById('matiere-filter'); | |
if (matiereFilterSelect) { | |
matiereFilterSelect.addEventListener('change', function() { | |
const selectedMatiereId = this.value; | |
const sousCategorieRows = document.querySelectorAll('.sous-categorie-row'); | |
sousCategorieRows.forEach(row => { | |
if (selectedMatiereId === '' || row.getAttribute('data-matiere-id') === selectedMatiereId) { | |
row.style.display = ''; | |
} else { | |
row.style.display = 'none'; | |
} | |
}); | |
}); | |
} | |
} | |
// Setup texte form functionality | |
function setupTexteForm() { | |
// Matiere select change - populate sous-categories | |
const matiereSelect = document.getElementById('matiere-select'); | |
if (matiereSelect) { | |
matiereSelect.addEventListener('change', function() { | |
const matiereId = this.value; | |
const sousCategorieSelect = document.getElementById('sous-categorie-select'); | |
if (matiereId && sousCategorieSelect) { | |
// Clear current options | |
sousCategorieSelect.innerHTML = '<option value="">Sélectionnez une sous-catégorie</option>'; | |
// Fetch sous-categories for the selected matiere | |
fetch(`/get_sous_categories/${matiereId}`) | |
.then(response => response.json()) | |
.then(data => { | |
data.forEach(sousCategorie => { | |
const option = document.createElement('option'); | |
option.value = sousCategorie.id; | |
option.textContent = sousCategorie.nom; | |
sousCategorieSelect.appendChild(option); | |
}); | |
}) | |
.catch(error => { | |
console.error('Error loading sous-categories:', error); | |
}); | |
} | |
}); | |
} | |
} | |
// Setup content block editor | |
function setupContentBlockEditor() { | |
const blocksContainer = document.getElementById('blocks-container'); | |
const addBlockButton = document.getElementById('add-block-button'); | |
const saveBlocksButton = document.getElementById('save-blocks-button'); | |
if (!blocksContainer) return; | |
// Add new block | |
if (addBlockButton) { | |
addBlockButton.addEventListener('click', function() { | |
addContentBlock(); | |
}); | |
} | |
// Make blocks sortable | |
if (window.Sortable) { | |
new Sortable(blocksContainer, { | |
animation: 150, | |
handle: '.block-handle', | |
ghostClass: 'block-ghost', | |
onEnd: function() { | |
// Update order numbers | |
updateBlockOrder(); | |
} | |
}); | |
} | |
// Save blocks | |
if (saveBlocksButton) { | |
saveBlocksButton.addEventListener('click', function() { | |
saveContentBlocks(); | |
}); | |
} | |
// Add event listeners for existing blocks | |
setupExistingBlockControls(); | |
} | |
// Setup controls for existing blocks | |
function setupExistingBlockControls() { | |
// Setup delete buttons | |
const deleteButtons = document.querySelectorAll('.delete-block-btn'); | |
deleteButtons.forEach(button => { | |
button.addEventListener('click', function() { | |
if (confirm('Êtes-vous sûr de vouloir supprimer ce bloc ?')) { | |
const blockEditor = this.closest('.block-editor'); | |
blockEditor.remove(); | |
updateBlockOrder(); | |
} | |
}); | |
}); | |
// Setup image position selects | |
const positionSelects = document.querySelectorAll('.image-position-select'); | |
positionSelects.forEach(select => { | |
select.addEventListener('change', function() { | |
updateBlockImagePreview(this.closest('.block-editor')); | |
}); | |
}); | |
// Setup image selection buttons | |
const imageSelectButtons = document.querySelectorAll('.select-image-btn'); | |
imageSelectButtons.forEach(button => { | |
button.addEventListener('click', function() { | |
const blockEditor = this.closest('.block-editor'); | |
const galleryModal = document.getElementById('image-gallery-modal'); | |
if (galleryModal) { | |
// Set current block ID as data attribute for the modal | |
galleryModal.setAttribute('data-target-block', blockEditor.getAttribute('data-block-id')); | |
// Show the modal | |
const modal = new bootstrap.Modal(galleryModal); | |
modal.show(); | |
} | |
}); | |
}); | |
// Setup image remove buttons | |
const removeImageButtons = document.querySelectorAll('.remove-image-btn'); | |
removeImageButtons.forEach(button => { | |
button.addEventListener('click', function() { | |
const blockEditor = this.closest('.block-editor'); | |
const imageIdInput = blockEditor.querySelector('.block-image-id'); | |
const imagePreview = blockEditor.querySelector('.image-preview'); | |
if (imageIdInput) { | |
imageIdInput.value = ''; | |
} | |
if (imagePreview) { | |
imagePreview.src = ''; | |
imagePreview.style.display = 'none'; | |
} | |
// Hide remove button | |
this.style.display = 'none'; | |
// Show select button | |
const selectButton = blockEditor.querySelector('.select-image-btn'); | |
if (selectButton) { | |
selectButton.style.display = 'inline-block'; | |
} | |
}); | |
}); | |
} | |
// Add a new content block to the editor | |
function addContentBlock(data = null) { | |
const blocksContainer = document.getElementById('blocks-container'); | |
if (!blocksContainer) return; | |
// Generate a unique ID for the block | |
const blockId = 'block-' + Date.now(); | |
// Create block HTML | |
const blockHtml = ` | |
<div class="block-editor" data-block-id="${blockId}"> | |
<div class="block-editor-header"> | |
<div class="d-flex align-items-center"> | |
<span class="block-handle"><i class="fas fa-grip-vertical"></i></span> | |
<h3 class="block-editor-title">Bloc #${blocksContainer.children.length + 1}</h3> | |
</div> | |
<div class="block-editor-actions"> | |
<button type="button" class="btn btn-danger btn-sm delete-block-btn"> | |
<i class="fas fa-trash"></i> | |
</button> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="${blockId}-title">Titre du bloc (optionnel)</label> | |
<input type="text" class="form-control block-title" id="${blockId}-title" value="${data?.title || ''}"> | |
</div> | |
<div class="form-group"> | |
<label for="${blockId}-content">Contenu du bloc</label> | |
<textarea class="form-control block-content" id="${blockId}-content" rows="5">${data?.content || ''}</textarea> | |
</div> | |
<div class="form-group"> | |
<label>Image</label> | |
<div class="d-flex align-items-center mb-2"> | |
<button type="button" class="btn btn-primary btn-sm select-image-btn" style="${data?.image ? 'display:none;' : ''}"> | |
<i class="fas fa-image"></i> Sélectionner une image | |
</button> | |
<button type="button" class="btn btn-warning btn-sm remove-image-btn ml-2" style="${data?.image ? '' : 'display:none;'}"> | |
<i class="fas fa-times"></i> Retirer l'image | |
</button> | |
</div> | |
<input type="hidden" class="block-image-id" value="${data?.image?.id || ''}"> | |
<img src="${data?.image?.src || ''}" alt="Preview" class="image-preview mb-2" style="${data?.image ? '' : 'display:none;'}"> | |
<div class="form-group"> | |
<label for="${blockId}-image-position">Position de l'image</label> | |
<select class="form-control image-position-select" id="${blockId}-image-position"> | |
<option value="left" ${data?.image_position === 'left' ? 'selected' : ''}>Gauche</option> | |
<option value="right" ${data?.image_position === 'right' ? 'selected' : ''}>Droite</option> | |
<option value="top" ${data?.image_position === 'top' ? 'selected' : ''}>Haut</option> | |
</select> | |
</div> | |
</div> | |
</div> | |
`; | |
// Add the block to the container | |
blocksContainer.insertAdjacentHTML('beforeend', blockHtml); | |
// Setup event listeners for the new block | |
const newBlock = blocksContainer.lastElementChild; | |
// Delete button | |
const deleteButton = newBlock.querySelector('.delete-block-btn'); | |
if (deleteButton) { | |
deleteButton.addEventListener('click', function() { | |
if (confirm('Êtes-vous sûr de vouloir supprimer ce bloc ?')) { | |
newBlock.remove(); | |
updateBlockOrder(); | |
} | |
}); | |
} | |
// Image position select | |
const positionSelect = newBlock.querySelector('.image-position-select'); | |
if (positionSelect) { | |
positionSelect.addEventListener('change', function() { | |
updateBlockImagePreview(newBlock); | |
}); | |
} | |
// Image selection button | |
const imageSelectButton = newBlock.querySelector('.select-image-btn'); | |
if (imageSelectButton) { | |
imageSelectButton.addEventListener('click', function() { | |
const galleryModal = document.getElementById('image-gallery-modal'); | |
if (galleryModal) { | |
// Set current block ID as data attribute for the modal | |
galleryModal.setAttribute('data-target-block', blockId); | |
// Show the modal | |
const modal = new bootstrap.Modal(galleryModal); | |
modal.show(); | |
} | |
}); | |
} | |
// Image remove button | |
const removeImageButton = newBlock.querySelector('.remove-image-btn'); | |
if (removeImageButton) { | |
removeImageButton.addEventListener('click', function() { | |
const imageIdInput = newBlock.querySelector('.block-image-id'); | |
const imagePreview = newBlock.querySelector('.image-preview'); | |
if (imageIdInput) { | |
imageIdInput.value = ''; | |
} | |
if (imagePreview) { | |
imagePreview.src = ''; | |
imagePreview.style.display = 'none'; | |
} | |
// Hide remove button | |
removeImageButton.style.display = 'none'; | |
// Show select button | |
if (imageSelectButton) { | |
imageSelectButton.style.display = 'inline-block'; | |
} | |
}); | |
} | |
// Scroll to the new block | |
newBlock.scrollIntoView({ behavior: 'smooth' }); | |
} | |
// Update block order numbers in the UI | |
function updateBlockOrder() { | |
const blocks = document.querySelectorAll('.block-editor'); | |
blocks.forEach((block, index) => { | |
const titleEl = block.querySelector('.block-editor-title'); | |
if (titleEl) { | |
titleEl.textContent = `Bloc #${index + 1}`; | |
} | |
}); | |
} | |
// Update image preview based on position | |
function updateBlockImagePreview(blockEditor) { | |
// This function would apply CSS classes to show how the image position | |
// will look in the frontend | |
const positionSelect = blockEditor.querySelector('.image-position-select'); | |
const imagePreview = blockEditor.querySelector('.image-preview'); | |
if (!positionSelect || !imagePreview || imagePreview.style.display === 'none') { | |
return; | |
} | |
const position = positionSelect.value; | |
// Remove existing position classes | |
imagePreview.classList.remove('position-left', 'position-right', 'position-top'); | |
// Add the selected position class | |
imagePreview.classList.add(`position-${position}`); | |
// Apply some simple styling to demonstrate the position | |
switch (position) { | |
case 'left': | |
imagePreview.style.float = 'left'; | |
imagePreview.style.marginRight = '15px'; | |
imagePreview.style.marginBottom = '10px'; | |
imagePreview.style.width = '30%'; | |
break; | |
case 'right': | |
imagePreview.style.float = 'right'; | |
imagePreview.style.marginLeft = '15px'; | |
imagePreview.style.marginBottom = '10px'; | |
imagePreview.style.width = '30%'; | |
break; | |
case 'top': | |
imagePreview.style.float = 'none'; | |
imagePreview.style.marginRight = '0'; | |
imagePreview.style.marginLeft = '0'; | |
imagePreview.style.marginBottom = '15px'; | |
imagePreview.style.width = '100%'; | |
break; | |
} | |
} | |
// Save content blocks | |
function saveContentBlocks() { | |
const blocksContainer = document.getElementById('blocks-container'); | |
const blocksDataInput = document.getElementById('blocks-data'); | |
if (!blocksContainer || !blocksDataInput) return; | |
const blocks = blocksContainer.querySelectorAll('.block-editor'); | |
const blocksData = []; | |
blocks.forEach((block, index) => { | |
const blockId = block.getAttribute('data-block-id'); | |
const title = block.querySelector('.block-title').value; | |
const content = block.querySelector('.block-content').value; | |
const imageId = block.querySelector('.block-image-id').value; | |
const imagePosition = block.querySelector('.image-position-select').value; | |
blocksData.push({ | |
id: blockId, | |
title: title, | |
content: content, | |
image_id: imageId, | |
image_position: imagePosition, | |
order: index | |
}); | |
}); | |
// Set the blocks data as JSON in the hidden input | |
blocksDataInput.value = JSON.stringify(blocksData); | |
// Submit the form | |
const form = document.getElementById('blocks-form'); | |
if (form) { | |
form.submit(); | |
} | |
} | |
// Setup image uploader | |
function setupImageUploader() { | |
const imageUploadForm = document.getElementById('image-upload-form'); | |
const imageFileInput = document.getElementById('image-file'); | |
const imagePreview = document.getElementById('upload-image-preview'); | |
if (imageFileInput && imagePreview) { | |
imageFileInput.addEventListener('change', function() { | |
if (this.files && this.files[0]) { | |
const reader = new FileReader(); | |
reader.onload = function(e) { | |
imagePreview.src = e.target.result; | |
imagePreview.style.display = 'block'; | |
}; | |
reader.readAsDataURL(this.files[0]); | |
} | |
}); | |
} | |
if (imageUploadForm) { | |
imageUploadForm.addEventListener('submit', function(e) { | |
const fileInput = this.querySelector('#image-file'); | |
if (!fileInput.files || fileInput.files.length === 0) { | |
e.preventDefault(); | |
alert('Veuillez sélectionner une image.'); | |
} | |
}); | |
} | |
} | |
// Setup image gallery | |
function setupImageGallery() { | |
// Handle image selection from gallery | |
const galleryItems = document.querySelectorAll('.gallery-item'); | |
galleryItems.forEach(item => { | |
item.addEventListener('click', function() { | |
const imageId = this.getAttribute('data-image-id'); | |
const imageSrc = this.querySelector('img').src; | |
const galleryModal = document.getElementById('image-gallery-modal'); | |
if (galleryModal) { | |
const targetBlockId = galleryModal.getAttribute('data-target-block'); | |
const blockEditor = document.querySelector(`.block-editor[data-block-id="${targetBlockId}"]`); | |
if (blockEditor) { | |
// Update the image ID input | |
const imageIdInput = blockEditor.querySelector('.block-image-id'); | |
if (imageIdInput) { | |
imageIdInput.value = imageId; | |
} | |
// Update the image preview | |
const imagePreview = blockEditor.querySelector('.image-preview'); | |
if (imagePreview) { | |
imagePreview.src = imageSrc; | |
imagePreview.style.display = 'block'; | |
} | |
// Hide select button and show remove button | |
const selectButton = blockEditor.querySelector('.select-image-btn'); | |
const removeButton = blockEditor.querySelector('.remove-image-btn'); | |
if (selectButton) { | |
selectButton.style.display = 'none'; | |
} | |
if (removeButton) { | |
removeButton.style.display = 'inline-block'; | |
} | |
// Update image preview position | |
updateBlockImagePreview(blockEditor); | |
} | |
// Close the modal | |
const modal = bootstrap.Modal.getInstance(galleryModal); | |
if (modal) { | |
modal.hide(); | |
} | |
} | |
}); | |
}); | |
} | |