{"id":2956,"date":"2025-10-16T18:38:47","date_gmt":"2025-10-16T17:38:47","guid":{"rendered":"https:\/\/vipenvironnement.net\/?page_id=2956"},"modified":"2025-12-29T13:52:54","modified_gmt":"2025-12-29T12:52:54","slug":"pointage-employes","status":"publish","type":"page","link":"https:\/\/vipenvironnement.net\/en\/pointage-employes\/","title":{"rendered":"Pointage Employ\u00e9s"},"content":{"rendered":"<div class=\"vip-pointage-container\">\n            <!-- \u00c9tape 1: Authentification Employ\u00e9 -->\n            <div id=\"vip-auth-step\" class=\"vip-step active\">\n                <div class=\"vip-auth-card\">\n                    <div class=\"vip-card-header\">\n                        <h2>\ud83d\udd10 Authentification Employ\u00e9<\/h2>\n                        <p class=\"vip-subtitle\">Identifiez-vous pour acc\u00e9der au pointage<\/p>\n                    <\/div>\n                    \n                    <div class=\"vip-card-body\">\n                        <form id=\"vip-auth-form\" class=\"vip-form\" action=\"\">\n                            <input type=\"hidden\" id=\"vip_auth_nonce\" name=\"vip_auth_nonce\" value=\"e8f347da73\" \/><input type=\"hidden\" name=\"_wp_http_referer\" value=\"\/en\/wp-json\/wp\/v2\/pages\/2956\" \/>                            <div class=\"vip-form-group\">\n                                <label class=\"vip-label\">ID Employ\u00e9 *<\/label>\n                                <input type=\"text\" id=\"vip_employe_id\" name=\"employe_id\" \n                                       required class=\"vip-input\" \n                                       placeholder=\"Ex: EMP001\" maxlength=\"20\">\n                                <div class=\"vip-input-hint\">Votre identifiant unique employ\u00e9<\/div>\n                            <\/div>\n                            \n                            <div class=\"vip-form-actions\">\n                                <button type=\"submit\" id=\"auth-submit-btn\" class=\"vip-button vip-button-primary\">\n                                    Se Connecter\n                                <\/button>\n                            <\/div>\n                        <input type=\"hidden\" name=\"trp-form-language\" value=\"en\"\/><\/form>\n                    <\/div>\n                <\/div>\n            <\/div>\n            \n            <!-- \u00c9tape 2: Interface Pointage -->\n            <div id=\"vip-pointage-step\" class=\"vip-step\">\n                <div class=\"vip-pointage-header\">\n                    <div class=\"vip-employe-welcome\">\n                        <h2>\ud83d\udc4b Bonjour, <span id=\"current-employe-nom\"><\/span><\/h2>\n                        <p>ID: <code id=\"current-employe-id\"><\/code><\/p>\n                    <\/div>\n                    <button type=\"button\" id=\"vip-logout-btn\" class=\"vip-button vip-button-secondary\">\n                        D\u00e9connexion\n                    <\/button>\n                <\/div>\n                \n                <div class=\"vip-grid-2\">\n                    <!-- Carte Pointage -->\n                    <div class=\"vip-card\">\n                        <div class=\"vip-card-header\">\n                            <h3>Pointage Intervention<\/h3>\n                        <\/div>\n                        <div class=\"vip-card-body\">\n                            <form id=\"vip-pointage-form\" class=\"vip-form\" action=\"\">\n                                <input type=\"hidden\" id=\"vip_pointage_nonce\" name=\"vip_pointage_nonce\" value=\"8732427954\" \/><input type=\"hidden\" name=\"_wp_http_referer\" value=\"\/en\/wp-json\/wp\/v2\/pages\/2956\" \/>                                <input type=\"hidden\" id=\"pointage-employe-id\" name=\"employe_id\">\n                                <input type=\"hidden\" id=\"pointage-employe-nom\" name=\"employe_nom\">\n                                \n                                <div class=\"vip-form-group\">\n                                    <label class=\"vip-label\">Type de Pointage *<\/label>\n                                    <select name=\"type_pointage\" id=\"type_pointage\" required class=\"vip-select\">\n                                        <option value=\"\">S\u00e9lectionner...<\/option>\n                                        <option value=\"D\u00e9but Intervention\">\ud83d\udfe2 D\u00e9but Intervention<\/option>\n                                        <option value=\"Fin Intervention\">\ud83d\udd34 Fin Intervention<\/option>\n                                    <\/select>\n                                <\/div>\n                                \n                                <div class=\"vip-form-group\">\n                                    <label class=\"vip-label\">Scanner QR Code *<\/label>\n                                    <div class=\"vip-qr-section\">\n                                        <!-- Scanner QR Code avec jsQR -->\n                                        <div class=\"vip-qr-scanner-container\">\n                                            <button type=\"button\" id=\"start-qr-scanner\" class=\"vip-button vip-button-primary\">\n                                                \ud83d\udcf7 Scanner QR Code avec Cam\u00e9ra\n                                            <\/button>\n                                            \n                                            <div id=\"qr-scanner-area\" style=\"display: none; margin-top: 15px;\">\n                                                <div style=\"text-align: center; margin-bottom: 10px;\">\n                                                    <strong>\ud83d\udcf8 Scanner en Temps R\u00e9el<\/strong>\n                                                    <p style=\"font-size: 12px; color: #666; margin: 5px 0;\">\n                                                        Placez le QR code dans le cadre - D\u00e9tection automatique\n                                                    <\/p>\n                                                <\/div>\n                                                \n                                                <!-- Zone de scan -->\n                                                <div style=\"position: relative; max-width: 400px; margin: 0 auto;\">\n                                                    <video id=\"qr-video\" autoplay playsinline style=\"width: 100%; border: 2px solid #345af1; border-radius: 8px;\"><\/video>\n                                                    <canvas id=\"qr-canvas\" style=\"display: none;\"><\/canvas>\n                                                    <div id=\"qr-overlay\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 2px solid #28a745; pointer-events: none; display: none;\"><\/div>\n                                                <\/div>\n                                                \n                                                <div id=\"qr-status\" style=\"text-align: center; margin: 10px 0; padding: 10px; background: #f8f9fa; border-radius: 6px;\">\n                                                    <span id=\"qr-status-text\">Pr\u00eat \u00e0 scanner...<\/span>\n                                                <\/div>\n                                                \n                                                <div style=\"text-align: center;\">\n                                                    <button type=\"button\" id=\"stop-qr-scanner\" class=\"vip-button vip-button-secondary\">\n                                                        \u274c Arr\u00eater le Scanner\n                                                    <\/button>\n                                                <\/div>\n                                            <\/div>\n                                            \n                                            <input type=\"text\" id=\"qr_code_scanne\" name=\"qr_code_scanne\" \n                                                   required class=\"vip-input\" \n                                                   placeholder=\"Le contenu du QR code appara\u00eetra ici automatiquement\" style=\"margin-top: 15px;\">\n                                        <\/div>\n                                        \n                                        <!-- Solution manuelle de secours -->\n                                        <div style=\"margin-top: 15px; padding: 15px; background: #fff3cd; border-radius: 8px; border-left: 4px solid #ffc107;\">\n                                            <p style=\"margin: 0 0 10px 0; font-size: 14px; color: #856404;\">\n                                                <strong>\ud83d\udd27 Mode Manuel<\/strong><br>\n                                                <small>Si le scanner ne fonctionne pas, saisissez manuellement le num\u00e9ro du QR code :<\/small>\n                                            <\/p>\n                                            <div style=\"display: flex; gap: 10px;\">\n                                                <input type=\"text\" id=\"manual-qr-input\" placeholder=\"Ex: vip-qr-123.png ou URL compl\u00e8te\" \n                                                       style=\"flex: 1; padding: 10px; border: 1px solid #ffeaa7; border-radius: 4px;\">\n                                                <button type=\"button\" id=\"manual-qr-btn\" class=\"vip-button\" style=\"background: #ffc107; color: #212529;\">\n                                                    \u2705 Valider Manuellement\n                                                <\/button>\n                                            <\/div>\n                                        <\/div>\n                                    <\/div>\n                                <\/div>\n                                \n                                <!-- Informations R\u00e9servation -->\n                                <div id=\"reservation-info\" class=\"vip-reservation-card\" style=\"display: none;\">\n                                    <h4>\u2705 R\u00e9servation Trouv\u00e9e<\/h4>\n                                    <div class=\"vip-info-grid\">\n                                        <div class=\"vip-info-item\">\n                                            <strong>Client:<\/strong>\n                                            <span id=\"info-client\"><\/span>\n                                        <\/div>\n                                        <div class=\"vip-info-item\">\n                                            <strong>Bien:<\/strong>\n                                            <span id=\"info-bien\"><\/span>\n                                        <\/div>\n                                        <div class=\"vip-info-item\">\n                                            <strong>Service:<\/strong>\n                                            <span id=\"info-service\"><\/span>\n                                        <\/div>\n                                        <div class=\"vip-info-item\">\n                                            <strong>Adresse:<\/strong>\n                                            <span id=\"info-adresse\"><\/span>\n                                        <\/div>\n                                        <div class=\"vip-info-item\">\n                                            <strong>Type:<\/strong>\n                                            <span id=\"info-type\"><\/span>\n                                        <\/div>\n                                    <\/div>\n                                <\/div>\n                                \n                                <div class=\"vip-form-group\">\n                                    <label class=\"vip-label\">Localisation Automatique<\/label>\n                                    <div class=\"vip-location-section\">\n                                        <button type=\"button\" id=\"vip-get-location\" class=\"vip-button vip-button-secondary\">\n                                            \ud83d\udccd Obtenir l'Adresse\n                                        <\/button>\n                                        <div id=\"location-status\" class=\"vip-location-status\">\n                                            <span id=\"location-text\">En attente de localisation...<\/span>\n                                        <\/div>\n                                        <input type=\"hidden\" id=\"latitude\" name=\"latitude\">\n                                        <input type=\"hidden\" id=\"longitude\" name=\"longitude\">\n                                        <textarea id=\"adresse_pointage\" name=\"adresse_pointage\" \n                                                  class=\"vip-textarea\" rows=\"3\" \n                                                  placeholder=\"Adresse texte d\u00e9tect\u00e9e automatiquement...\" readonly><\/textarea>\n                                    <\/div>\n                                <\/div>\n                                \n                                <div class=\"vip-form-group\">\n                                    <label class=\"vip-label\">Notes (Optionnel)<\/label>\n                                    <textarea name=\"notes\" class=\"vip-textarea\" rows=\"3\" \n                                              placeholder=\"Observations, probl\u00e8mes rencontr\u00e9s...\"><\/textarea>\n                                <\/div>\n                                \n                                <div class=\"vip-form-actions\">\n                                    <button type=\"submit\" id=\"submit-pointage-btn\" class=\"vip-button vip-button-primary\">\n                                        \u2705 Enregistrer le Pointage\n                                    <\/button>\n                                    <div id=\"submit-status\" style=\"margin-top: 10px; display: none;\"><\/div>\n                                <\/div>\n                            <input type=\"hidden\" name=\"trp-form-language\" value=\"en\"\/><\/form>\n                        <\/div>\n                    <\/div>\n                    \n                    <!-- Carte Aide -->\n                    <div class=\"vip-card\">\n                        <div class=\"vip-card-header\">\n                            <h3>\ud83d\udccb Instructions<\/h3>\n                        <\/div>\n                        <div class=\"vip-card-body\">\n                            <div class=\"vip-instructions\">\n                                <div class=\"vip-instruction-item\">\n                                    <strong>\ud83d\udd10 Authentification<\/strong>\n                                    <p>Utilisez votre ID employ\u00e9 pour vous connecter<\/p>\n                                <\/div>\n                                <div class=\"vip-instruction-item\">\n                                    <strong>\ud83d\udcf7 Scanner QR<\/strong>\n                                    <p>Scannez le QR code sur place chez le client<\/p>\n                                <\/div>\n                                <div class=\"vip-instruction-item\">\n                                    <strong>\ud83d\udccd Adresse Texte<\/strong>\n                                    <p>L'adresse en texte est maintenant d\u00e9tect\u00e9e<\/p>\n                                <\/div>\n                                <div class=\"vip-instruction-item\">\n                                    <strong>\u2705 Enregistrement<\/strong>\n                                    <p>Cliquez sur Enregistrer pour valider le pointage<\/p>\n                                <\/div>\n                            <\/div>\n                        <\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <!-- STYLE CSS -->\n        <style>\n        .vip-pointage-container { max-width: 1200px; margin: 0 auto; padding: 20px; }\n        .vip-step { display: none; }\n        .vip-step.active { display: block; }\n        .vip-card { \n            background: white; border-radius: 12px; overflow: hidden;\n            box-shadow: 0 2px 15px rgba(0,0,0,0.08); border: 1px solid #f1f3f4;\n            margin-bottom: 25px;\n        }\n        .vip-card-header { \n            background: #f8f9fa; padding: 20px 25px; border-bottom: 1px solid #e9ecef;\n        }\n        .vip-card-header h2, .vip-card-header h3 { \n            margin: 0; color: #2c3e50; font-size: 18px; font-weight: 600;\n        }\n        .vip-subtitle { color: #7f8c8d; margin: 5px 0 0 0; font-size: 14px; }\n        .vip-card-body { padding: 25px; }\n        .vip-form-group { margin-bottom: 20px; }\n        .vip-label { \n            display: block; margin-bottom: 8px; font-weight: 600; color: #2c3e50;\n            font-size: 14px;\n        }\n        .vip-input, .vip-select, .vip-textarea { \n            width: 100%; padding: 12px 15px; border: 1px solid #e1e5e9; \n            border-radius: 8px; font-size: 14px; transition: all 0.3s ease;\n            background: white;\n        }\n        .vip-input:focus, .vip-select:focus, .vip-textarea:focus { \n            border-color: #345af1; box-shadow: 0 0 0 3px rgba(52, 90, 241, 0.1);\n            outline: none;\n        }\n        .vip-input-hint { \n            font-size: 12px; color: #6c757d; margin-top: 5px; font-style: italic;\n        }\n        .vip-button { \n            padding: 12px 20px; border: none; border-radius: 8px; font-weight: 600;\n            cursor: pointer; transition: all 0.3s ease; font-size: 14px;\n            margin: 5px;\n        }\n        .vip-button-primary { \n            background: #345af1; color: white; \n        }\n        .vip-button-primary:hover { \n            background: #2a4bd4; transform: translateY(-1px);\n            box-shadow: 0 4px 12px rgba(52, 90, 241, 0.3);\n        }\n        .vip-button-secondary { \n            background: #6c757d; color: white; \n        }\n        .vip-button-secondary:hover { background: #5a6268; }\n        .vip-form-actions { margin-top: 25px; }\n        .vip-pointage-header {\n            display: flex; justify-content: space-between; align-items: center;\n            background: white; padding: 20px; border-radius: 12px;\n            margin-bottom: 25px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n        }\n        .vip-employe-welcome h2 { margin: 0; color: #2c3e50; }\n        .vip-employe-welcome p { margin: 5px 0 0 0; color: #6c757d; }\n        .vip-grid-2 { display: grid; grid-template-columns: 1fr 350px; gap: 25px; }\n        \n        .vip-reservation-card {\n            background: #e3f2fd; padding: 15px; border-radius: 8px;\n            border-left: 4px solid #1976d2; margin: 15px 0;\n        }\n        .vip-reservation-card h4 { margin: 0 0 10px 0; color: #1976d2; }\n        .vip-info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }\n        .vip-info-item { display: flex; flex-direction: column; }\n        .vip-info-item strong { font-size: 12px; color: #666; margin-bottom: 2px; }\n        .vip-instructions { display: flex; flex-direction: column; gap: 15px; }\n        .vip-instruction-item { \n            padding: 12px; background: #f8f9fa; border-radius: 8px;\n            border-left: 3px solid #345af1;\n        }\n        .vip-instruction-item strong { display: block; color: #2c3e50; margin-bottom: 5px; }\n        .vip-instruction-item p { margin: 0; color: #6c757d; font-size: 13px; }\n        \n        \/* Scanner styles *\/\n        #qr-video {\n            max-width: 100%;\n            max-height: 300px;\n            background: #000;\n        }\n        \n        #qr-overlay {\n            border: 3px solid #28a745;\n            animation: pulse 2s infinite;\n            box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);\n        }\n        \n        @keyframes pulse {\n            0% { border-color: #28a745; }\n            50% { border-color: #20c997; }\n            100% { border-color: #28a745; }\n        }\n        \n        #qr-status {\n            font-size: 14px;\n            font-weight: 500;\n            min-height: 20px;\n        }\n        \n        .qr-scanning { background: #d4edda !important; color: #155724 !important; }\n        .qr-success { background: #d4edda !important; color: #155724 !important; }\n        .qr-error { background: #f8d7da !important; color: #721c24 !important; }\n        \n        \/* Status messages *\/\n        #submit-status {\n            padding: 10px;\n            border-radius: 6px;\n            text-align: center;\n            font-weight: 500;\n        }\n        \n        .submit-success { background: #d4edda; color: #155724; }\n        .submit-error { background: #f8d7da; color: #721c24; }\n        .submit-loading { background: #fff3cd; color: #856404; }\n        \n        @media (max-width: 768px) {\n            .vip-grid-2 { grid-template-columns: 1fr; }\n            .vip-pointage-header { flex-direction: column; gap: 15px; }\n        }\n        <\/style>\n\n        <!-- JAVASCRIPT CORRIG\u00c9 - SCAN MULTI-CR\u00c9NEAUX + ADRESSE TEXTE -->\n        <script>\n        jQuery(document).ready(function($) {\n            let currentEmploye = null;\n            let cameraStream = null;\n            let scanInterval = null;\n            let isScanning = false;\n            let videoElement = null;\n            let canvasElement = null;\n            let canvasContext = null;\n            \n            \/\/ \u00c9tape 1: Authentification\n            $('#vip-auth-form').on('submit', function(e) {\n                e.preventDefault();\n                \n                const employeId = $('#vip_employe_id').val().trim();\n                const authNonce = $('input[name=\"vip_auth_nonce\"]').val();\n                \n                if (!employeId) {\n                    alert('\u274c Veuillez saisir votre ID employ\u00e9');\n                    return;\n                }\n                \n                $('#auth-submit-btn').prop('disabled', true).text('V\u00e9rification...');\n                \n                $.ajax({\n                    url: 'https:\/\/vipenvironnement.net\/wp-admin\/admin-ajax.php',\n                    type: 'POST',\n                    data: {\n                        action: 'vip_verify_employe',\n                        employe_id: employeId,\n                        vip_auth_nonce: authNonce\n                    },\n                    success: function(response) {\n                        if (response.success) {\n                            currentEmploye = response.data;\n                            switchToPointageStep();\n                        } else {\n                            alert('\u274c ' + response.data);\n                            $('#auth-submit-btn').prop('disabled', false).text('Se Connecter');\n                        }\n                    },\n                    error: function(xhr, status, error) {\n                        alert('\u274c Erreur de connexion au serveur');\n                        $('#auth-submit-btn').prop('disabled', false).text('Se Connecter');\n                    }\n                });\n            });\n            \n            function switchToPointageStep() {\n                $('#vip-auth-step').removeClass('active');\n                $('#vip-pointage-step').addClass('active');\n                \n                $('#current-employe-nom').text(currentEmploye.nom_complet);\n                $('#current-employe-id').text(currentEmploye.id_employe);\n                $('#pointage-employe-id').val(currentEmploye.id_employe);\n                $('#pointage-employe-nom').val(currentEmploye.nom_complet);\n                \n                $('#auth-submit-btn').prop('disabled', false).text('Se Connecter');\n            }\n            \n            \/\/ D\u00e9connexion\n            $('#vip-logout-btn').on('click', function() {\n                currentEmploye = null;\n                $('#vip-pointage-step').removeClass('active');\n                $('#vip-auth-step').addClass('active');\n                $('#vip-auth-form')[0].reset();\n                stopQRScanner();\n            });\n            \n            \/\/ SCANNER QR CODE R\u00c9EL - CORRIG\u00c9\n            $('#start-qr-scanner').on('click', function() {\n                startQRScanner();\n            });\n            \n            $('#stop-qr-scanner').on('click', function() {\n                stopQRScanner();\n            });\n            \n            \/\/ Solution manuelle\n            $('#manual-qr-btn').on('click', function() {\n                const manualQr = $('#manual-qr-input').val().trim();\n                if (manualQr) {\n                    $('#qr_code_scanne').val(manualQr);\n                    processScannedQR(manualQr);\n                    $('#manual-qr-input').val('');\n                } else {\n                    alert('\u274c Veuillez saisir le num\u00e9ro du QR code');\n                }\n            });\n            \n            \/\/ SCANNER QR CODE R\u00c9EL - FONCTIONNEL\n            async function startQRScanner() {\n                try {\n                    $('#start-qr-scanner').prop('disabled', true).text('Initialisation...');\n                    $('#qr-scanner-area').show();\n                    updateQRStatus('\ud83d\udd04 Acc\u00e8s \u00e0 la cam\u00e9ra...', 'scanning');\n                    \n                    \/\/ Initialiser les \u00e9l\u00e9ments\n                    videoElement = document.getElementById('qr-video');\n                    canvasElement = document.getElementById('qr-canvas');\n                    canvasContext = canvasElement.getContext('2d');\n                    \n                    \/\/ Demander l'acc\u00e8s \u00e0 la cam\u00e9ra\n                    cameraStream = await navigator.mediaDevices.getUserMedia({ \n                        video: { \n                            facingMode: 'environment',\n                            width: { ideal: 1280 },\n                            height: { ideal: 720 }\n                        } \n                    });\n                    \n                    \/\/ Afficher le flux vid\u00e9o\n                    videoElement.srcObject = cameraStream;\n                    \n                    \/\/ Attendre que la vid\u00e9o soit pr\u00eate\n                    videoElement.onloadedmetadata = () => {\n                        videoElement.play();\n                        \n                        \/\/ Ajuster le canvas \u00e0 la vid\u00e9o\n                        canvasElement.width = videoElement.videoWidth;\n                        canvasElement.height = videoElement.videoHeight;\n                        \n                        \/\/ Afficher l'overlay de scan\n                        $('#qr-overlay').show();\n                        \n                        \/\/ D\u00e9marrer la d\u00e9tection\n                        startQRDetection();\n                    };\n                    \n                } catch (error) {\n                    console.error('Erreur cam\u00e9ra:', error);\n                    handleCameraError(error);\n                }\n            }\n            \n            function startQRDetection() {\n                if (isScanning) return;\n                \n                isScanning = true;\n                updateQRStatus('\ud83d\udd0d Recherche de QR code...', 'scanning');\n                \n                scanInterval = setInterval(() => {\n                    if (!isScanning || !videoElement || videoElement.readyState !== videoElement.HAVE_ENOUGH_DATA) {\n                        return;\n                    }\n                    \n                    try {\n                        \/\/ Capturer l'image de la vid\u00e9o\n                        canvasContext.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);\n                        const imageData = canvasContext.getImageData(0, 0, canvasElement.width, canvasElement.height);\n                        \n                        \/\/ D\u00e9tecter le QR code avec jsQR\n                        const code = jsQR(imageData.data, imageData.width, imageData.height, {\n                            inversionAttempts: \"dontInvert\",\n                        });\n                        \n                        if (code) {\n                            \/\/ QR code d\u00e9tect\u00e9 !\n                            console.log('QR code d\u00e9tect\u00e9:', code.data);\n                            $('#qr_code_scanne').val(code.data);\n                            updateQRStatus('\u2705 QR code d\u00e9tect\u00e9!', 'success');\n                            stopQRScanner();\n                            processScannedQR(code.data);\n                        }\n                    } catch (error) {\n                        console.error('Erreur d\u00e9tection QR:', error);\n                    }\n                }, 250); \/\/ V\u00e9rifier toutes les 250ms (plus lent pour meilleure performance)\n            }\n            \n            function stopQRScanner() {\n                isScanning = false;\n                \n                if (scanInterval) {\n                    clearInterval(scanInterval);\n                    scanInterval = null;\n                }\n                \n                if (cameraStream) {\n                    cameraStream.getTracks().forEach(track => track.stop());\n                    cameraStream = null;\n                }\n                \n                if (videoElement) {\n                    videoElement.srcObject = null;\n                }\n                \n                $('#qr-scanner-area').hide();\n                $('#qr-overlay').hide();\n                $('#start-qr-scanner').prop('disabled', false).text('\ud83d\udcf7 Scanner QR Code avec Cam\u00e9ra');\n                updateQRStatus('Pr\u00eat \u00e0 scanner...', 'ready');\n            }\n            \n            function updateQRStatus(message, type) {\n                const statusElement = $('#qr-status-text');\n                const statusContainer = $('#qr-status');\n                \n                statusElement.text(message);\n                statusContainer.removeClass('qr-scanning qr-success qr-error');\n                \n                if (type === 'scanning') {\n                    statusContainer.addClass('qr-scanning');\n                } else if (type === 'success') {\n                    statusContainer.addClass('qr-success');\n                } else if (type === 'error') {\n                    statusContainer.addClass('qr-error');\n                }\n            }\n            \n            function handleCameraError(error) {\n                let errorMessage = 'Erreur cam\u00e9ra: ';\n                \n                if (error.name === 'NotAllowedError') {\n                    errorMessage = '\u274c Acc\u00e8s cam\u00e9ra refus\u00e9. Autorisez la cam\u00e9ra dans les param\u00e8tres de votre navigateur.';\n                } else if (error.name === 'NotFoundError') {\n                    errorMessage = '\u274c Aucune cam\u00e9ra d\u00e9tect\u00e9e. V\u00e9rifiez que votre appareil poss\u00e8de une cam\u00e9ra.';\n                } else if (error.name === 'NotSupportedError') {\n                    errorMessage = '\u274c Votre navigateur ne supporte pas l\\'acc\u00e8s cam\u00e9ra. Essayez avec Chrome ou Firefox.';\n                } else {\n                    errorMessage = '\u274c Erreur inconnue: ' + error.message;\n                }\n                \n                alert(errorMessage);\n                updateQRStatus(errorMessage, 'error');\n                $('#start-qr-scanner').prop('disabled', false).text('\ud83d\udcf7 Scanner QR Code avec Cam\u00e9ra');\n            }\n            \n            function processScannedQR(qrData) {\n                console.log('QR code \u00e0 traiter:', qrData);\n                \n                if (!qrData || qrData.trim() === '') {\n                    alert('\u274c QR code vide d\u00e9tect\u00e9');\n                    return;\n                }\n                \n                \/\/ R\u00e9cup\u00e9rer les informations de r\u00e9servation via AJAX\n                const pointageNonce = $('input[name=\"vip_pointage_nonce\"]').val();\n                \n                $.ajax({\n                    url: 'https:\/\/vipenvironnement.net\/wp-admin\/admin-ajax.php',\n                    type: 'POST',\n                    data: {\n                        action: 'vip_get_reservation_info',\n                        qr_data: qrData,\n                        vip_pointage_nonce: pointageNonce\n                    },\n                    beforeSend: function() {\n                        updateQRStatus('\ud83d\udd0d Recherche des informations...', 'scanning');\n                    },\n                    success: function(response) {\n                        if (response.success) {\n                            $('#info-client').text(response.data.client_nom);\n                            $('#info-bien').text(response.data.bien_nom);\n                            $('#info-service').text(response.data.service_nom);\n                            $('#info-adresse').text(response.data.adresse);\n                            $('#info-type').text(response.data.type_reservation);\n                            $('#reservation-info').show();\n                            updateQRStatus('\u2705 R\u00e9servation trouv\u00e9e!', 'success');\n                        } else {\n                            alert('\u26a0\ufe0f ' + response.data);\n                            updateQRStatus('\u274c ' + response.data, 'error');\n                        }\n                    },\n                    error: function() {\n                        alert('\u274c Erreur lors de la r\u00e9cup\u00e9ration des informations');\n                        updateQRStatus('\u274c Erreur connexion', 'error');\n                    }\n                });\n            }\n            \n            \/\/ G\u00c9OLOCALISATION AVEC ADRESSE TEXTE - CORRIG\u00c9\n            $('#vip-get-location').on('click', function() {\n                if (!navigator.geolocation) {\n                    showLocationStatus('\u274c G\u00e9olocalisation non support\u00e9e', 'error');\n                    return;\n                }\n                \n                $('#vip-get-location').prop('disabled', true).text('D\u00e9tection adresse...');\n                showLocationStatus('\ud83d\udd04 D\u00e9tection de votre position...', 'loading');\n                \n                navigator.geolocation.getCurrentPosition(\n                    function(position) {\n                        const lat = position.coords.latitude;\n                        const lng = position.coords.longitude;\n                        \n                        $('#latitude').val(lat);\n                        $('#longitude').val(lng);\n                        \n                        \/\/ Obtenir l'adresse texte depuis les coordonn\u00e9es\n                        getAddressFromCoordinates(lat, lng);\n                    },\n                    function(error) {\n                        let message = '\u274c Erreur: ';\n                        switch(error.code) {\n                            case error.PERMISSION_DENIED:\n                                message += 'Permission refus\u00e9e';\n                                break;\n                            case error.POSITION_UNAVAILABLE:\n                                message += 'Position indisponible';\n                                break;\n                            case error.TIMEOUT:\n                                message += 'Timeout';\n                                break;\n                            default:\n                                message += 'Erreur inconnue';\n                        }\n                        showLocationStatus(message, 'error');\n                        $('#vip-get-location').prop('disabled', false).text('\ud83d\udccd Obtenir l\\'Adresse');\n                    }\n                );\n            });\n            \n            function getAddressFromCoordinates(lat, lng) {\n                showLocationStatus('\ud83d\udd04 R\u00e9cup\u00e9ration de l\\'adresse...', 'loading');\n                \n                \/\/ Utiliser l'API Nominatim d'OpenStreetMap pour obtenir l'adresse texte\n                $.ajax({\n                    url: 'https:\/\/nominatim.openstreetmap.org\/reverse',\n                    data: {\n                        format: 'json',\n                        lat: lat,\n                        lon: lng,\n                        addressdetails: 1,\n                        'accept-language': 'fr'\n                    },\n                    success: function(data) {\n                        if (data && data.display_name) {\n                            const address = data.display_name;\n                            $('#adresse_pointage').val(address);\n                            showLocationStatus('\u2705 Adresse d\u00e9tect\u00e9e avec succ\u00e8s', 'success');\n                        } else {\n                            \/\/ Fallback si pas d'adresse trouv\u00e9e\n                            const address = `Adresse approximative: ${lat.toFixed(6)}, ${lng.toFixed(6)}`;\n                            $('#adresse_pointage').val(address);\n                            showLocationStatus('\u26a0\ufe0f Adresse approximative (coordonn\u00e9es)', 'success');\n                        }\n                        $('#vip-get-location').prop('disabled', false).text('\ud83d\udccd Obtenir l\\'Adresse');\n                    },\n                    error: function() {\n                        \/\/ Fallback en cas d'erreur\n                        const address = `Adresse localis\u00e9e: ${lat.toFixed(6)}, ${lng.toFixed(6)}`;\n                        $('#adresse_pointage').val(address);\n                        showLocationStatus('\u26a0\ufe0f Adresse bas\u00e9e sur coordonn\u00e9es', 'success');\n                        $('#vip-get-location').prop('disabled', false).text('\ud83d\udccd Obtenir l\\'Adresse');\n                    }\n                });\n            }\n            \n            \/\/ SOUMISSION POINTAGE - BASE DE DONN\u00c9ES CORRIG\u00c9E\n            $('#vip-pointage-form').on('submit', function(e) {\n                e.preventDefault();\n                \n                if (!$('#type_pointage').val()) {\n                    showSubmitStatus('\u274c Veuillez s\u00e9lectionner le type de pointage', 'error');\n                    return;\n                }\n                \n                if (!$('#qr_code_scanne').val()) {\n                    showSubmitStatus('\u274c Veuillez scanner le QR code', 'error');\n                    return;\n                }\n                \n                const pointageNonce = $('input[name=\"vip_pointage_nonce\"]').val();\n                \n                \/\/ Pr\u00e9parer les donn\u00e9es\n                const formData = {\n                    action: 'vip_submit_pointage',\n                    employe_id: $('#pointage-employe-id').val(),\n                    employe_nom: $('#pointage-employe-nom').val(),\n                    type_pointage: $('#type_pointage').val(),\n                    qr_code_scanne: $('#qr_code_scanne').val(),\n                    latitude: $('#latitude').val() || '',\n                    longitude: $('#longitude').val() || '',\n                    adresse_pointage: $('#adresse_pointage').val() || '',\n                    notes: $('textarea[name=\"notes\"]').val() || '',\n                    vip_pointage_nonce: pointageNonce\n                };\n                \n                \/\/ Afficher le statut de chargement\n                showSubmitStatus('\u23f3 Enregistrement en cours...', 'loading');\n                $('#submit-pointage-btn').prop('disabled', true).text('Enregistrement...');\n                \n                \/\/ Envoyer la requ\u00eate AJAX\n                $.ajax({\n                    url: 'https:\/\/vipenvironnement.net\/wp-admin\/admin-ajax.php',\n                    type: 'POST',\n                    data: formData,\n                    dataType: 'json',\n                    success: function(response) {\n                        if (response.success) {\n                            showSubmitStatus('\u2705 ' + response.data, 'success');\n                            \n                            \/\/ R\u00e9initialiser le formulaire apr\u00e8s 2 secondes\n                            setTimeout(function() {\n                                $('#vip-pointage-form')[0].reset();\n                                $('#reservation-info').hide();\n                                $('#submit-status').hide();\n                                stopQRScanner();\n                                $('#submit-pointage-btn').prop('disabled', false).text('\u2705 Enregistrer le Pointage');\n                            }, 2000);\n                            \n                        } else {\n                            showSubmitStatus('\u274c ' + response.data, 'error');\n                            $('#submit-pointage-btn').prop('disabled', false).text('\u2705 Enregistrer le Pointage');\n                        }\n                    },\n                    error: function(xhr, status, error) {\n                        showSubmitStatus('\u274c Erreur de connexion au serveur', 'error');\n                        $('#submit-pointage-btn').prop('disabled', false).text('\u2705 Enregistrer le Pointage');\n                    }\n                });\n            });\n            \n            \/\/ Fonctions utilitaires\n            function showSubmitStatus(message, type) {\n                const statusElement = $('#submit-status');\n                statusElement.text(message).show();\n                statusElement.removeClass('submit-success submit-error submit-loading')\n                           .addClass('submit-' + type);\n            }\n            \n            function showLocationStatus(message, type) {\n                $('#location-text').text(message);\n            }\n        });\n        <\/script>\n        \n\n\n\n<p><\/p>","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2956","page","type-page","status-publish","hentry"],"blocksy_meta":[],"_hostinger_reach_plugin_has_subscription_block":false,"_hostinger_reach_plugin_is_elementor":false,"_links":{"self":[{"href":"https:\/\/vipenvironnement.net\/en\/wp-json\/wp\/v2\/pages\/2956","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vipenvironnement.net\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/vipenvironnement.net\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/vipenvironnement.net\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vipenvironnement.net\/en\/wp-json\/wp\/v2\/comments?post=2956"}],"version-history":[{"count":2,"href":"https:\/\/vipenvironnement.net\/en\/wp-json\/wp\/v2\/pages\/2956\/revisions"}],"predecessor-version":[{"id":2958,"href":"https:\/\/vipenvironnement.net\/en\/wp-json\/wp\/v2\/pages\/2956\/revisions\/2958"}],"wp:attachment":[{"href":"https:\/\/vipenvironnement.net\/en\/wp-json\/wp\/v2\/media?parent=2956"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}