Файловый менеджер - Редактировать - /home/gqdcvggs/vertchasseur.com/suivi.php
Назад
<?php require_once 'db.php'; session_start(); if (!isset($_GET['commande']) || !is_numeric($_GET['commande'])) { die('Commande introuvable'); } $commandeId = (int)$_GET['commande']; if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!verifyCSRFToken($_POST['csrf_token'] ?? '')) { die('Token CSRF invalide'); } if (isset($_POST['action'])) { switch ($_POST['action']) { case 'send_message': $message = sanitizeInput($_POST['message']); db()->insert('vr_messages', [ 'commande_id' => $commandeId, 'expediteur' => 'client', 'message' => $message ]); echo json_encode(['success' => true]); exit; case 'get_messages': $messages = db()->fetchAll(" SELECT * FROM vr_messages WHERE commande_id = ? ORDER BY created_at ASC ", [$commandeId]); db()->update('vr_messages', ['lu' => 1], 'commande_id = ? AND expediteur = "livreur"', [$commandeId]); echo json_encode($messages); exit; case 'get_status': $commande = db()->fetchOne(" SELECT c.*, l.prenom as livreur_prenom, l.nom as livreur_nom, l.photo_profil, l.telephone as livreur_tel FROM vr_commandes c LEFT JOIN vr_livreurs l ON c.livreur_id = l.id WHERE c.id = ? ", [$commandeId]); echo json_encode($commande); exit; case 'rate_delivery': $note = (int)$_POST['note']; $commentaire = sanitizeInput($_POST['commentaire'] ?? ''); if ($note >= 1 && $note <= 5) { db()->update('vr_commandes', ['note_client' => $note, 'commentaire_client' => $commentaire], 'id = ? AND statut = "livree"', [$commandeId] ); $commande = db()->fetchOne("SELECT livreur_id FROM vr_commandes WHERE id = ?", [$commandeId]); if ($commande['livreur_id']) { $avgNote = db()->fetchOne(" SELECT AVG(note_client) as moyenne FROM vr_commandes WHERE livreur_id = ? AND note_client IS NOT NULL ", [$commande['livreur_id']]); db()->update('vr_livreurs', ['note_moyenne' => round($avgNote['moyenne'], 1)], 'id = ?', [$commande['livreur_id']] ); } echo json_encode(['success' => true]); } else { echo json_encode(['error' => 'Note invalide']); } exit; } } } $commande = db()->fetchOne(" SELECT c.*, co.nom as commerce_nom, r.first_name, r.last_name, l.prenom as livreur_prenom, l.nom as livreur_nom, l.photo_profil, l.telephone as livreur_tel FROM vr_commandes c JOIN vr_commerces co ON c.commerce_id = co.id JOIN residents r ON c.resident_id = r.id LEFT JOIN vr_livreurs l ON c.livreur_id = l.id WHERE c.id = ? ", [$commandeId]); if (!$commande) { die('Commande introuvable'); } $items = db()->fetchAll(" SELECT ci.*, p.nom as produit_nom FROM vr_commandes_items ci JOIN vr_produits p ON ci.produit_id = p.id WHERE ci.commande_id = ? ", [$commandeId]); ?> <!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Suivi de commande #<?= $commandeId ?> - Vert Chasseur</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: Arial, sans-serif; background: #f5f5f5; } .header { background: #2c5530; color: white; padding: 15px 0; text-align: center; } .container { max-width: 800px; margin: 0 auto; padding: 20px; } .card { background: white; padding: 25px; margin-bottom: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .status-timeline { display: flex; justify-content: space-between; margin: 30px 0; position: relative; } .status-timeline::before { content: ''; position: absolute; top: 20px; left: 0; right: 0; height: 2px; background: #ddd; z-index: 1; } .status-step { text-align: center; background: white; padding: 0 10px; z-index: 2; position: relative; } .status-icon { width: 40px; height: 40px; border-radius: 50%; background: #ddd; display: flex; align-items: center; justify-content: center; margin: 0 auto 10px; font-size: 18px; } .status-step.completed .status-icon { background: #28a745; color: white; } .status-step.current .status-icon { background: #ffc107; color: #000; } .status-label { font-size: 12px; color: #666; } .driver-info { display: flex; align-items: center; gap: 15px; padding: 15px; background: #e8f5e9; border-radius: 5px; } .driver-photo { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; } .chat-container { max-height: 300px; overflow-y: auto; border: 1px solid #ddd; padding: 15px; margin: 15px 0; border-radius: 5px; background: #fafafa; } .message { margin-bottom: 15px; padding: 10px; border-radius: 8px; } .message.client { background: #e3f2fd; text-align: right; margin-left: 20%; } .message.livreur { background: #f1f8e9; text-align: left; margin-right: 20%; } .message-time { font-size: 11px; color: #666; margin-top: 5px; } .message-input { display: flex; gap: 10px; margin-top: 15px; } .message-input input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 5px; } .btn { background: #2c5530; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; } .btn:hover { background: #1e3a21; } .btn-call { background: #007bff; text-decoration: none; display: inline-block; } .order-summary { background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 15px 0; } .summary-line { display: flex; justify-content: space-between; margin-bottom: 8px; } .summary-total { font-weight: bold; font-size: 18px; color: #2c5530; border-top: 1px solid #ddd; padding-top: 8px; } .rating-section { margin-top: 20px; padding: 20px; background: #fff3cd; border-radius: 5px; } .stars { display: flex; gap: 5px; margin: 10px 0; } .star { font-size: 24px; color: #ddd; cursor: pointer; } .star.active { color: #ffc107; } .hidden { display: none; } h3 { color: #2c5530; margin-bottom: 15px; } .status-current { color: #ffc107; font-weight: bold; } .order-details { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } .detail-group h4 { margin-bottom: 10px; color: #2c5530; } </style> </head> <body> <div class="header"> <h1>🌿 Suivi de commande #<?= $commandeId ?></h1> </div> <div class="container"> <div class="card"> <h3>Statut de votre commande</h3> <div class="status-current" id="current-status"> <?= ucfirst(str_replace('_', ' ', $commande['statut'])) ?> </div> <div class="status-timeline"> <div class="status-step" id="step-confirmee"> <div class="status-icon">✓</div> <div class="status-label">Confirmée</div> </div> <div class="status-step" id="step-en_route_commerce"> <div class="status-icon">🚗</div> <div class="status-label">En route vers le commerce</div> </div> <div class="status-step" id="step-commande_recue"> <div class="status-icon">📦</div> <div class="status-label">Réception de votre commande</div> </div> <div class="status-step" id="step-en_route_client"> <div class="status-icon">🏠</div> <div class="status-label">En route vers votre domicile</div> </div> <div class="status-step" id="step-livree"> <div class="status-icon">🎉</div> <div class="status-label">Arrivée</div> </div> </div> </div> <?php if ($commande['livreur_prenom']): ?> <div class="card"> <h3>Votre livreur</h3> <div class="driver-info"> <img src="<?= $commande['photo_profil'] ?: 'default-avatar.png' ?>" class="driver-photo" alt="Photo livreur"> <div> <h4><?= htmlspecialchars($commande['livreur_prenom'] . ' ' . $commande['livreur_nom']) ?></h4> <p><?= htmlspecialchars($commande['livreur_prenom']) ?> prend en charge votre commande</p> <?php if ($commande['statut'] !== 'livree'): ?> <a href="tel:<?= $commande['livreur_tel'] ?>" class="btn btn-call">📞 Appeler</a> <button class="btn" onclick="toggleChat()" id="chat-toggle">💬 Message</button> <?php endif; ?> </div> </div> <?php if ($commande['statut'] !== 'livree'): ?> <div id="chat-section" class="hidden"> <div class="chat-container" id="messages-container"></div> <div class="message-input"> <input type="text" id="message-input" placeholder="Tapez votre message..." maxlength="500"> <button class="btn" onclick="sendMessage()">Envoyer</button> </div> </div> <?php endif; ?> </div> <?php endif; ?> <div class="card"> <h3>Détails de la commande</h3> <div class="order-details"> <div class="detail-group"> <h4>Informations générales</h4> <p><strong>Commerce:</strong> <?= htmlspecialchars($commande['commerce_nom']) ?></p> <p><strong>Client:</strong> <?= htmlspecialchars($commande['first_name'] . ' ' . $commande['last_name']) ?></p> <p><strong>Date:</strong> <?= date('d/m/Y H:i', strtotime($commande['date_commande'])) ?></p> <p><strong>Créneau:</strong> <?= $commande['creneau_debut'] ?> - <?= $commande['creneau_fin'] ?></p> </div> <div class="detail-group"> <h4>Livraison</h4> <p><strong>Adresse:</strong> <?= htmlspecialchars($commande['adresse_livraison']) ?></p> <p><strong>Distance:</strong> <?= $commande['distance_km'] ?> km</p> <?php if ($commande['instructions_livraison']): ?> <p><strong>Instructions:</strong> <?= htmlspecialchars($commande['instructions_livraison']) ?></p> <?php endif; ?> </div> </div> <div class="order-summary"> <h4>Articles commandés</h4> <?php foreach ($items as $item): ?> <div class="summary-line"> <span><?= $item['quantite'] ?>x <?= htmlspecialchars($item['produit_nom']) ?></span> <span><?= number_format($item['total'], 2) ?>€</span> </div> <?php endforeach; ?> <div class="summary-line"> <span>Total produits:</span> <span><?= number_format($commande['total_produits'], 2) ?>€</span> </div> <div class="summary-line"> <span>Frais de livraison:</span> <span><?= number_format($commande['frais_livraison'], 2) ?>€</span> </div> <div class="summary-line summary-total"> <span>Total:</span> <span><?= number_format($commande['total_commande'], 2) ?>€</span> </div> </div> </div> <?php if ($commande['statut'] === 'livree' && !$commande['note_client']): ?> <div class="card"> <div class="rating-section"> <h3>Notez votre livraison</h3> <p>Comment s'est passée votre livraison avec <?= htmlspecialchars($commande['livreur_prenom']) ?> ?</p> <div class="stars" id="rating-stars"> <span class="star" data-rating="1">⭐</span> <span class="star" data-rating="2">⭐</span> <span class="star" data-rating="3">⭐</span> <span class="star" data-rating="4">⭐</span> <span class="star" data-rating="5">⭐</span> </div> <textarea id="comment-input" placeholder="Commentaire (optionnel)" rows="3" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; margin: 10px 0;"></textarea> <button class="btn" onclick="submitRating()" id="submit-rating">Envoyer la note</button> </div> </div> <?php elseif ($commande['note_client']): ?> <div class="card"> <h3>Votre évaluation</h3> <p>Note donnée: <?= $commande['note_client'] ?>/5 ⭐</p> <?php if ($commande['commentaire_client']): ?> <p>Commentaire: <?= htmlspecialchars($commande['commentaire_client']) ?></p> <?php endif; ?> </div> <?php endif; ?> </div> <script> let selectedRating = 0; let refreshInterval; document.addEventListener('DOMContentLoaded', function() { updateStatus('<?= $commande['statut'] ?>'); loadMessages(); if ('<?= $commande['statut'] ?>' !== 'livree') { refreshInterval = setInterval(function() { checkStatus(); loadMessages(); }, 10000); } setupRatingStars(); }); function updateStatus(status) { const steps = ['confirmee', 'en_route_commerce', 'commande_recue', 'en_route_client', 'livree']; const currentIndex = steps.indexOf(status); steps.forEach((step, index) => { const element = document.getElementById(`step-${step}`); if (index <= currentIndex) { element.classList.add('completed'); } if (index === currentIndex) { element.classList.add('current'); } }); const statusLabels = { 'en_attente': 'En attente de confirmation', 'confirmee': 'Commande confirmée', 'en_route_commerce': 'En route vers le commerce', 'commande_recue': 'Commande récupérée', 'en_route_client': 'En route vers votre domicile', 'livree': 'Commande livrée' }; document.getElementById('current-status').textContent = statusLabels[status] || status; } function checkStatus() { fetch('suivi.php?commande=<?= $commandeId ?>', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'action=get_status&csrf_token=<?= generateCSRFToken() ?>' }) .then(response => response.json()) .then(data => { if (data.statut !== '<?= $commande['statut'] ?>') { updateStatus(data.statut); if (data.statut === 'livree') { clearInterval(refreshInterval); location.reload(); } } }); } function toggleChat() { const chatSection = document.getElementById('chat-section'); const toggle = document.getElementById('chat-toggle'); if (chatSection.classList.contains('hidden')) { chatSection.classList.remove('hidden'); toggle.textContent = '❌ Fermer chat'; loadMessages(); } else { chatSection.classList.add('hidden'); toggle.textContent = '💬 Message'; } } function loadMessages() { fetch('suivi.php?commande=<?= $commandeId ?>', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'action=get_messages&csrf_token=<?= generateCSRFToken() ?>' }) .then(response => response.json()) .then(messages => { const container = document.getElementById('messages-container'); if (!container) return; container.innerHTML = messages.map(msg => ` <div class="message ${msg.expediteur}"> <strong>${msg.expediteur === 'client' ? 'Vous' : '<?= htmlspecialchars($commande['livreur_prenom']) ?>'}:</strong> ${msg.message} <div class="message-time">${new Date(msg.created_at).toLocaleString()}</div> </div> `).join(''); container.scrollTop = container.scrollHeight; }); } function sendMessage() { const input = document.getElementById('message-input'); const message = input.value.trim(); if (!message) return; fetch('suivi.php?commande=<?= $commandeId ?>', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `action=send_message&message=${encodeURIComponent(message)}&csrf_token=<?= generateCSRFToken() ?>` }) .then(response => response.json()) .then(data => { if (data.success) { input.value = ''; loadMessages(); } }); } function setupRatingStars() { const stars = document.querySelectorAll('.star'); stars.forEach(star => { star.addEventListener('click', function() { selectedRating = parseInt(this.dataset.rating); updateStarsDisplay(); }); star.addEventListener('mouseover', function() { const hoverRating = parseInt(this.dataset.rating); updateStarsDisplay(hoverRating); }); }); document.getElementById('rating-stars').addEventListener('mouseleave', function() { updateStarsDisplay(); }); } function updateStarsDisplay(hoverRating = null) { const stars = document.querySelectorAll('.star'); const rating = hoverRating || selectedRating; stars.forEach((star, index) => { if (index < rating) { star.classList.add('active'); } else { star.classList.remove('active'); } }); } function submitRating() { if (selectedRating === 0) { alert('Veuillez sélectionner une note'); return; } const comment = document.getElementById('comment-input').value.trim(); fetch('suivi.php?commande=<?= $commandeId ?>', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `action=rate_delivery¬e=${selectedRating}&commentaire=${encodeURIComponent(comment)}&csrf_token=<?= generateCSRFToken() ?>` }) .then(response => response.json()) .then(data => { if (data.success) { alert('Merci pour votre évaluation !'); location.reload(); } else { alert('Erreur: ' + data.error); } }); } document.getElementById('message-input')?.addEventListener('keypress', function(e) { if (e.key === 'Enter') { sendMessage(); } }); </script> </body> </html>
| ver. 1.6 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка