Файловый менеджер - Редактировать - /home/gqdcvggs/imators.systems/waterloo/shop-add/index.php
Назад
<?php include 'db.php'; $errors = []; $success = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (isset($_POST['action'])) { $action = $_POST['action']; if ($action === 'save') { $shopId = isset($_POST['id']) ? intval($_POST['id']) : 0; $title = $_POST['title'] ?? ''; $description = $_POST['description'] ?? ''; $phone = $_POST['phone'] ?? ''; $uber = $_POST['uber'] ?? ''; $hourData = []; if (isset($_POST['schedule']) && is_array($_POST['schedule'])) { foreach ($_POST['schedule'] as $day => $slots) { foreach ($slots as $slot) { if (!empty($slot['open']) && !empty($slot['close'])) { if (!isset($hourData[$day])) { $hourData[$day] = []; } $hourData[$day][] = ['open' => $slot['open'], 'close' => $slot['close']]; } } } } $openDays = array_keys($hourData); $daysStr = implode(',', $openDays); $firstSlot = ''; foreach ($hourData as $day => $slots) { if (!empty($slots[0]['open']) && !empty($slots[0]['close'])) { $firstSlot = $slots[0]['open'] . ' à ' . $slots[0]['close']; break; } } $uploadDir = "uploads/"; $baseUrl = "https://vertchasseur.com/shop-add/"; $imageStr = ''; if (!empty($_FILES['images']['name'][0])) { if (!file_exists($uploadDir)) { mkdir($uploadDir, 0777, true); } $uploadedImages = []; foreach($_FILES['images']['tmp_name'] as $key => $tmp_name) { if (empty($tmp_name)) continue; $fileName = uniqid() . '_' . $_FILES['images']['name'][$key]; $filePath = $uploadDir . $fileName; $dbImagePath = $baseUrl . $uploadDir . $fileName; if (move_uploaded_file($tmp_name, $filePath)) { $uploadedImages[] = $dbImagePath; } } if ($shopId > 0) { $query = "SELECT url_image FROM shop WHERE id = ?"; $stmt = mysqli_prepare($conn, $query); mysqli_stmt_bind_param($stmt, "i", $shopId); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if ($shop = mysqli_fetch_assoc($result)) { $existing = $shop['url_image']; if (!empty($existing)) { $existing = trim($existing, '()'); if (!empty($existing)) { $existingImages = explode(',', $existing); $uploadedImages = array_merge($existingImages, $uploadedImages); } } } } if (!empty($uploadedImages)) { $imageStr = '(' . implode(',', $uploadedImages) . ')'; } } if ($shopId > 0) { if (!empty($imageStr)) { $query = "UPDATE shop SET title = ?, description = ?, number_phone = ?, uber_eats_link = ?, hour_open = ?, day_open = ?, url_image = ? WHERE id = ?"; $stmt = mysqli_prepare($conn, $query); mysqli_stmt_bind_param($stmt, "sssssssi", $title, $description, $phone, $uber, $firstSlot, $daysStr, $imageStr, $shopId); } else { $query = "UPDATE shop SET title = ?, description = ?, number_phone = ?, uber_eats_link = ?, hour_open = ?, day_open = ? WHERE id = ?"; $stmt = mysqli_prepare($conn, $query); mysqli_stmt_bind_param($stmt, "ssssssi", $title, $description, $phone, $uber, $firstSlot, $daysStr, $shopId); } if (mysqli_stmt_execute($stmt)) { $success = "Magasin mis à jour avec succès"; } else { $errors[] = "Erreur: " . mysqli_error($conn); } } else { $query = "INSERT INTO shop (title, description, number_phone, uber_eats_link, hour_open, day_open, url_image) VALUES (?, ?, ?, ?, ?, ?, ?)"; $stmt = mysqli_prepare($conn, $query); mysqli_stmt_bind_param($stmt, "sssssss", $title, $description, $phone, $uber, $firstSlot, $daysStr, $imageStr); if (mysqli_stmt_execute($stmt)) { $shopId = mysqli_insert_id($conn); $query = "UPDATE shop SET active = 1 WHERE id = ?"; $stmt = mysqli_prepare($conn, $query); mysqli_stmt_bind_param($stmt, "i", $shopId); mysqli_stmt_execute($stmt); $success = "Magasin ajouté avec succès"; } else { $errors[] = "Erreur: " . mysqli_error($conn); } } } if ($action === 'toggle') { $id = isset($_POST['id']) ? intval($_POST['id']) : 0; $active = isset($_POST['active']) ? intval($_POST['active']) : 0; $newStatus = $active ? 0 : 1; $query = "UPDATE shop SET active = ? WHERE id = ?"; $stmt = mysqli_prepare($conn, $query); mysqli_stmt_bind_param($stmt, "ii", $newStatus, $id); if (mysqli_stmt_execute($stmt)) { echo json_encode(['success' => true]); exit; } else { echo json_encode(['error' => mysqli_error($conn)]); exit; } } if ($action === 'delete') { $id = isset($_POST['id']) ? intval($_POST['id']) : 0; $query = "DELETE FROM shop WHERE id = ?"; $stmt = mysqli_prepare($conn, $query); mysqli_stmt_bind_param($stmt, "i", $id); if (mysqli_stmt_execute($stmt)) { echo json_encode(['success' => true]); exit; } else { echo json_encode(['error' => mysqli_error($conn)]); exit; } } } } $query = "SELECT * FROM shop ORDER BY price_smilemore DESC, title ASC"; $result = mysqli_query($conn, $query); $shops = []; if ($result) { while ($row = mysqli_fetch_assoc($result)) { $shops[] = $row; } } ?> <!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Gestion des magasins | Imators</title> <script src="https://cdn.tailwindcss.com"></script> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> <style> body { font-family: 'Poppins', sans-serif; background-color: #f1f5f9; } .dark body { background-color: #0f172a; } .btn-primary { background-color: #06b6d4; color: white; } .btn-primary:hover { background-color: #0891b2; } .switch { position: relative; display: inline-block; width: 50px; height: 26px; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #cbd5e1; transition: .3s; border-radius: 34px; } .slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 4px; bottom: 4px; background-color: white; transition: .3s; border-radius: 50%; } input:checked + .slider { background-color: #06b6d4; } input:checked + .slider:before { transform: translateX(24px); } .shop-card { background-color: white; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); transition: transform 0.2s, box-shadow 0.2s; } .shop-card:hover { transform: translateY(-4px); box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); } .dark .shop-card { background-color: #1e293b; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1); } .modal-content { background-color: white; border-radius: 20px; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); width: 95%; max-width: 920px; max-height: 90vh; overflow-y: auto; } .dark .modal-content { background-color: #1e293b; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.2); } .form-input { border-radius: 12px; padding: 10px 16px; transition: all 0.2s; } .form-input:focus { border-color: #06b6d4; box-shadow: 0 0 0 3px rgba(6, 182, 212, 0.15); } .tab-active { color: #06b6d4; border-bottom: 2px solid #06b6d4; } </style> </head> <body class="text-slate-800 dark:text-slate-200"> <div class="min-h-screen p-4 md:p-6 lg:p-8"> <div class="max-w-7xl mx-auto"> <header class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-10 gap-4"> <div> <h1 class="text-3xl font-bold">Gestion des magasins</h1> <p class="text-slate-500 dark:text-slate-400 mt-1">Gère tes points de ventes en toute simplicité</p> </div> <div class="flex items-center gap-4"> <button id="theme-toggle" class="p-2.5 text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-full transition"> <i class="fas fa-moon dark:hidden text-lg"></i> <i class="fas fa-sun hidden dark:block text-lg"></i> </button> <button id="add-shop-btn" class="btn-primary px-6 py-3 rounded-xl font-medium flex items-center gap-2 shadow-lg shadow-cyan-500/20"> <i class="fas fa-plus"></i>Ajouter un magasin </button> </div> </header> <?php if (!empty($errors)): ?> <div class="bg-red-50 dark:bg-red-900/30 text-red-700 dark:text-red-300 p-4 rounded-xl mb-8 border-l-4 border-red-500 flex items-start animate-fadeIn"> <i class="fas fa-exclamation-circle text-red-500 mr-3 mt-0.5 text-xl"></i> <div> <p class="font-medium">Des erreurs sont survenues :</p> <ul class="list-disc pl-5 mt-1 text-sm"> <?php foreach ($errors as $error): ?> <li><?= htmlspecialchars($error) ?></li> <?php endforeach; ?> </ul> </div> </div> <?php endif; ?> <?php if ($success): ?> <div class="bg-emerald-50 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300 p-4 rounded-xl mb-8 border-l-4 border-emerald-500 flex items-center animate-fadeIn"> <i class="fas fa-check-circle text-emerald-500 mr-3 text-xl"></i> <p><?= htmlspecialchars($success) ?></p> </div> <?php endif; ?> <?php if (empty($shops)): ?> <div class="shop-card py-20 text-center"> <div class="max-w-md mx-auto"> <div class="bg-cyan-50 dark:bg-cyan-900/30 h-24 w-24 rounded-full flex items-center justify-center mx-auto mb-6"> <i class="fas fa-store text-cyan-500 text-4xl"></i> </div> <h2 class="text-2xl font-semibold mb-3">Aucun magasin</h2> <p class="text-slate-500 dark:text-slate-400 mb-8">Tu n'as pas encore ajouté de magasin. Commence par créer ton premier point de vente.</p> <button id="empty-add-btn" class="btn-primary px-6 py-3 rounded-xl font-medium inline-flex items-center gap-2 shadow-lg shadow-cyan-500/20"> <i class="fas fa-plus"></i>Ajouter un magasin </button> </div> </div> <?php else: ?> <div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> <?php foreach ($shops as $shop): ?> <?php $mainImage = ''; if (!empty($shop['url_image'])) { $images = trim($shop['url_image'], '()'); if (!empty($images)) { $imageArray = explode(',', $images); $mainImage = trim($imageArray[0]); } } ?> <div class="shop-card"> <div class="flex flex-col sm:flex-row"> <div class="w-full sm:w-40 h-40 bg-slate-100 dark:bg-slate-800 flex items-center justify-center overflow-hidden"> <?php if (!empty($mainImage)): ?> <img src="<?= htmlspecialchars($mainImage) ?>" class="w-full h-full object-cover"> <?php else: ?> <i class="fas fa-store text-slate-400 text-4xl"></i> <?php endif; ?> </div> <div class="flex-1 p-5"> <div class="flex justify-between"> <div> <h3 class="font-semibold text-xl"><?= htmlspecialchars($shop['title']) ?></h3> <?php if (isset($shop['price_smilemore']) && $shop['price_smilemore'] == 1): ?> <span class="text-xs text-amber-600 dark:text-amber-400 font-medium flex items-center mt-1"> <i class="fas fa-award mr-1"></i> Le plus fréquenté </span> <?php endif; ?> </div> <label class="switch" title="<?= isset($shop['active']) && $shop['active'] == 1 ? 'Actif' : 'Masqué' ?>"> <input type="checkbox" class="toggle-switch" data-id="<?= $shop['id'] ?>" <?= isset($shop['active']) && $shop['active'] == 1 ? 'checked' : '' ?>> <span class="slider"></span> </label> </div> <div class="mt-4 space-y-2"> <div class="flex items-center text-sm"> <i class="fas fa-phone text-slate-400 w-5"></i> <span><?= htmlspecialchars($shop['number_phone']) ?></span> </div> <div class="flex items-center text-sm"> <i class="fas fa-clock text-slate-400 w-5"></i> <span><?= htmlspecialchars($shop['hour_open']) ?></span> </div> <div class="flex items-start text-sm"> <i class="fas fa-calendar-day text-slate-400 w-5 mt-0.5"></i> <div> <?php $days = explode(',', $shop['day_open']); $dayLabels = [ 'Monday' => 'Lundi', 'Tuesday' => 'Mardi', 'Wednesday' => 'Mercredi', 'Thursday' => 'Jeudi', 'Friday' => 'Vendredi', 'Saturday' => 'Samedi', 'Sunday' => 'Dimanche' ]; $daysFr = []; foreach ($days as $day) { if (isset($dayLabels[$day])) { $daysFr[] = $dayLabels[$day]; } } echo !empty($daysFr) ? implode(', ', $daysFr) : 'Aucun jour'; ?> </div> </div> </div> <div class="mt-6 pt-4 border-t border-slate-100 dark:border-slate-700 flex justify-end gap-3"> <button data-shop='<?= json_encode($shop) ?>' class="edit-btn px-4 py-2 text-sm font-medium text-cyan-600 hover:text-cyan-700 dark:text-cyan-400 dark:hover:text-cyan-300 flex items-center gap-1.5 hover:bg-cyan-50 dark:hover:bg-cyan-900/20 rounded-lg transition"> <i class="fas fa-edit"></i>Modifier </button> <button data-id="<?= $shop['id'] ?>" class="delete-btn px-4 py-2 text-sm font-medium text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 flex items-center gap-1.5 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg transition"> <i class="fas fa-trash"></i>Supprimer </button> </div> </div> </div> </div> <?php endforeach; ?> </div> <?php endif; ?> </div> </div> <div id="shop-modal" class="fixed inset-0 bg-black/70 dark:bg-slate-900/80 flex items-center justify-center z-50 hidden"> <div class="modal-content"> <div class="flex items-center justify-between bg-gradient-to-r from-cyan-600 to-sky-600 dark:from-cyan-700 dark:to-sky-700 text-white p-5 sticky top-0 z-10"> <h3 id="modal-title" class="font-semibold text-xl">Ajouter un magasin</h3> <button id="close-modal" class="text-2xl leading-none hover:text-cyan-200 transition">×</button> </div> <div class="p-6"> <div class="flex border-b border-slate-200 dark:border-slate-700 mb-6"> <button class="tab-btn tab-active py-3 px-4 font-medium" data-tab="info">Informations</button> <button class="tab-btn py-3 px-4 font-medium" data-tab="hours">Horaires</button> <button class="tab-btn py-3 px-4 font-medium" data-tab="images">Images</button> </div> <form id="edit-form" action="" method="POST" enctype="multipart/form-data"> <input type="hidden" name="action" value="save"> <input type="hidden" name="id" id="shop-id"> <div id="tab-info" class="tab-content"> <div class="bg-slate-50 dark:bg-slate-800/50 p-6 rounded-2xl mb-6"> <h4 class="font-medium text-lg mb-4">Informations générales</h4> <div class="space-y-5"> <div> <label for="shop-title" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Nom du magasin</label> <input type="text" name="title" id="shop-title" required placeholder="Nom de ton magasin" class="form-input w-full border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 outline-none focus:ring-2 focus:ring-cyan-500 focus:border-cyan-500"> </div> <div> <label for="shop-description" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Description</label> <textarea name="description" id="shop-description" required placeholder="Décris ton magasin" class="form-input w-full border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 outline-none focus:ring-2 focus:ring-cyan-500 focus:border-cyan-500" rows="4"></textarea> </div> </div> </div> <div class="bg-slate-50 dark:bg-slate-800/50 p-6 rounded-2xl"> <h4 class="font-medium text-lg mb-4">Contact</h4> <div class="grid grid-cols-1 sm:grid-cols-2 gap-5"> <div> <label for="shop-phone" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Téléphone</label> <div class="relative"> <div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none"> <i class="fas fa-phone text-slate-400"></i> </div> <input type="tel" name="phone" id="shop-phone" required placeholder="01 23 45 67 89" class="form-input w-full pl-11 border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 outline-none focus:ring-2 focus:ring-cyan-500 focus:border-cyan-500"> </div> </div> <div> <label for="shop-uber" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Lien Uber Eats</label> <div class="relative"> <div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none"> <i class="fas fa-link text-slate-400"></i> </div> <input type="url" name="uber" id="shop-uber" placeholder="https://..." class="form-input w-full pl-11 border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 outline-none focus:ring-2 focus:ring-cyan-500 focus:border-cyan-500"> </div> </div> </div> </div> <div class="flex justify-between items-center mt-8"> <button type="button" id="cancel-btn" class="px-5 py-3 border border-slate-300 dark:border-slate-600 text-slate-700 dark:text-slate-300 font-medium rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800 transition"> Annuler </button> <button type="button" id="next-hours-btn" class="btn-primary px-5 py-3 rounded-xl font-medium"> Suivant <i class="fas fa-arrow-right ml-1"></i> </button> </div> </div> <div id="tab-hours" class="tab-content hidden"> <div class="bg-slate-50 dark:bg-slate-800/50 p-6 rounded-2xl"> <h4 class="font-medium text-lg mb-5">Horaires d'ouverture</h4> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <?php $days = [ 'Monday' => 'Lundi', 'Tuesday' => 'Mardi', 'Wednesday' => 'Mercredi', 'Thursday' => 'Jeudi', 'Friday' => 'Vendredi', 'Saturday' => 'Samedi', 'Sunday' => 'Dimanche' ]; foreach ($days as $value => $label): ?> <div class="border border-slate-200 dark:border-slate-700 rounded-xl p-4 hover:border-cyan-300 dark:hover:border-cyan-700 transition-colors"> <div class="flex justify-between items-center mb-3"> <span class="font-medium"><?= $label ?></span> <button type="button" data-day="<?= $value ?>" class="add-slot-btn text-sm text-cyan-600 dark:text-cyan-400 hover:text-cyan-800 dark:hover:text-cyan-300 px-2 py-1 rounded flex items-center gap-1 hover:bg-cyan-50 dark:hover:bg-cyan-900/20 transition"> <i class="fas fa-plus"></i>Ajouter </button> </div> <div id="slots-<?= $value ?>" class="day-slots space-y-3"></div> </div> <?php endforeach; ?> </div> </div> <div class="flex justify-between items-center mt-8"> <button type="button" id="back-info-btn" class="px-5 py-3 border border-slate-300 dark:border-slate-600 text-slate-700 dark:text-slate-300 font-medium rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800 transition"> <i class="fas fa-arrow-left mr-1"></i> Retour </button> <button type="button" id="next-images-btn" class="btn-primary px-5 py-3 rounded-xl font-medium"> Suivant <i class="fas fa-arrow-right ml-1"></i> </button> </div> </div> <div id="tab-images" class="tab-content hidden"> <div class="bg-slate-50 dark:bg-slate-800/50 p-6 rounded-2xl"> <h4 class="font-medium text-lg mb-4">Images du magasin</h4> <div class="border-2 border-dashed border-slate-300 dark:border-slate-600 rounded-xl p-8 text-center hover:border-cyan-400 dark:hover:border-cyan-600 transition-colors"> <input type="file" id="shop-images" name="images[]" multiple accept="image/*" class="hidden"> <label for="shop-images" class="cursor-pointer block"> <i class="fas fa-cloud-upload-alt text-4xl text-cyan-500 dark:text-cyan-400 mb-4"></i> <p class="text-slate-600 dark:text-slate-400"> Dépose tes images ici ou <span class="text-cyan-600 dark:text-cyan-400 font-medium">parcours</span> </p> <p class="text-xs text-slate-500 dark:text-slate-500 mt-2"> PNG, JPG ou JPEG jusqu'à 5MB </p> </label> </div> <div id="image-preview" class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4 mt-6"></div> </div> <div class="flex justify-between items-center mt-8"> <button type="button" id="back-hours-btn" class="px-5 py-3 border border-slate-300 dark:border-slate-600 text-slate-700 dark:text-slate-300 font-medium rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800 transition"> <i class="fas fa-arrow-left mr-1"></i> Retour </button> <button type="submit" class="btn-primary px-5 py-3 rounded-xl font-medium"> <i class="fas fa-save mr-1"></i> Enregistrer </button> </div> </div> </form> </div> </div> </div> <script> if (window.matchMedia('(prefers-color-scheme: dark)').matches) { document.documentElement.classList.add('dark'); } function addTimeSlot(day, container, openTime = '', closeTime = '') { const index = container.querySelectorAll('.time-slot').length; const slot = document.createElement('div'); slot.className = 'time-slot flex items-center gap-3'; slot.innerHTML = ` <div class="flex-1 flex gap-3 items-center"> <input type="time" name="schedule[${day}][${index}][open]" value="${openTime}" class="form-input flex-1 border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 outline-none focus:ring-2 focus:ring-cyan-500 focus:border-cyan-500"> <span class="text-slate-500 dark:text-slate-400">à</span> <input type="time" name="schedule[${day}][${index}][close]" value="${closeTime}" class="form-input flex-1 border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 outline-none focus:ring-2 focus:ring-cyan-500 focus:border-cyan-500"> </div> <button type="button" class="remove-slot-btn p-2 text-red-500 dark:text-red-400 hover:text-red-700 dark:hover:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg transition"> <i class="fas fa-times"></i> </button> `; container.appendChild(slot); slot.querySelector('.remove-slot-btn').addEventListener('click', function() { removeTimeSlot(this); }); } function removeTimeSlot(button) { const parent = button.closest('.day-slots'); if (parent.querySelectorAll('.time-slot').length > 1) { button.closest('.time-slot').remove(); } else { const inputs = button.closest('.time-slot').querySelectorAll('input[type="time"]'); inputs.forEach(input => input.value = ''); } } function setupSchedule(shop) { const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; const dayOpen = shop.day_open ? shop.day_open.split(',') : []; const hourOpen = shop.hour_open ? shop.hour_open.split(' à ') : ['', '']; days.forEach(day => { const container = document.getElementById(`slots-${day}`); container.innerHTML = ''; if (dayOpen.includes(day)) { addTimeSlot(day, container, hourOpen[0], hourOpen[1]); } else { addTimeSlot(day, container); } }); } function resetSchedule() { const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; days.forEach(day => { const container = document.getElementById(`slots-${day}`); container.innerHTML = ''; addTimeSlot(day, container); }); } function switchTab(tabId) { document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.add('hidden'); }); document.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.remove('tab-active'); }); document.getElementById(`tab-${tabId}`).classList.remove('hidden'); document.querySelector(`.tab-btn[data-tab="${tabId}"]`).classList.add('tab-active'); } function openAddModal() { document.getElementById('edit-form').reset(); document.getElementById('shop-id').value = ''; document.getElementById('image-preview').innerHTML = ''; resetSchedule(); switchTab('info'); document.getElementById('modal-title').textContent = 'Ajouter un magasin'; document.getElementById('shop-modal').classList.remove('hidden'); document.body.style.overflow = 'hidden'; } function openEditModal(shop) { document.getElementById('edit-form').reset(); document.getElementById('shop-id').value = shop.id; document.getElementById('shop-title').value = shop.title; document.getElementById('shop-description').value = shop.description; document.getElementById('shop-phone').value = shop.number_phone; document.getElementById('shop-uber').value = shop.uber_eats_link; setupSchedule(shop); const preview = document.getElementById('image-preview'); preview.innerHTML = ''; if (shop.url_image) { const images = shop.url_image.replace('(', '').replace(')', '').split(','); for (let img of images) { if (img.trim()) { preview.innerHTML += ` <div class="relative aspect-square rounded-xl overflow-hidden bg-slate-100 dark:bg-slate-700 group"> <img src="${img.trim()}" class="w-full h-full object-cover"> <div class="absolute inset-0 bg-black/60 opacity-0 group-hover:opacity-100 flex items-center justify-center transition"> <button type="button" class="delete-image text-white hover:text-red-400 p-2"> <i class="fas fa-trash"></i> </button> </div> </div> `; } } document.querySelectorAll('.delete-image').forEach(btn => { btn.addEventListener('click', function() { this.closest('.relative').remove(); }); }); } switchTab('info'); document.getElementById('modal-title').textContent = 'Modifier un magasin'; document.getElementById('shop-modal').classList.remove('hidden'); document.body.style.overflow = 'hidden'; } function closeModal() { document.getElementById('shop-modal').classList.add('hidden'); document.body.style.overflow = ''; } function toggleShop(id, active) { fetch('', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `action=toggle&id=${id}&active=${active}` }) .then(response => response.json()) .then(data => { if (data.success) { showNotification(`Statut du magasin modifié avec succès`, 'success'); setTimeout(() => { window.location.reload(); }, 1500); } else { showNotification(`Erreur lors de la modification du statut`, 'error'); } }) .catch(error => { showNotification(`Erreur: ${error.message}`, 'error'); }); } function deleteShop(id) { const confirmModal = document.createElement('div'); confirmModal.className = 'fixed inset-0 bg-black/70 dark:bg-slate-900/80 flex items-center justify-center z-50'; confirmModal.innerHTML = ` <div class="bg-white dark:bg-slate-800 rounded-2xl shadow-2xl max-w-md w-full p-6 scale-100 transition-transform"> <div class="text-center mb-6"> <div class="inline-flex items-center justify-center h-16 w-16 rounded-full bg-red-100 dark:bg-red-900/30 text-red-500 mb-4"> <i class="fas fa-exclamation-triangle text-2xl"></i> </div> <h3 class="text-xl font-semibold mb-2">Confirmation de suppression</h3> <p class="text-slate-600 dark:text-slate-300">Es-tu sûr de vouloir supprimer ce magasin ? Cette action est irréversible.</p> </div> <div class="flex justify-center gap-3"> <button class="cancel-delete px-5 py-3 border border-slate-300 dark:border-slate-600 text-slate-700 dark:text-slate-300 font-medium rounded-xl hover:bg-slate-50 dark:hover:bg-slate-700 transition"> Annuler </button> <button class="confirm-delete px-5 py-3 bg-red-600 hover:bg-red-700 text-white font-medium rounded-xl transition"> Supprimer </button> </div> </div> `; document.body.appendChild(confirmModal); document.body.style.overflow = 'hidden'; setTimeout(() => { confirmModal.querySelector('div > div').classList.add('scale-105'); setTimeout(() => { confirmModal.querySelector('div > div').classList.remove('scale-105'); }, 200); }, 10); confirmModal.querySelector('.cancel-delete').addEventListener('click', () => { confirmModal.querySelector('div > div').classList.add('scale-95'); setTimeout(() => { document.body.removeChild(confirmModal); document.body.style.overflow = ''; }, 200); }); confirmModal.querySelector('.confirm-delete').addEventListener('click', () => { fetch('', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `action=delete&id=${id}` }) .then(response => response.json()) .then(data => { document.body.removeChild(confirmModal); document.body.style.overflow = ''; if (data.success) { showNotification(`Magasin supprimé avec succès`, 'success'); setTimeout(() => { window.location.reload(); }, 1500); } else { showNotification(`Erreur: ${data.error}`, 'error'); } }) .catch(error => { document.body.removeChild(confirmModal); document.body.style.overflow = ''; showNotification(`Erreur: ${error.message}`, 'error'); }); }); } function showNotification(message, type = 'info') { const notification = document.createElement('div'); let bgColor, iconClass, textColor, borderColor, iconBg; switch(type) { case 'success': bgColor = 'bg-emerald-50 dark:bg-emerald-900/20'; textColor = 'text-emerald-700 dark:text-emerald-300'; borderColor = 'border-emerald-500'; iconClass = 'fas fa-check'; iconBg = 'bg-emerald-500'; break; case 'error': bgColor = 'bg-red-50 dark:bg-red-900/20'; textColor = 'text-red-700 dark:text-red-300'; borderColor = 'border-red-500'; iconClass = 'fas fa-exclamation'; iconBg = 'bg-red-500'; break; default: bgColor = 'bg-blue-50 dark:bg-blue-900/20'; textColor = 'text-blue-700 dark:text-blue-300'; borderColor = 'border-blue-500'; iconClass = 'fas fa-info'; iconBg = 'bg-blue-500'; } notification.className = `fixed top-6 right-6 flex items-center p-4 rounded-xl shadow-xl ${bgColor} ${textColor} max-w-sm z-50 transform transition-all duration-500 translate-y-[-20px] opacity-0`; notification.innerHTML = ` <div class="shrink-0 w-10 h-10 ${iconBg} text-white rounded-full flex items-center justify-center mr-4"> <i class="${iconClass}"></i> </div> <p class="font-medium">${message}</p> `; document.body.appendChild(notification); setTimeout(() => { notification.classList.remove('translate-y-[-20px]', 'opacity-0'); }, 10); setTimeout(() => { notification.classList.add('translate-y-[-20px]', 'opacity-0'); setTimeout(() => { if (document.body.contains(notification)) { document.body.removeChild(notification); } }, 500); }, 4000); } document.addEventListener('DOMContentLoaded', function() { document.getElementById('theme-toggle').addEventListener('click', function() { document.documentElement.classList.toggle('dark'); }); document.querySelectorAll('.day-slots').forEach(container => { const day = container.id.replace('slots-', ''); addTimeSlot(day, container); }); document.getElementById('add-shop-btn').addEventListener('click', openAddModal); if (document.getElementById('empty-add-btn')) { document.getElementById('empty-add-btn').addEventListener('click', openAddModal); } document.getElementById('close-modal').addEventListener('click', closeModal); document.getElementById('cancel-btn').addEventListener('click', closeModal); document.querySelectorAll('.tab-btn').forEach(tab => { tab.addEventListener('click', function() { switchTab(this.dataset.tab); }); }); document.getElementById('next-hours-btn').addEventListener('click', function() { switchTab('hours'); }); document.getElementById('back-info-btn').addEventListener('click', function() { switchTab('info'); }); document.getElementById('next-images-btn').addEventListener('click', function() { switchTab('images'); }); document.getElementById('back-hours-btn').addEventListener('click', function() { switchTab('hours'); }); document.getElementById('shop-modal').addEventListener('click', function(e) { if (e.target === this) { closeModal(); } }); document.querySelectorAll('.add-slot-btn').forEach(button => { button.addEventListener('click', function() { const day = this.dataset.day; const container = document.getElementById(`slots-${day}`); addTimeSlot(day, container); }); }); document.querySelectorAll('.toggle-switch').forEach(toggle => { toggle.addEventListener('change', function() { toggleShop(this.dataset.id, this.checked ? 1 : 0); }); }); document.querySelectorAll('.delete-btn').forEach(button => { button.addEventListener('click', function() { deleteShop(this.dataset.id); }); }); document.querySelectorAll('.edit-btn').forEach(button => { button.addEventListener('click', function() { openEditModal(JSON.parse(this.dataset.shop)); }); }); const imageInput = document.getElementById('shop-images'); const imagePreview = document.getElementById('image-preview'); imageInput.addEventListener('change', function() { if (!this.files || !this.files.length) return; if (document.getElementById('shop-id').value === '') { imagePreview.innerHTML = ''; } for (let file of this.files) { if (!file.type.startsWith('image/')) continue; const reader = new FileReader(); reader.onload = function(e) { imagePreview.innerHTML += ` <div class="relative aspect-square rounded-xl overflow-hidden bg-slate-100 dark:bg-slate-700 group"> <img src="${e.target.result}" class="w-full h-full object-cover"> <div class="absolute inset-0 bg-black/60 opacity-0 group-hover:opacity-100 flex items-center justify-center transition"> <button type="button" class="delete-image text-white hover:text-red-400 p-2"> <i class="fas fa-trash"></i> </button> </div> </div> `; const removeButtons = imagePreview.querySelectorAll('.delete-image'); const lastButton = removeButtons[removeButtons.length - 1]; if (lastButton) { lastButton.addEventListener('click', function() { this.closest('.relative').remove(); }); } }; reader.readAsDataURL(file); } }); const dropZone = document.querySelector('.border-dashed'); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, preventDefaults, false); }); function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } ['dragenter', 'dragover'].forEach(eventName => { dropZone.addEventListener(eventName, highlight, false); }); ['dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, unhighlight, false); }); function highlight() { dropZone.classList.add('border-cyan-500', 'bg-cyan-50', 'dark:bg-cyan-900/10'); } function unhighlight() { dropZone.classList.remove('border-cyan-500', 'bg-cyan-50', 'dark:bg-cyan-900/10'); } dropZone.addEventListener('drop', handleDrop, false); function handleDrop(e) { const dt = e.dataTransfer; const files = dt.files; if (files.length > 0) { imageInput.files = files; const event = new Event('change', { bubbles: true }); imageInput.dispatchEvent(event); } } document.getElementById('edit-form').addEventListener('submit', function(e) { const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; let hasOpenDay = false; for (let day of days) { const slots = document.getElementById(`slots-${day}`).querySelectorAll('.time-slot'); for (let slot of slots) { const openInput = slot.querySelector('input[name*="[open]"]'); const closeInput = slot.querySelector('input[name*="[close]"]'); if (openInput.value && closeInput.value) { hasOpenDay = true; break; } } if (hasOpenDay) break; } if (!hasOpenDay) { e.preventDefault(); showNotification('Tu dois définir au moins un créneau horaire pour un jour de la semaine.', 'error'); } }); document.querySelectorAll('.bg-emerald-50, .bg-red-50').forEach(alert => { setTimeout(() => { alert.style.opacity = '0'; alert.style.transition = 'opacity 0.5s'; setTimeout(() => { if (alert.parentNode) { alert.parentNode.removeChild(alert); } }, 500); }, 5000); }); let formChanged = false; document.getElementById('edit-form').addEventListener('input', function() { formChanged = true; }); window.addEventListener('beforeunload', function(e) { if (formChanged && !document.getElementById('shop-modal').classList.contains('hidden')) { e.preventDefault(); e.returnValue = ''; return ''; } }); document.getElementById('edit-form').addEventListener('submit', function() { formChanged = false; }); document.getElementById('close-modal').addEventListener('click', function() { formChanged = false; }); document.getElementById('cancel-btn').addEventListener('click', function() { formChanged = false; }); }); </script> </body> </html>
| ver. 1.6 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка