Outiref

Code source de l'URL : http://rdv.glowupsignature.fr/

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Prendre rendez-vous — Wafa Mazouz — Glow Up Signature</title>
<meta name="robots" content="noindex">

<!-- PWA -->
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#E8935E">
<link rel="apple-touch-icon" href="apple-touch-icon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="GUS RDV">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,500;0,600;1,500&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
  :root {
    --cream: #FDFBF7; --cream-deep: #F7EEE3;
    --peach: #F4B896; --coral: #E8935E;
    --burgundy: #5C2A2A; --burgundy-soft: #8a5a4a;
  }
  * { box-sizing: border-box; }
  html, body { margin:0; padding:0; background: var(--cream); font-family:'Manrope',sans-serif; color: var(--burgundy); }

  .wrap { max-width: 560px; margin: 0 auto; padding: 24px 18px 60px; }

  .top-header { text-align: center; margin-bottom: 24px; }
  .top-header__eyebrow { font-size: .7rem; font-weight:700; letter-spacing:.16em; text-transform:uppercase; color: var(--coral); display:block; margin-bottom: 8px; }
  .top-header__title { font-family:'Playfair Display',serif; font-weight:600; font-size:1.7rem; margin:0; }

  .steps { display:flex; justify-content:center; gap:8px; margin-bottom: 28px; }
  .step-dot { width:8px; height:8px; border-radius:50%; background:#e5d8c9; transition:background .2s; }
  .step-dot.active { background: var(--coral); width:22px; border-radius:5px; }

  .panel { display:none; }
  .panel.active { display:block; animation: fadein .25s ease; }
  @keyframes fadein { from{opacity:0;transform:translateY(6px);} to{opacity:1;transform:translateY(0);} }

  .cat-title { font-family:'Playfair Display',serif; font-weight:600; font-size:1.05rem; margin: 20px 0 10px; }
  .cat-title:first-child { margin-top:0; }

  .service-card {
    display:flex; justify-content:space-between; align-items:center;
    width:100%; text-align:left; background:#fff; border:1.5px solid #ece1d4; border-radius:14px;
    padding:14px 16px; margin-bottom:10px; cursor:pointer; transition:border-color .15s, background .15s;
  }
  .service-card:hover, .service-card.selected { border-color: var(--coral); background:#fff9f4; }
  .service-card__name { font-weight:700; font-size:.92rem; }
  .service-card__duration { font-size:.72rem; color: var(--burgundy-soft); margin-top:2px; }
  .service-card__price { font-weight:800; color: var(--coral); font-size:.95rem; white-space:nowrap; padding-left:10px; }

  .btn-back { background:none; border:none; color: var(--burgundy-soft); font-size:.82rem; font-weight:600; cursor:pointer; margin-bottom:16px; padding:0; display:inline-flex; align-items:center; gap:5px; }

  input[type=date] {
    width:100%; padding:12px 14px; border-radius:12px; border:1.5px solid #ece1d4;
    font-family:'Manrope',sans-serif; font-size:.9rem; color: var(--burgundy); margin-bottom:18px;
  }

  .slots-grid { display:grid; grid-template-columns: repeat(3,1fr); gap:8px; margin-bottom: 10px; }
  .slot-btn {
    padding:11px 4px; border-radius:10px; border:1.5px solid #ece1d4; background:#fff;
    font-size:.82rem; font-weight:700; color: var(--burgundy); cursor:pointer; transition:.15s;
  }
  .slot-btn:hover { border-color: var(--coral); }
  .slot-btn.selected { background: var(--coral); border-color: var(--coral); color:#fff; }
  .slots-empty, .slots-loading { text-align:center; color: var(--burgundy-soft); font-size:.85rem; padding:20px 0; }

  label { display:block; font-size:.78rem; font-weight:700; margin-bottom:5px; color: var(--burgundy-soft); }
  .field { margin-bottom:14px; }
  input[type=text], input[type=email], input[type=tel], textarea {
    width:100%; padding:11px 14px; border-radius:12px; border:1.5px solid #ece1d4;
    font-family:'Manrope',sans-serif; font-size:.9rem; color: var(--burgundy);
  }
  textarea { resize: vertical; min-height:60px; }

  .btn-primary {
    width:100%; padding:15px; border:none; border-radius:50px;
    background: linear-gradient(135deg, var(--coral), #d97a45); color:#fff; font-weight:700; font-size:.95rem;
    cursor:pointer; box-shadow: 0 10px 24px rgba(232,147,94,.35); margin-top:6px;
  }
  .btn-primary:disabled { opacity:.5; cursor:not-allowed; box-shadow:none; }

  .recap { background: var(--cream-deep); border-radius:14px; padding:16px 18px; margin-bottom:18px; font-size:.85rem; line-height:1.7; }
  .recap strong { color: var(--burgundy); }

  .error-msg { background:#fdeeea; color:#b3402a; border-radius:10px; padding:10px 14px; font-size:.82rem; margin-bottom:14px; display:none; }
  .error-msg.show { display:block; }

  .confirm-box { text-align:center; padding: 20px 0; }
  .confirm-box__icon { width:56px; height:56px; border-radius:50%; background:linear-gradient(135deg,var(--peach),var(--coral)); display:flex; align-items:center; justify-content:center; margin:0 auto 16px; }
  .confirm-box__title { font-family:'Playfair Display',serif; font-weight:600; font-size:1.4rem; margin:0 0 10px; }
  .confirm-box__text { font-size:.88rem; color: var(--burgundy-soft); line-height:1.6; }

  .install-prompt {
    display: none;
    text-align: left;
    background: var(--cream-deep);
    border-radius: 16px;
    padding: 18px 20px;
    margin-top: 28px;
  }
  .install-prompt.show { display: block; }
  .install-prompt__title {
    font-family: 'Playfair Display', serif;
    font-weight: 600;
    font-size: 1rem;
    color: var(--burgundy);
    margin: 0 0 6px;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .install-prompt__text {
    font-size: .82rem;
    color: var(--burgundy-soft);
    line-height: 1.55;
    margin: 0 0 14px;
  }
  .install-prompt__text strong { color: var(--burgundy); }
  .install-prompt__btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 11px 20px;
    border-radius: 50px;
    background: linear-gradient(135deg, var(--coral), #d97a45);
    color: #fff;
    border: none;
    font-weight: 700;
    font-size: .85rem;
    cursor: pointer;
  }
  .install-prompt__ios-steps {
    font-size: .82rem;
    color: var(--burgundy);
    background: #fff;
    border-radius: 10px;
    padding: 12px 14px;
    line-height: 1.6;
  }
</style>
</head>
<body>
<div class="wrap">

  <div class="top-header">
    <span class="top-header__eyebrow">Wafa Mazouz — Glow Up Signature</span>
    <h1 class="top-header__title">Prendre rendez-vous</h1>
  </div>

  
    <div class="steps">
      <div class="step-dot active" data-step-dot="1"></div>
      <div class="step-dot" data-step-dot="2"></div>
      <div class="step-dot" data-step-dot="3"></div>
      <div class="step-dot" data-step-dot="4"></div>
    </div>

    <div class="error-msg" id="globalError"></div>

    <!-- ÉTAPE 1 : choix de la prestation -->
    <div class="panel active" data-panel="1">
              <div class="cat-title">Diagnostic</div>
                  <div class="service-card"
               data-id="1"
               data-nom="Diagnostic Peau"
               data-duree="60">
            <div>
              <div class="service-card__name">Diagnostic Peau</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">30€</div>
          </div>
                      <div class="cat-title">Soins Visage</div>
                  <div class="service-card"
               data-id="2"
               data-nom="Hydrafacial"
               data-duree="60">
            <div>
              <div class="service-card__name">Hydrafacial</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">65€</div>
          </div>
                  <div class="service-card"
               data-id="3"
               data-nom="Soin Régénérant ACS"
               data-duree="60">
            <div>
              <div class="service-card__name">Soin Régénérant ACS</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">75€</div>
          </div>
                  <div class="service-card"
               data-id="4"
               data-nom="Soin Anti-Âge &amp; Fermeté"
               data-duree="60">
            <div>
              <div class="service-card__name">Soin Anti-Âge &amp; Fermeté</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">55€</div>
          </div>
                  <div class="service-card"
               data-id="5"
               data-nom="Soin Peau Grasse"
               data-duree="60">
            <div>
              <div class="service-card__name">Soin Peau Grasse</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">55€</div>
          </div>
                  <div class="service-card"
               data-id="6"
               data-nom="Soin Peau Sèche"
               data-duree="60">
            <div>
              <div class="service-card__name">Soin Peau Sèche</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">55€</div>
          </div>
                  <div class="service-card"
               data-id="7"
               data-nom="Soin Hydratant"
               data-duree="60">
            <div>
              <div class="service-card__name">Soin Hydratant</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">55€</div>
          </div>
                  <div class="service-card"
               data-id="8"
               data-nom="Luminothérapie"
               data-duree="40">
            <div>
              <div class="service-card__name">Luminothérapie</div>
              <div class="service-card__duration">40 min</div>
            </div>
            <div class="service-card__price">25€</div>
          </div>
                  <div class="service-card"
               data-id="9"
               data-nom="Gua Sha Visage"
               data-duree="45">
            <div>
              <div class="service-card__name">Gua Sha Visage</div>
              <div class="service-card__duration">45 min</div>
            </div>
            <div class="service-card__price">Sur devis</div>
          </div>
                  <div class="service-card"
               data-id="10"
               data-nom="Ventouses Visage"
               data-duree="30">
            <div>
              <div class="service-card__name">Ventouses Visage</div>
              <div class="service-card__duration">30 min</div>
            </div>
            <div class="service-card__price">50€</div>
          </div>
                      <div class="cat-title">Drainage</div>
                  <div class="service-card"
               data-id="11"
               data-nom="Drainage Lymphatique Visage"
               data-duree="45">
            <div>
              <div class="service-card__name">Drainage Lymphatique Visage</div>
              <div class="service-card__duration">45 min</div>
            </div>
            <div class="service-card__price">50€</div>
          </div>
                  <div class="service-card"
               data-id="12"
               data-nom="Drainage Post-Opératoire Visage"
               data-duree="60">
            <div>
              <div class="service-card__name">Drainage Post-Opératoire Visage</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">70€</div>
          </div>
                  <div class="service-card"
               data-id="13"
               data-nom="Drainage Lymphatique Brésilien"
               data-duree="60">
            <div>
              <div class="service-card__name">Drainage Lymphatique Brésilien</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">70€</div>
          </div>
                  <div class="service-card"
               data-id="14"
               data-nom="Madérothérapie"
               data-duree="60">
            <div>
              <div class="service-card__name">Madérothérapie</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">70€</div>
          </div>
                      <div class="cat-title">Massages</div>
                  <div class="service-card"
               data-id="15"
               data-nom="Kobido"
               data-duree="60">
            <div>
              <div class="service-card__name">Kobido</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">80€</div>
          </div>
                  <div class="service-card"
               data-id="16"
               data-nom="Massage Suédois Californien"
               data-duree="60">
            <div>
              <div class="service-card__name">Massage Suédois Californien</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">70€</div>
          </div>
                  <div class="service-card"
               data-id="17"
               data-nom="Massage AromaTouch"
               data-duree="60">
            <div>
              <div class="service-card__name">Massage AromaTouch</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">60€</div>
          </div>
                  <div class="service-card"
               data-id="18"
               data-nom="Massage Signature"
               data-duree="60">
            <div>
              <div class="service-card__name">Massage Signature</div>
              <div class="service-card__duration">60 min</div>
            </div>
            <div class="service-card__price">65€</div>
          </div>
                  <div class="service-card"
               data-id="19"
               data-nom="Massage Bébé"
               data-duree="30">
            <div>
              <div class="service-card__name">Massage Bébé</div>
              <div class="service-card__duration">30 min</div>
            </div>
            <div class="service-card__price">50€</div>
          </div>
                  </div>

    <!-- ÉTAPE 2 : date + créneau -->
    <div class="panel" data-panel="2">
      <button class="btn-back" data-back="1">← Changer de prestation</button>
      <label for="dateInput">Choisissez une date</label>
      <input type="date" id="dateInput">
      <div id="slotsContainer"></div>
    </div>

    <!-- ÉTAPE 3 : coordonnées -->
    <div class="panel" data-panel="3">
      <button class="btn-back" data-back="2">← Changer de créneau</button>
      <div class="recap" id="recapBox"></div>
      <div class="field"><label>Nom complet</label><input type="text" id="champNom"></div>
      <div class="field"><label>Email</label><input type="email" id="champEmail"></div>
      <div class="field"><label>Téléphone</label><input type="tel" id="champTelephone"></div>
      <div class="field">
        <label>🎂 Date de naissance <span style="font-weight:500;text-transform:none;letter-spacing:0;">(facultatif)</span></label>
        <input type="date" id="champDateNaissance">
        <p style="font-size:.72rem;color:var(--burgundy-soft);margin:5px 0 0;line-height:1.4;">
          Uniquement pour vous préparer une petite surprise le jour de votre anniversaire — jamais utilisé à d'autres fins.
        </p>
      </div>
      <div class="field"><label>Message (optionnel)</label><textarea id="champNotes"></textarea></div>
      <button class="btn-primary" id="btnConfirmer">Confirmer le rendez-vous</button>
    </div>

    <!-- ÉTAPE 4 : confirmation -->
    <div class="panel" data-panel="4">
      <div class="confirm-box">
        <div class="confirm-box__icon">
          <svg width="26" height="26" viewbox="0 0 24 24" fill="none" stroke="#5C2A2A" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>
        </div>
        <h2 class="confirm-box__title">C'est confirmé !</h2>
        <p class="confirm-box__text" id="confirmText"></p>

        <div class="install-prompt" id="installPrompt">
          <p class="install-prompt__title">🌿 Envie de réserver encore plus vite ?</p>
          <p class="install-prompt__text">
            Ajoutez cette page à votre écran d'accueil, comme une vraie appli — en un geste, sans rien installer depuis un store.
            <strong>Aucun téléchargement, aucune donnée en plus collectée</strong>, juste un raccourci pratique pour la prochaine fois.
          </p>
          <button class="install-prompt__btn" id="btnInstallAndroid" style="display:none;">
            📲 Ajouter à mon écran d'accueil
          </button>
          <div class="install-prompt__ios-steps" id="stepsIos" style="display:none;">
            Sur iPhone : appuyez sur <strong>Partager</strong> <span style="font-size:1.1em;">⬆️</span> en bas de l'écran, puis sur
            <strong>« Sur l'écran d'accueil »</strong>.
          </div>
        </div>
      </div>
    </div>

  </div>

<script>
(function() {
  const state = { prestationId: null, prestationNom: '', dureeMinutes: 0, date: null, heure: null };
  let currentStep = 1;
  let deferredInstallPrompt = null;

  // Capturé tôt : Chrome/Android proposent cet évènement pour déclencher
  // l'installation par programmation plutôt que d'attendre leur bannière native.
  window.addEventListener('beforeinstallprompt', (e) => {
    e.preventDefault();
    deferredInstallPrompt = e;
  });

  function afficherPropositionInstallation() {
    const bloc = document.getElementById('installPrompt');
    const btnAndroid = document.getElementById('btnInstallAndroid');
    const stepsIos = document.getElementById('stepsIos');

    // Déjà installée en PWA ? On ne montre rien.
    if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone) {
      return;
    }

    const estIos = /iphone|ipad|ipod/i.test(navigator.userAgent);

    if (deferredInstallPrompt) {
      btnAndroid.style.display = 'inline-flex';
    } else if (estIos) {
      stepsIos.style.display = 'block';
    } else {
      return; // Ni prompt Android disponible, ni iOS -> pas de bloc à montrer
    }

    bloc.classList.add('show');
  }

  document.getElementById('btnInstallAndroid').addEventListener('click', async () => {
    if (!deferredInstallPrompt) return;
    deferredInstallPrompt.prompt();
    await deferredInstallPrompt.userChoice;
    deferredInstallPrompt = null;
    document.getElementById('btnInstallAndroid').style.display = 'none';
  });

  function goToStep(n) {
    document.querySelectorAll('[data-panel]').forEach(p => p.classList.toggle('active', p.dataset.panel == n));
    document.querySelectorAll('[data-step-dot]').forEach(d => d.classList.toggle('active', d.dataset.stepDot <= n));
    currentStep = n;
    hideError();
    if (n === 4) {
      afficherPropositionInstallation();
    }
  }

  function showError(msg) {
    const el = document.getElementById('globalError');
    el.textContent = msg;
    el.classList.add('show');
  }
  function hideError() {
    const el = document.getElementById('globalError');
    if (el) el.classList.remove('show');
  }

  // Étape 1 -> 2
  document.querySelectorAll('.service-card').forEach(card => {
    card.addEventListener('click', () => {
      document.querySelectorAll('.service-card').forEach(c => c.classList.remove('selected'));
      card.classList.add('selected');
      state.prestationId = card.dataset.id;
      state.prestationNom = card.dataset.nom;
      state.dureeMinutes = card.dataset.duree;
      goToStep(2);

      const dateInput = document.getElementById('dateInput');
      const today = new Date();
      const minDate = new Date(today.getTime() + 24*3600*1000);
      const maxDate = new Date(today.getTime() + 60*24*3600*1000);
      dateInput.min = minDate.toISOString().split('T')[0];
      dateInput.max = maxDate.toISOString().split('T')[0];
      dateInput.value = '';
      document.getElementById('slotsContainer').innerHTML = '';
    });
  });

  document.querySelectorAll('[data-back]').forEach(btn => {
    btn.addEventListener('click', () => goToStep(parseInt(btn.dataset.back)));
  });

  // Étape 2 : chargement des créneaux
  document.getElementById('dateInput').addEventListener('change', async (e) => {
    const date = e.target.value;
    if (!date) return;
    state.date = date;
    state.heure = null;

    const container = document.getElementById('slotsContainer');
    container.innerHTML = '<div class="slots-loading">Recherche des créneaux disponibles…</div>';

    try {
      const res = await fetch(`api/creneaux.php?prestation_id=${state.prestationId}&date=${date}`);
      const data = await res.json();

      if (!data.success) { showError(data.error || 'Erreur de chargement.'); container.innerHTML=''; return; }

      if (data.creneaux.length === 0) {
        container.innerHTML = '<div class="slots-empty">Aucun créneau disponible ce jour-là. Essayez une autre date.</div>';
        return;
      }

      const grid = document.createElement('div');
      grid.className = 'slots-grid';
      data.creneaux.forEach(c => {
        const btn = document.createElement('button');
        btn.className = 'slot-btn';
        btn.textContent = c.debut;
        btn.addEventListener('click', () => {
          document.querySelectorAll('.slot-btn').forEach(b => b.classList.remove('selected'));
          btn.classList.add('selected');
          state.heure = c.debut;
          state.heureFin = c.fin;
          setTimeout(() => goToStep(3), 200);
          document.getElementById('recapBox').innerHTML =
            `<strong>${state.prestationNom}</strong><br>${formatDateFr(state.date)} à ${state.heure}`;
        });
        grid.appendChild(btn);
      });
      container.innerHTML = '';
      container.appendChild(grid);
    } catch (err) {
      showError('Impossible de charger les créneaux. Vérifiez votre connexion.');
      container.innerHTML = '';
    }
  });

  function formatDateFr(iso) {
    const d = new Date(iso + 'T00:00:00');
    return d.toLocaleDateString('fr-FR', { weekday:'long', day:'numeric', month:'long' });
  }

  // Étape 3 : confirmation de réservation
  document.getElementById('btnConfirmer').addEventListener('click', async () => {
    const nom = document.getElementById('champNom').value.trim();
    const email = document.getElementById('champEmail').value.trim();
    const telephone = document.getElementById('champTelephone').value.trim();
    const dateNaissance = document.getElementById('champDateNaissance').value;
    const notes = document.getElementById('champNotes').value.trim();

    if (!nom || !email || !telephone) {
      showError('Merci de remplir votre nom, email et téléphone.');
      return;
    }

    const btn = document.getElementById('btnConfirmer');
    btn.disabled = true;
    btn.textContent = 'Envoi en cours…';

    try {
      const formData = new URLSearchParams();
      formData.append('prestation_id', state.prestationId);
      formData.append('date', state.date);
      formData.append('heure', state.heure);
      formData.append('nom', nom);
      formData.append('email', email);
      formData.append('telephone', telephone);
      formData.append('date_naissance', dateNaissance);
      formData.append('notes', notes);

      const res = await fetch('api/reserver.php', { method: 'POST', body: formData });
      const data = await res.json();

      if (!data.success) {
        showError(data.error || 'Une erreur est survenue.');
        btn.disabled = false;
        btn.textContent = 'Confirmer le rendez-vous';
        return;
      }

      document.getElementById('confirmText').innerHTML =
        `Votre rendez-vous pour <strong>${state.prestationNom}</strong> est enregistré le
         <strong>${formatDateFr(state.date)}</strong> à <strong>${data.rdv.heure_debut}</strong>.<br><br>
         Un email de confirmation vous a été envoyé à ${email}.`;
      goToStep(4);
    } catch (err) {
      showError('Impossible de finaliser la réservation. Réessayez ou contactez-nous directement.');
      btn.disabled = false;
      btn.textContent = 'Confirmer le rendez-vous';
    }
  });
})();
</script>
<script>
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('service-worker.js').catch(() => {});
  }
</script>
</body>
</html>