{"id":1654,"date":"2024-08-01T15:19:18","date_gmt":"2024-08-01T15:19:18","guid":{"rendered":"https:\/\/summithealth.sisnettdesign.com\/prendre-rendez-vous\/"},"modified":"2026-06-27T11:25:55","modified_gmt":"2026-06-27T11:25:55","slug":"prendre-rendez-vous","status":"publish","type":"page","link":"https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/","title":{"rendered":"Prendre rendez-vous"},"content":{"rendered":"\n\n<!DOCTYPE html>\n\n<html lang=\"fr\">\n  <head>\n    <meta charset=\"utf-8\" \/>\n    <meta\n      http-equiv=\"Content-Security-Policy\"\n      content=\"upgrade-insecure-requests\"\n    \/>\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1.0\"\n    \/>\n\n    <!-- Bootstrap -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/bootstrap.min.js\"><\/script>\n    <link rel=\"stylesheet\" href=\"https:\/\/cliniquevoyageur.ca\/external-js\/bootstrap.min.css\">\n    <!-- Font Awesome -->\n    <!-- <link rel=\"stylesheet\" href=\"https:\/\/cliniquevoyageur.ca\/external-js\/all.min.css\"> -->\n    <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.7.2\/css\/all.min.css\"\n      integrity=\"sha512-Evv84Mr4kqVGRNSgIGL\/F\/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+\/U6swU2Im1vVX0SVk9ABhg==\"\n      crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\" \/>\n    <!-- jQuery -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/jquery.min.js\"><\/script>\n    <!-- Semantic UI -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/semantic.min.js\"><\/script>\n    <!-- <link rel=\"stylesheet\" href=\"https:\/\/cliniquevoyageur.ca\/external-js\/semantic.min.css\"> -->\n    <link href=\"https:\/\/cdn.jsdelivr.net\/npm\/semantic-ui@2.5.0\/dist\/semantic.min.css\" rel=\"stylesheet\">\n  \n    <style>\n      .App {\n        transition: 0.3s;\n        padding-top: 10px;\n        min-height: 100vh;\n      }\n      \n      .book {\n        color: #0058a4;\n        font-family: \"Poppins\", sans-serif;\n      }\n\n      .info {\n        color: #6b655b;\n      }\n\n      .info-container {\n        border: 3px solid rgb(244, 119, 33);\n        border-radius: 10px;\n      }\n\n      .tip {\n        color: #0058a4;\n        align-self: flex-start;\n        flex: 1;\n      }\n\n      .button {\n        background-color: #1e73be;\n        border-color: #1e73be;\n        min-width: 100px;\n      }\n\n      button.light {\n        background-color: white;\n        border-color: white;\n        color: #1e73be;\n      }\n\n      .button:hover {\n        background-color: #1e73be;\n        border-color: #1e73be;\n      }\n\n      .button.light:hover {\n        background-color: white;\n        border-color: white;\n        color: #1e73be;\n      }\n\n      .map-container {\n        overflow: hidden;\n        padding-bottom: 56.25%;\n        position: relative;\n        height: 0;\n      }\n\n      .map-container iframe {\n        left: 0;\n        top: 0;\n        height: 100%;\n        width: 100%;\n        position: absolute;\n      }\n\n      #loading-spinner {\n        width: 60px;\n        height: 60px;\n        color: #1e73be;\n      }\n\n      .hl-day {\n        flex: 1;\n        margin: 0;\n      }\n\n      .hl-day .day {\n        font-size: 14px;\n      }\n\n      .day.text-muted {\n        background-color: #9e9e9e;\n      }     \n\n      .day:hover {\n        background-color: #cdcdcd; \n      }\n\n      .time-slot {\n        color: #0058a4;\n        border-color: #1e73be;\n        border-radius: 3px;\n      }\n\n      .time-slot:hover {\n        color: white;\n        border-color: white;\n        background-color: #1e73be;\n      }\n\n      .time-slot.selected {\n        color: white;\n        border-color: white;\n        background-color: #1e73be;\n      }\n\n      .map-cont {\n        border-bottom: #1e73be 2px solid;\n        margin: 20px 0px;\n      }\n\n      .scrolling-wrapper {\n        overflow-x: auto;\n        -ms-overflow-style: none;\n        scrollbar-width: none;\n        display: flex;\n      }\n\n      .scrolling-wrapper::-webkit-scrollbar {\n        display: none;\n      }\n\n      .day {\n        width: 60px;\n        height: 60px;\n      }\n\n      .entryRow {\n        box-shadow: 0 4px 8px 0 rgb(0 0 0 \/ 20%);\n        transition: 0.3s;\n        padding-top: 10px;\n      }\n\n      .locationName {\n        font-size: 25px;\n        font-weight: 500;\n      }\n\n      .bg-primary {\n        background-color: #0058a4 !important;\n      }\n\n      .chevron {\n        background-color: white;\n      }\n\n      .chevronDay {\n        background-color: #0058a4;\n      }\n\n      .rescheduleDiv {\n        background-color: #ffeea46e;\n      }\n      \n      .progress-bar {\n        background-color: #59a1ff;\n        border-radius: 10px;\n      }\n\n      .progressLabel {\n        color: \"gray\";\n        font-family: \"Poppins\", sans-serif;\n        font-size: 18px;\n      }\n\n      .service-info {\n        border-right: 1px solid #cccccc;\n        padding-right: 50px;\n      }\n\n      input[type=\"date\"] {\n        text-align: right;\n      }\n\n      input[type=\"date\"]:before {\n        color: gray;\n        content: attr(placeholder) !important;\n        margin-right: 0.5em;\n      }\n\n      .input-error {\n        border: 2px solid rgb(214, 0, 0);\n        border-radius: 4px;\n      }\n\n      .input-container {\n        display: flex;\n        flex-direction: column;\n        align-items: flex-start;\n        width: 100%;\n      }\n\n      .input-wrapper {\n        width: 100%;\n        position: relative;\n      }\n\n      .error-message {\n        color: rgb(214, 0, 0);\n        font-size: 14px;\n        display: inline-block;       \n        white-space: nowrap;         \n        margin-top: 0 !important;\n      }\n\n      .error-border select {\n        border: 2px solid rgb(214, 0, 0) !important;\n      }\n\n      .date-input {\n        width: 100%;\n        margin-bottom: 0; \n        padding-bottom: 0;\n      }\n\n      [id^=\"date\"] {\n        background-color: #fafafa !important;\n      }\n\n      .chip {\n        display: inline-block;\n        padding: 0px 10px;\n        font-size: 14px;\n        line-height: 30px;\n        border-radius: 25px;\n        background-color: #fafafa;\n        margin-right: 10px;\n        margin-bottom: 5px;\n        align-items: center;\n      }\n\n      .closebtn {\n        padding-left: 10px;\n        color: #0058a4;\n        font-weight: bold;\n        float: right;\n        font-size: 20px;\n        cursor: pointer;\n      }\n\n      .closebtn:hover {\n        color: #59a1ff;\n      }\n\n      .birthdate-col {\n        padding-left: 2px;\n        padding-right: 2px;\n      }\n\n      .birthdate-col-dd {\n        padding-right: 2px;\n      }\n\n      @media screen and (max-width: 500px) {\n        .hl-day {\n          padding: 0 5px;\n          margin: 0;\n        }\n\n        .day {\n          width: 42px;\n          height: 42px;\n        }\n\n        .day-font {\n          font-size: 13px;\n        }\n\n        .progressLabel {\n          font-size: small;\n        }\n\n        .birthdate-col,\n        .birthdate-col-dd {\n          margin-bottom: 8px !important; \n          width: 100px; \n        }\n      }\n      @media screen and (max-width: 767px) {\n        .service-info {\n          border: 0;\n          padding-right: 0;\n        }\n\n        .birthdate-col,\n        .birthdate-col-dd {\n          margin-bottom: 8px !important; \n          width: 100px; \n        }\n      }\n      \/* Terms & Conditions Modal Styles *\/\n      .terms-modal-popup {\n        max-width: 95% !important;\n      }\n      \n      .terms-modal-container {\n        padding: 0 !important;\n        margin: 0 !important;\n        overflow: hidden;\n      }\n      \n      .terms-modal-container iframe {\n        width: 100% !important;\n        max-width: 100% !important;\n        height: 70vh !important;\n        min-height: 400px !important;\n        border: none !important;\n        border-radius: 8px;\n      }\n      \n      @media (max-width: 768px) {\n        .terms-modal-popup {\n          width: 95% !important;\n          max-width: 95% !important;\n        }\n        \n        .terms-modal-container iframe {\n          height: 60vh !important;\n          min-height: 300px !important;\n        }\n      }\n      .grecaptcha-badge {\n        right: 8px !important;\n        bottom: 8px !important;\n        max-width: calc(100vw - 16px);\n        transform-origin: right bottom;\n      }\n\n      @media screen and (max-width: 767px) {\n        .grecaptcha-badge {\n          transform: scale(0.82);\n        }\n      }\n      input.form-control[aria-hidden=\"true\"] {\n        box-sizing: border-box;\n        left: 0;\n        max-width: 100%;\n      }\n    <\/style>\n  <script>\n(function (root, factory) {\n  var api = factory(root);\n\n  if (typeof module === \"object\" && module.exports) {\n    module.exports = api;\n  }\n\n  root.SummitQuestionnaireV2 = api;\n\n  \/\/ Expose helper functions as globals so legacy HTML call sites that reference them\n  \/\/ by bare name (e.g. `resolvePublicApiBase(...)`) keep working after the helper is\n  \/\/ inlined into the page. Without this, the IIFE only exposes the api object via\n  \/\/ `window.SummitQuestionnaireV2`, which causes a ReferenceError in code paths that\n  \/\/ do not use the defensive `window.SummitQuestionnaireV2 && ...` pattern.\n  if (api && typeof api === \"object\") {\n    var _helperExports = [\n      \"resolveStage\",\n      \"resolveApiBase\",\n      \"resolvePublicApiBase\",\n      \"rewriteUrl\",\n      \"rewriteLegacyUrl\",\n      \"rewriteManagedTargets\",\n      \"attachManagedForms\",\n    ];\n    for (var _i = 0; _i < _helperExports.length; _i++) {\n      var _name = _helperExports[_i];\n      if (typeof api[_name] === \"function\" &#038;&#038; typeof root[_name] !== \"function\") {\n        root[_name] = api[_name];\n      }\n    }\n  }\n})(typeof globalThis !== \"undefined\" ? globalThis : this, function (root) {\n  var PROD_BASE = \"https:\/\/api.ascentemr.com\/v2\";\n  var DEV_BASE = \"https:\/\/dev.ascentemr.com\/v2\";\n  var STAGING_BASE = \"https:\/\/staging.ascentemr.com\/v2\";\n  var LOCAL_BASE = \"http:\/\/localhost:4010\/dev\/v2\";\n  var DOM_READY_EVENTS = [\"DOMContentLoaded\", \"load\"];\n\n  function asSearchParams(input) {\n    if (typeof URLSearchParams === \"undefined\") {\n      return {\n        get: function () {\n          return null;\n        },\n      };\n    }\n\n    if (!input &#038;&#038; root.location &#038;&#038; typeof root.location.search === \"string\") {\n      return new URLSearchParams(root.location.search);\n    }\n\n    if (input instanceof URLSearchParams) {\n      return input;\n    }\n\n    if (typeof input === \"string\") {\n      return new URLSearchParams(input.charAt(0) === \"?\" ? input.slice(1) : input);\n    }\n\n    if (input &#038;&#038; typeof input === \"object\" &#038;&#038; typeof input.toString === \"function\") {\n      return new URLSearchParams(String(input));\n    }\n\n    return new URLSearchParams(\"\");\n  }\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function currentOrigin() {\n    if (root.location &#038;&#038; typeof root.location.origin === \"string\" &#038;&#038; root.location.origin) {\n      return root.location.origin.replace(\/\\\/$\/, \"\");\n    }\n    if (typeof URL !== \"undefined\" &#038;&#038; root.location &#038;&#038; typeof root.location.href === \"string\") {\n      try {\n        return new URL(root.location.href).origin.replace(\/\\\/$\/, \"\");\n      } catch (_error) {}\n    }\n    return \"\";\n  }\n\n  function canUseSameOriginApi() {\n    var hostname = normalize(root.location &#038;&#038; root.location.hostname);\n    if (!hostname) {\n      return false;\n    }\n\n    return (\n      hostname === \"localhost\" ||\n      hostname === \"127.0.0.1\" ||\n      hostname === \"::1\" ||\n      hostname === \"dev.ascent.summithealth.ca\" ||\n      hostname === \"ascent.summithealth.ca\" ||\n      hostname.indexOf(\".vercel.app\") >= 0\n    );\n  }\n\n  function isProductionWordpressHost() {\n    var hostname = normalize(root.location && root.location.hostname);\n    if (!hostname) {\n      return false;\n    }\n    if (\n      hostname.indexOf(\"dev.\") === 0 ||\n      hostname.indexOf(\"dev-\") === 0 ||\n      hostname.indexOf(\"staging.\") === 0 ||\n      hostname.indexOf(\"staging-\") === 0 ||\n      hostname.indexOf(\".staging.\") >= 0 ||\n      hostname.indexOf(\".vercel.app\") >= 0\n    ) {\n      return false;\n    }\n\n    return (\n      hostname === \"dormalab.com\" ||\n      hostname === \"www.dormalab.com\" ||\n      hostname === \"summithealth.ca\" ||\n      hostname === \"www.summithealth.ca\" ||\n      hostname === \"cliniquevoyageur.ca\" ||\n      hostname === \"www.cliniquevoyageur.ca\"\n    );\n  }\n\n  function resolveStage(searchLike) {\n    var params = asSearchParams(searchLike);\n    var requestedStage = normalize(params.get(\"stage\"));\n    var testMode = normalize(params.get(\"test\"));\n    var hostname = normalize(root.location && root.location.hostname);\n\n    if (requestedStage === \"local\" || testMode === \"true\") {\n      return \"local\";\n    }\n    if (requestedStage === \"dev\") {\n      return \"dev\";\n    }\n    if (requestedStage === \"staging\") {\n      return \"staging\";\n    }\n    if (requestedStage === \"prod\" || requestedStage === \"production\") {\n      return \"prod\";\n    }\n\n    if (!hostname || hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"::1\") {\n      return \"local\";\n    }\n    if (\n      hostname === \"staging.ascentemr.com\" ||\n      hostname.indexOf(\"staging.\") === 0 ||\n      hostname.indexOf(\"staging-\") === 0 ||\n      hostname.indexOf(\".staging.\") >= 0\n    ) {\n      return \"staging\";\n    }\n    if (\n      hostname === \"dev.ascentemr.com\" ||\n      hostname === \"dev.ascent.summithealth.ca\" ||\n      hostname.indexOf(\"dev.\") === 0 ||\n      hostname.indexOf(\"dev-\") === 0 ||\n      hostname.indexOf(\".vercel.app\") >= 0\n    ) {\n      return \"dev\";\n    }\n\n    return \"prod\";\n  }\n\n  function resolveApiBase(searchLike) {\n    var stage = resolveStage(searchLike);\n\n    if (isProductionWordpressHost()) {\n      return PROD_BASE;\n    }\n    if (canUseSameOriginApi()) {\n      return currentOrigin() + \"\/api\/v2\";\n    }\n    if (stage === \"local\") {\n      return LOCAL_BASE;\n    }\n    if (stage === \"dev\") {\n      return DEV_BASE;\n    }\n    if (stage === \"staging\") {\n      return STAGING_BASE;\n    }\n\n    return currentOrigin() ? currentOrigin() + \"\/api\/v2\" : DEV_BASE;\n  }\n\n  function resolvePublicApiBase(searchLike) {\n    return resolveApiBase(searchLike).replace(\/\\\/$\/, \"\") + \"\/public\";\n  }\n\n  function startsWith(value, prefix) {\n    return String(value || \"\").slice(0, prefix.length) === prefix;\n  }\n\n  function isAscentApiHost(host) {\n    var normalizedHost = normalize(host);\n    return (\n      normalizedHost === \"api.ascentemr.com\" ||\n      normalizedHost === \"dev.ascentemr.com\" ||\n      normalizedHost === \"staging.ascentemr.com\" ||\n      normalizedHost === \"api.summittravelhealthdev.com\" ||\n      normalizedHost === \"dev.summittravelhealthdev.com\" ||\n      normalizedHost === \"staging.summittravelhealthdev.com\" ||\n      normalizedHost.indexOf(\"localhost:3000\") === 0 ||\n      normalizedHost.indexOf(\"127.0.0.1:3000\") === 0 ||\n      normalizedHost.indexOf(\"localhost:4010\") === 0 ||\n      normalizedHost.indexOf(\"127.0.0.1:4010\") === 0\n    );\n  }\n\n  function normalizePublicPath(pathname) {\n    var normalized = String(pathname || \"\");\n    if (!normalized) {\n      return \"\/public\";\n    }\n\n    if (startsWith(normalized, \"\/public\")) {\n      return normalized;\n    }\n    if (startsWith(normalized, \"\/v2\/public\")) {\n      return normalized.slice(3);\n    }\n    if (startsWith(normalized, \"\/dev\/v2\/public\")) {\n      return normalized.slice(7);\n    }\n    if (startsWith(normalized, \"\/dev\/v2\/\")) {\n      return \"\/public\" + normalized.slice(7);\n    }\n    if (startsWith(normalized, \"\/dev\")) {\n      return \"\/public\" + normalized.slice(4);\n    }\n    if (startsWith(normalized, \"\/v2\/\")) {\n      return \"\/public\" + normalized.slice(3);\n    }\n\n    return normalized;\n  }\n\n  function shouldRewriteUrl(parsed, rawUrl) {\n    var pathname = String(parsed && parsed.pathname || \"\");\n    if (\n      startsWith(pathname, \"\/public\") ||\n      startsWith(pathname, \"\/v2\/public\") ||\n      startsWith(pathname, \"\/dev\/v2\/public\") ||\n      startsWith(pathname, \"\/dev\") ||\n      startsWith(pathname, \"\/v2\/\")\n    ) {\n      var rawValue = String(rawUrl || \"\");\n      var isRelative =\n        startsWith(rawValue, \"\/\") ||\n        startsWith(rawValue, \".\/\") ||\n        startsWith(rawValue, \"..\/\");\n      return isRelative || isAscentApiHost(parsed.host) || normalize(parsed.host) === normalize(root.location && root.location.host);\n    }\n\n    return false;\n  }\n\n  function rewriteUrl(rawUrl, searchLike) {\n    var value = String(rawUrl || \"\");\n    if (!value || typeof URL === \"undefined\") {\n      return value;\n    }\n\n    try {\n      var parsed = new URL(value, root.location && root.location.href ? root.location.href : undefined);\n      if (!shouldRewriteUrl(parsed, value)) {\n        return value;\n      }\n\n      var apiBase = resolveApiBase(searchLike);\n      var normalizedPath = normalizePublicPath(parsed.pathname);\n\n      return apiBase.replace(\/\\\/$\/, \"\") + normalizedPath + parsed.search + parsed.hash;\n    } catch (_error) {\n      return value;\n    }\n  }\n\n  function patchFetch() {\n    if (typeof root.fetch !== \"function\" || root.fetch.__summitQuestionnaireV2Patched) {\n      return;\n    }\n\n    var originalFetch = root.fetch.bind(root);\n\n    var patchedFetch = function (input, init) {\n      if (typeof Request !== \"undefined\" && input instanceof Request) {\n        var rewrittenRequestUrl = rewriteUrl(input.url);\n        if (rewrittenRequestUrl !== input.url) {\n          input = new Request(rewrittenRequestUrl, input);\n        }\n      } else if (typeof input === \"string\" || (typeof URL !== \"undefined\" && input instanceof URL)) {\n        input = rewriteUrl(String(input));\n      }\n\n      return originalFetch(input, init);\n    };\n\n    patchedFetch.__summitQuestionnaireV2Patched = true;\n    root.fetch = patchedFetch;\n  }\n\n  function patchXmlHttpRequest() {\n    if (\n      typeof XMLHttpRequest === \"undefined\" ||\n      !XMLHttpRequest.prototype ||\n      XMLHttpRequest.prototype.open.__summitQuestionnaireV2Patched\n    ) {\n      return;\n    }\n\n    var originalOpen = XMLHttpRequest.prototype.open;\n    XMLHttpRequest.prototype.open = function (method, url) {\n      var rewrittenUrl = rewriteUrl(url);\n      return originalOpen.apply(this, [method, rewrittenUrl].concat([].slice.call(arguments, 2)));\n    };\n    XMLHttpRequest.prototype.open.__summitQuestionnaireV2Patched = true;\n  }\n\n  function rewriteManagedTargets(documentLike) {\n    var doc = documentLike || root.document;\n    if (!doc || typeof doc.querySelectorAll !== \"function\") {\n      return;\n    }\n\n    doc.querySelectorAll(\"form[action], a[href]\").forEach(function (element) {\n      var attributeName = element.tagName === \"FORM\" ? \"action\" : \"href\";\n      var currentValue = element.getAttribute(attributeName);\n      var rewrittenValue = rewriteUrl(currentValue);\n      if (rewrittenValue && rewrittenValue !== currentValue) {\n        element.setAttribute(attributeName, rewrittenValue);\n      }\n    });\n  }\n\n  function getRecaptchaSiteKey(form) {\n    if (!form || !form.querySelector) {\n      return null;\n    }\n\n    var localWidget = form.querySelector(\".g-recaptcha[data-sitekey]\");\n    if (localWidget) {\n      return localWidget.getAttribute(\"data-sitekey\");\n    }\n\n    var globalWidget =\n      root.document && root.document.querySelector(\".g-recaptcha[data-sitekey]\");\n    return globalWidget ? globalWidget.getAttribute(\"data-sitekey\") : null;\n  }\n\n  function collectFormPayload(form) {\n    var payload = {};\n    var formData = new FormData(form);\n\n    formData.forEach(function (value, key) {\n      if (payload[key] !== undefined) {\n        if (!Array.isArray(payload[key])) {\n          payload[key] = [payload[key]];\n        }\n        payload[key].push(value);\n        return;\n      }\n\n      payload[key] = value;\n    });\n\n    payload.formKey = form.getAttribute(\"data-v2-form-key\") || \"\";\n    payload.pageUrl = root.location ? root.location.href : \"\";\n    payload.recaptchaToken =\n      payload.recaptchaToken ||\n      payload[\"g-recaptcha-response\"] ||\n      \"\";\n    payload.recaptchaSiteKey = payload.recaptchaSiteKey || getRecaptchaSiteKey(form);\n\n    return payload;\n  }\n\n  function ensureStatusElement(form, kind) {\n    var attributeName = kind === \"error\" ? \"data-v2-form-error\" : \"data-v2-form-success\";\n    var existing = form.querySelector(\"[\" + attributeName + \"]\");\n    if (existing) {\n      return existing;\n    }\n\n    var element = root.document.createElement(\"div\");\n    element.setAttribute(attributeName, \"true\");\n    element.style.display = \"none\";\n    element.style.marginBottom = \"12px\";\n    element.style.padding = \"12px\";\n    element.style.borderRadius = \"4px\";\n    element.style.fontSize = \"14px\";\n    element.style.background = kind === \"error\" ? \"#fbe9e7\" : \"#e8f5e9\";\n    element.style.color = kind === \"error\" ? \"#b71c1c\" : \"#1b5e20\";\n    form.insertBefore(element, form.firstChild);\n    return element;\n  }\n\n  function setStatusMessage(form, kind, message) {\n    var element = ensureStatusElement(form, kind);\n    element.textContent = message || \"\";\n    element.style.display = message ? \"block\" : \"none\";\n  }\n\n  function disableSubmitButtons(form, disabled) {\n    var buttons = form.querySelectorAll(\"button[type=submit], input[type=submit]\");\n    buttons.forEach(function (button) {\n      button.disabled = Boolean(disabled);\n    });\n  }\n\n  async function submitManagedForm(form) {\n    var payload = collectFormPayload(form);\n    var action = rewriteUrl(form.getAttribute(\"action\"));\n    var response = await root.fetch(action, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application\/json\",\n      },\n      body: JSON.stringify(payload),\n    });\n\n    var contentType = String(response.headers && response.headers.get && response.headers.get(\"content-type\") || \"\");\n    var data =\n      contentType.indexOf(\"application\/json\") >= 0\n        ? await response.json()\n        : { success: response.ok, message: await response.text() };\n\n    if (!response.ok || data.success === false) {\n      throw new Error(data.error || data.message || \"Form submission failed.\");\n    }\n\n    return data;\n  }\n\n  function attachFormHandler(form) {\n    if (!form || form.__summitQuestionnaireV2Attached) {\n      return;\n    }\n\n    form.__summitQuestionnaireV2Attached = true;\n    form.addEventListener(\"submit\", async function (event) {\n      event.preventDefault();\n\n      if (form.__summitQuestionnaireV2Submitting) {\n        return;\n      }\n\n      if (typeof form.checkValidity === \"function\" && !form.checkValidity()) {\n        if (typeof form.reportValidity === \"function\") {\n          form.reportValidity();\n        }\n        return;\n      }\n\n      form.__summitQuestionnaireV2Submitting = true;\n      setStatusMessage(form, \"error\", \"\");\n      setStatusMessage(form, \"success\", \"\");\n      disableSubmitButtons(form, true);\n\n      try {\n        var result = await submitManagedForm(form);\n        var redirectUrl = result.redirectUrl || collectFormPayload(form).retURL;\n\n        if (redirectUrl && root.location && typeof root.location.assign === \"function\") {\n          root.location.assign(redirectUrl);\n          return;\n        }\n\n        setStatusMessage(form, \"success\", \"Votre formulaire a ete soumis avec succes.\");\n      } catch (error) {\n        setStatusMessage(form, \"error\", error && error.message ? error.message : \"Le formulaire n'a pas pu etre soumis.\");\n        if (root.console && typeof root.console.error === \"function\") {\n          root.console.error(\"SummitQuestionnaireV2 form submission failed\", error);\n        }\n      } finally {\n        disableSubmitButtons(form, false);\n        form.__summitQuestionnaireV2Submitting = false;\n      }\n    });\n  }\n\n  function attachManagedForms(documentLike) {\n    var doc = documentLike || root.document;\n    if (!doc || typeof doc.querySelectorAll !== \"function\") {\n      return;\n    }\n\n    var managedForms = doc.querySelectorAll(\"form[data-v2-form-key]\");\n    managedForms.forEach(attachFormHandler);\n  }\n\n  function bootstrap() {\n    patchFetch();\n    patchXmlHttpRequest();\n\n    if (!root.document || typeof root.document.addEventListener !== \"function\") {\n      return;\n    }\n\n    rewriteManagedTargets(root.document);\n    attachManagedForms(root.document);\n    DOM_READY_EVENTS.forEach(function (eventName) {\n      root.document.addEventListener(eventName, function () {\n        rewriteManagedTargets(root.document);\n        attachManagedForms(root.document);\n      });\n    });\n\n    root.document.addEventListener(\"submit\", function (event) {\n      var form = event && event.target;\n      if (form && typeof form.getAttribute === \"function\") {\n        var currentAction = form.getAttribute(\"action\");\n        var rewrittenAction = rewriteUrl(currentAction);\n        if (rewrittenAction && rewrittenAction !== currentAction) {\n          form.setAttribute(\"action\", rewrittenAction);\n        }\n      }\n    }, true);\n  }\n\n  bootstrap();\n\n  return {\n    resolveStage: resolveStage,\n    resolveApiBase: resolveApiBase,\n    resolvePublicApiBase: resolvePublicApiBase,\n    rewriteUrl: rewriteUrl,\n    rewriteLegacyUrl: rewriteUrl,\n    rewriteManagedTargets: rewriteManagedTargets,\n    attachManagedForms: attachManagedForms,\n    bootstrap: bootstrap,\n  };\n});\n<\/script>\n<\/head>\n\n  <body>\n    <div id=\"book-form\"><\/div>\n    <!-- Google Maps API -->\n    <script type=\"text\/javascript\" src=\"https:\/\/maps.google.com\/maps\/api\/js?libraries=places&#038;language=en-US&#038;key=AIzaSyDFO90WdM-sPpr--JJQlP3PikKyuFvfwfc\"><\/script>\n    <!-- Babel Standalone -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/babel.min.js\"><\/script>\n    <!-- React & ReactDOM -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/react.production.min.js\"><\/script>\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/react-dom.production.min.js\"><\/script>\n    <!-- React Bootstrap -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/react-bootstrap.min.js\"><\/script>\n    <!-- React Modal -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/react-modal.min.js\"><\/script>\n    <!-- SweetAlert2 -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/sweetalert2.all.min.js\"><\/script>\n    <link rel=\"stylesheet\" href=\"https:\/\/cliniquevoyageur.ca\/external-js\/sweetalert2.min.css\">\n    <!-- Moment.js -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/moment.min.js\"><\/script>\n    <!-- Lodash -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/lodash.min.js\"><\/script>\n    <!-- Google reCAPTCHA -->\n    <script src=\"https:\/\/www.google.com\/recaptcha\/api.js?render=6Lc_N-ccAAAAAIk6kJA_6lrea6jnEW7rcO8gKMOZ\"><\/script>\n    <!-- Semantic UI Calendar React -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/semantic-ui-calendar-react.js\"><\/script>\n    <!-- Moment Timezone -->\n    <script src=\"https:\/\/cliniquevoyageur.ca\/external-js\/moment-timezone-with-data.min.js\"><\/script>\n\n    <script type=\"text\/babel\">\n      let NO_BYPASSED_DAYS = 0;\n      const urlParams = new URLSearchParams(window.location.search);\n      const resolvePublicApiBaseUrl = () => {\n        return resolvePublicApiBase(window.location.search).replace(\/\\\/$\/, \"\") + \"\/\";\n      };\n      const BASE_URL = resolvePublicApiBaseUrl();\n      const PROD_BASE_URL = BASE_URL;\n      const DEV_BASE_URL = BASE_URL;\n\n      const get = async (url, params) => {\n        const formattedParams = new URLSearchParams(params);\n        const res = await fetch(BASE_URL + url + formattedParams);\n        const data = await res.json();\n        return data;\n      };\n\n      const post = async (url, params, extraHeaders = {}) => {\n        try {\n          const res = await fetch(BASE_URL + url, {\n            method: \"POST\",\n            headers: {\n              \"Content-Type\": \"application\/json\",\n              ...extraHeaders,\n            },\n            body: JSON.stringify(params),\n          });\n          \n          let data;\n          try {\n            data = await res.json();\n          } catch (parseError) {\n            \/\/ If JSON parsing fails, create error structure\n            if (!res.ok || res.status >= 400) {\n              return {\n                errorCode: res.status.toString(),\n                errorMessage: `Erreur serveur (${res.status}): Impossible d'analyser la r\u00e9ponse`\n              };\n            }\n            throw parseError;\n          }\n          \n          \/\/ If HTTP status indicates an error, throw or return error structure\n          if (!res.ok || res.status >= 400) {\n            \/\/ If data already has error structure, return it\n            if (data.errorCode || data.errorMessage) {\n              return data;\n            }\n            \/\/ Otherwise create error structure\n            return {\n              errorCode: res.status.toString(),\n              errorMessage: data.message || data.error || `Erreur serveur (${res.status})`\n            };\n          }\n          \n          return data;\n        } catch (fetchError) {\n          \/\/ Handle network errors, CORS errors, etc.\n          console.error(\"Network error in post request:\", fetchError);\n          \/\/ Return error structure instead of throwing\n          return {\n            errorCode: \"NETWORK_ERROR\",\n            errorMessage: fetchError.message || \"Erreur r\u00e9seau. Veuillez v\u00e9rifier votre connexion et r\u00e9essayer.\"\n          };\n        }\n      };\n\n      const getServiceForOnlineBooking = async (params) => {\n        return get(\"calendar\/services?\", params);\n      };\n\n      const getLocations = (params) => {\n        return get(\"calendar\/locations?\", params);\n      };\n\n      const getAvailabilities = (params) => {\n        return post(\"calendar\/availabilities\", params);\n      };\n\n      const AVAILABILITY_DATE_FORMAT = \"D_M_YYYY\";\n      const isAvailabilityDateString = (date) =>\n        typeof date === \"string\" && \/^\\d{1,2}_\\d{1,2}_\\d{4}$\/.test(date);\n      const formatAvailabilityDateForApi = (date) => {\n        if (!date || isAvailabilityDateString(date)) {\n          return date;\n        }\n        const parsedDate = moment(date);\n        return parsedDate.isValid() ? parsedDate.format(AVAILABILITY_DATE_FORMAT) : date;\n      };\n\n      const getVirtualAvailabilities = (params) => {\n        return post(\"calendar\/virtual-availabilities\", params);\n      };\n\n      const book = (params) => {\n        const idempotencyKey = params && params.requestId;\n        return post(\"calendar\/book\", params, idempotencyKey ? { \"Idempotency-Key\": idempotencyKey } : {});\n      };\n\n      const getAppointment = (params) => {\n        return get(`appointment\/${params.id}?`, params);\n      };\n\n      const rescheduleAppointment = (params) => {\n        return post(`appointment\/${params.id}\/rescheduleAppointment`, params);\n      };\n\n      const checkDuplicateAppointments = (params) => {\n        return post(`user\/checkDuplicateAppointments?`, params);\n      };\n\n      const validatePromoCode = (params) => {\n        return post(\"promo-codes\/validate\", params);\n      };\n\n      const getCountries = (params) => {\n        return get(\"questionnaire\/countries\", params);\n      };\n\n      const getVaccines = (params) => {\n        return get(\"calendar\/vaccines\", params);\n      };\n\n      const { React, ReactBootstrap } = window;\n      const {\n        useState,\n        useRef,\n        useEffect,\n        useCallback,\n        useMemo,\n        useLayoutEffect,\n      } = React;\n      const {\n        Form,\n        Col,\n        Row,\n        Card,\n        Button,\n        Container,\n        Spinner,\n        Image,\n        ProgressBar,\n        Tab,\n        Tabs,\n      } = ReactBootstrap;\n\n      const debounce = _.debounce;\n\n      const useRecaptcha = (action, params) => {\n        return new Promise((resolve, reject) => {\n          \/\/ Check if grecaptcha is available\n          if (typeof grecaptcha === 'undefined') {\n            console.log(\"reCAPTCHA not available, bypassing...\");\n            \/\/ Call the action directly without reCAPTCHA\n            action(params).then(resolve).catch(reject);\n            return;\n          }\n          \n          grecaptcha.ready(async () => {\n            try {\n              const recaptchaToken = await grecaptcha.execute(\n                \"6Lc_N-ccAAAAAIk6kJA_6lrea6jnEW7rcO8gKMOZ\",\n                {\n                  action: \"book\",\n                }\n              );\n              try {\n                const response = await action({\n                  recaptchaToken,\n                  ...params,\n                });\n                if (!response.errorCode) {\n                  resolve(response);\n                } else {\n                  reject(response);\n                }\n              } catch (e) {\n                reject(e);\n              }\n            } catch (e) {\n              console.log(\"reCAPTCHA error, bypassing...\", e);\n              \/\/ Fallback: call action without reCAPTCHA\n              action(params).then(resolve).catch(reject);\n            }\n          });\n        });\n      };\n\n      const getAddressAttribute = (\n        loc,\n        param,\n        defaultValue = \"\",\n        long = true\n      ) => {\n        if (loc && loc.address_components) {\n          const component = loc.address_components.find((component) =>\n            component.types.includes(param)\n          );\n          if (component) {\n            return long ? component.long_name : component.short_name;\n          }\n        }\n        return defaultValue;\n      };\n\n      const getFormattedAddress = (address) => {\n        return `${address.street}, ${address.apartment}, ${address.city}, ${address.province}`;\n      };\n\n      const validateCard = (cardNumber) => {\n        \/\/ Remove any spaces or dashes from the card number\n        cardNumber = cardNumber.replace(\/\\s+|-\/g, \"\");\n\n        \/\/ Check if the card number is the correct length\n        if (cardNumber.length < 13 || cardNumber.length > 19) {\n          return false;\n        }\n\n        \/\/ Use the Luhn algorithm to check the validity of the card number\n        let sum = 0;\n        let isEven = false;\n        for (let i = cardNumber.length - 1; i >= 0; i--) {\n          let digit = parseInt(cardNumber.charAt(i));\n          if (isEven) {\n            digit *= 2;\n            if (digit > 9) {\n              digit -= 9;\n            }\n          }\n          sum += digit;\n          isEven = !isEven;\n        }\n\n        return sum % 10 === 0;\n      };\n\n      var removeBookedSlot = function (slotJson) {\n        if (slotJson == null) return [];\n        for (var k = 0; k < slotJson.length; k++) {\n          var avaiableSlot = [];\n          var selectedSlots = slotJson[k].slots;\n          var bookedSlot = {};\n          if (selectedSlots != null) {\n            for (var index in selectedSlots) {\n              if (selectedSlots[index].isBookedSlot)\n                bookedSlot[selectedSlots[index].slot] = true;\n            }\n            for (var index in selectedSlots) {\n              if (\n                selectedSlots[index].slot &#038;&#038;\n                !selectedSlots[index].isBookedSlot\n              ) {\n                var isSlotAvailable = true;\n                for (var blockedindex in bookedSlot) {\n                  var eventStart, eventEnd, slotStart, slotEnd;\n                  var slotTime = selectedSlots[index].slot.split(\"_\");\n                  var eventTime = blockedindex.split(\"_\");\n                  if (\n                    !isSlotAvailableforBooking(\n                      eventTime[0],\n                      eventTime[1],\n                      slotTime[0],\n                      slotTime[1]\n                    )\n                  ) {\n                    isSlotAvailable = false;\n                    break;\n                  }\n                }\n                if (!isSlotAvailable) {\n                  continue;\n                }\n                avaiableSlot.push(selectedSlots[index]);\n              }\n            }\n          }\n          slotJson[k].slots = avaiableSlot;\n        }\n        return slotJson;\n      };\n      \/\/ Additional method,\n      var getTimeValue = function (e) {\n          var t = 0,\n            a = e.split(\" \"),\n            i = a[0].split(\":\");\n          return (\n            \"PM\" == a[1] &#038;&#038; \"12\" != i[0]\n              ? (t += 720)\n              : \"AM\" == a[1] &#038;&#038; \"12\" == i[0] &#038;&#038; (i[0] = 0),\n            (t += 60 * parseInt(i[0])),\n            (t += parseInt(i[1]))\n          );\n        },\n        isSlotAvailableforBooking = function (e, t, a, i) {\n          var l = getTimeValue(e),\n            r = getTimeValue(t),\n            n = getTimeValue(a),\n            u = getTimeValue(i);\n          return (\n            \"12:00 AM\" == t &#038;&#038; (r = 1440),\n            !(\n              (l < n &#038;&#038; r > n) ||\n              (l < u &#038;&#038; r > u) ||\n              (l < n &#038;&#038; r > u) ||\n              (l >= n && r <= u)\n            )\n          );\n        };\n\n      const getMergedDates = (availableExpert) => {\n        let dates = {};\n        let experts = {};\n        for (let key in availableExpert) {\n          let expert = availableExpert[key];\n          expert = removeBookedSlot(expert);\n          experts[key] = expert;\n        }\n        if (availableExpert) {\n          let mergedExperts = _(experts).toArray().flatten().value();\n          mergedExperts.forEach((value) => {\n            const existingSlots = dates[value.strDate]\n              ? dates[value.strDate].slots\n              : [];\n            dates[value.strDate] = {\n              ...value,\n              slots: [...existingSlots, ...value.slots],\n            };\n          });\n        }\n        return dates;\n      };\n\n      const weekDays = [\"Dim\", \"Lun\", \"Mar\", \"Mer\", \"Jeu\", \"Ven\", \"Sam\"];\n\n      const DatePicker = ({\n        selectedDate,\n        onChange,\n        showDateModal,\n        minDate,\n        maxDate,\n        disableNavigation,\n      }) => {\n        const [showDate, setShowDate] = useState(new Date(selectedDate));\n        const [startDate, setStartDate] = useState(new Date()); \n        const containerRef = useRef(null);\n        const currentDayRef = useRef();\n        const selectedDateRef = useRef(selectedDate);\n        const [initialized, setInitialized] = useState(false);\n\n        const shouldBypassDates = minDate || maxDate;\n\n        const date = new Date();\n        const now = new Date(\n          date.getFullYear(),\n          date.getMonth(),\n          date.getDate()\n        );\n\n        useLayoutEffect(() => {\n          if (currentDayRef.current) {\n            requestAnimationFrame(() => {\n              currentDayRef.current.scrollIntoView({\n                inline: \"center\",\n                block: \"nearest\",\n                behavior: \"smooth\",\n              });\n            });\n          }\n          if (selectedDateRef.current !== selectedDate) {\n            setShowDate(new Date(selectedDate));\n            selectedDateRef.current = selectedDate;\n          }\n        }, [currentDayRef.current, selectedDate]);\n\n        \/\/ first day of the month, CAREFULL: object will change by for loop!\n        const firstDayThisMonth = new Date(\n          showDate.getFullYear(),\n          showDate.getMonth(),\n          1\n        );\n\n        \/\/ getDay sunday=0 and we monday=0\n        const dayOfWeek = (firstDayThisMonth.getDay() + 6) % 7;\n\n        \/\/ first day of next month\n        const firstDayNextMonth = new Date(\n          showDate.getFullYear(),\n          showDate.getMonth() + 1,\n          1\n        );\n\n        \/\/ useEffect(() => {\n        \/\/   if(initialTBDateTime){\n        \/\/     if (minDate) {\n        \/\/       setStartDate(minDate);\n        \/\/     }\n        \/\/   } else {\n        \/\/     if (minDate) {\n        \/\/       setStartDate(minDate);\n        \/\/       setShowDate(minDate);\n        \/\/     }\n        \/\/   }\n        \/\/ }, [minDate, initialTBDateTime]);\n\n        useEffect(() => {\n          if (!initialized && minDate) {\n            setStartDate(minDate);\n            setShowDate(minDate);\n            onChange(minDate); \n            setInitialized(true);\n          }\n        }, [minDate, initialized]);\n\n        const isPastDate = (d) => {\n          return (\n            d.getDate() < now.getDate() &#038;&#038;\n            d.getMonth() === now.getMonth() &#038;&#038;\n            d.getFullYear() === now.getFullYear()\n          );\n        };\n\n        const chunkSize = 11;\n        const totalDays = moment(maxDate).diff(moment(minDate), 'days') + 1;\n        const canScroll = totalDays > chunkSize;\n\n        \/\/ loop whole month and keep in memo to save 1ms per time\n        const month = useMemo(() => {\n          const m = [];\n          let d = new Date(startDate);\n\n          for (let i = 0; i < chunkSize; i++) {\n            const shouldByPassDate =\n              isPastDate(d) ||\n              (minDate &#038;&#038; moment(d).isBefore(moment(minDate), \"day\")) ||\n              (maxDate &#038;&#038; moment(d).isAfter(moment(maxDate), \"day\"));\n\n            if (!shouldByPassDate) {\n              m.push(new Date(d));\n            }\n            d.setDate(d.getDate() + 1);\n          }\n\n          return m;\n        }, [startDate, minDate, maxDate]);\n\n        const isAtLeftBoundary = !minDate ? false : startDate <= minDate;\n        const isAtRightBoundary = !maxDate ? false : moment(startDate).add(chunkSize, 'days').isAfter(maxDate);\n        const disablePrevMonth = minDate ? moment(showDate).startOf('month').isSameOrBefore(moment(minDate).startOf('month')) : false;\n        const disableNextMonth = maxDate ? moment(showDate).startOf('month').isSameOrAfter(moment(maxDate).startOf('month')) : false;\n\n        const canShowModal = (e) => {\n          return (\n            e.scrollLeft <= 0 &#038;&#038;\n            showDate.getMonth() === now.getMonth() &#038;&#038;\n            showDate.getFullYear() === now.getFullYear() &#038;&#038;\n            !!shouldBypassDates &#038;&#038;\n            NO_BYPASSED_DAYS > 0\n          );\n        };\n\n        const goRight = () => {\n          const newStartDate = new Date(startDate);\n          newStartDate.setDate(startDate.getDate() + 11);\n          setStartDate(newStartDate);\n\n          if (newStartDate.getMonth() !== startDate.getMonth()) {\n            setShowDate(newStartDate);\n          }\n        };\n\n        const goLeft = (e) => {\n          const today = new Date();\n          today.setHours(0, 0, 0, 0);\n\n          const newStartDate = new Date(startDate);\n          newStartDate.setDate(startDate.getDate() - 11);\n\n          if (newStartDate < today) {\n            setStartDate(today);\n          } else {\n            setStartDate(newStartDate);\n          }\n\n          if (newStartDate.getMonth() !== startDate.getMonth()) {\n            setShowDate(newStartDate);\n          }\n        };\n\n        const onPressLeft = () => {\n          let e = containerRef.current;\n          if (canShowModal(e)) {\n            showDateModal();\n          } else {\n            goLeft(e);\n          }\n        };\n\n        const canGoPastMonth =\n          (!(\n            minDate &&\n            minDate.getMonth() === showDate.getMonth() &&\n            minDate.getFullYear() === showDate.getFullYear()\n          ) &&\n            showDate.getMonth() > now.getMonth()) ||\n          showDate.getFullYear() > now.getFullYear();\n\n        const handleScroll = () => {\n          if (!containerRef.current) return;\n          const { scrollLeft, scrollWidth, clientWidth } = containerRef.current;\n\n          if (scrollLeft === 0) {\n            const newShowDate = new Date(showDate);\n            newShowDate.setMonth(newShowDate.getMonth() - 1);\n            setShowDate(newShowDate);\n          } else if (scrollLeft + clientWidth >= scrollWidth) {\n            const newShowDate = new Date(showDate);\n            newShowDate.setMonth(newShowDate.getMonth() + 1);\n            setShowDate(newShowDate);\n          }\n        };\n\n        useEffect(() => {\n          const container = containerRef.current;\n          if (container) {\n            container.addEventListener(\"scroll\", handleScroll);\n          }\n          return () => {\n            if (container) {\n              container.removeEventListener(\"scroll\", handleScroll);\n            }\n          };\n        }, [showDate]);\n\n        const isCurrentMonth = (day) => day.getMonth() === showDate.getMonth();\n\n        const days = useMemo(() => month, [month]);\n        \/\/console.log(\"Dates g\u00e9n\u00e9r\u00e9es :\", days);\n\n        const handlePrevMonth = () => {\n          const newShowDate = new Date(showDate);\n          newShowDate.setMonth(newShowDate.getMonth() - 1);\n          if (minDate && newShowDate < minDate) return;\n          if (newShowDate < now) return;\n          setShowDate(newShowDate);\n          setStartDate(new Date(newShowDate.getFullYear(), newShowDate.getMonth(), 1));\n          containerRef.current.scrollLeft = 0;\n        };\n\n        const handleNextMonth = () => {\n          const newShowDate = new Date(showDate);\n          newShowDate.setMonth(newShowDate.getMonth() + 1);\n          setShowDate(newShowDate);\n          setStartDate(new Date(newShowDate.getFullYear(), newShowDate.getMonth(), 1));\n          containerRef.current.scrollLeft = 0;\n        };\n\n        return (\n          <div className=\"bg-white pb-2\">\n            <div className=\"d-flex flex-wrap flex-row align-items-baseline justify-content-center px-3 py-3 bg-primary\">\n              {canGoPastMonth && !disablePrevMonth && (\n                <Button\n                  onClick={disableNavigation ? undefined : handlePrevMonth}\n                  disabled={disableNavigation || disablePrevMonth}\n                  className=\"bg-white\"\n                >\n                  <i className=\"fas fa-chevron-left text-primary fs-0.75 chevron\" \/>\n                <\/Button>\n              )}\n              <div className=\"h4 text-white px-3\">\n                {showDate.toLocaleString(\"fr-fr\", {\n                  month: \"long\",\n                  year: \"numeric\",\n                })}\n              <\/div>\n              {!disableNextMonth && (\n                <Button\n                  onClick={disableNavigation ? undefined : handleNextMonth}\n                  disabled={disableNavigation || disableNextMonth}\n                  className=\"bg-white\"\n                >\n                  <i className=\"fas fa-chevron-right text-primary fs-0.75 chevron\" \/>\n                <\/Button>\n              )}\n            <\/div>\n            <div className=\"d-flex align-items-center \">\n              <Button\n                onClick={disableNavigation ? undefined : onPressLeft}\n                disabled={disableNavigation || isAtLeftBoundary}\n                className=\"mx-2 mx-sm-3 mt-3 position-relative\"\n              >\n                <i className=\"fas fa-chevron-left fs-0.75\" \/>\n              <\/Button>\n              <div\n                className=\"scrolling-wrapper row flex-nowrap mt-3 py-2 mx-0 mx-sm-2\"\n                ref={containerRef}\n              >\n              {days.map((day) => {\n                  const isSelectedDate =\n                    selectedDate &&\n                    selectedDate.getDate() === day.getDate() &&\n                    selectedDate.getMonth() === day.getMonth() &&\n                    selectedDate.getFullYear() === day.getFullYear();\n\n                  const highlightSelectedDate = isSelectedDate;\n                  return (\n                    <div\n                      key={day}\n                      className={`hl-day ${isCurrentMonth(day) ? \"\" : \"text-muted custom-highlight\"}`}\n                      ref={isSelectedDate ? currentDayRef : null}\n                    >\n                      <Button\n                        onClick={() => onChange(day)}\n                        className={`day hl-bc0 border-0 p-0`}\n                        variant={highlightSelectedDate ? \"primary\" : \"light\"}\n                      >\n                        <div className=\"h-3 day-font\">{day.getDate()}<\/div>\n                        <div className=\"h-6 day-font d-flex justify-content-center\">\n                          {weekDays[day.getDay()]}\n                        <\/div>\n                      <\/Button>\n                    <\/div>\n                  );\n                })}\n              <\/div>\n              <div className=\"d-flex justify-content-end flex-fill\">\n                <Button\n                  onClick={disableNavigation ? undefined : goRight}\n                  disabled={disableNavigation || isAtRightBoundary}\n                  className=\"mx-2 mx-sm-3 mt-3 position-relative\"\n                >\n                  <i className=\"fas fa-chevron-right fs-0.75\" \/>\n                <\/Button>\n              <\/div>\n            <\/div>\n          <\/div>\n        );\n      };\n\n      const FormComponent = (props) => {\n        const { component, required, onChange, disabled, defaultValue, value } =\n          props;\n        const [val, setVal] = useState(defaultValue || value || \"\");\n        let myRef = null;\n        const setRef = (ref) => {\n          myRef = ref;\n        };\n        useEffect(() => {\n          if (defaultValue || value) {\n            setVal(defaultValue || value || \"\");\n          }\n        }, [defaultValue, value]);\n        const onValueChange = (value, actionMeta) => {\n          onChange(value, actionMeta);\n          setVal(value.target.value);\n        };\n        let Component = component;\n        return (\n          <div className=\"position-relative\">\n            <Component {...props} ref={setRef} onChange={onValueChange} \/>\n            <Form.Control\n              tabIndex={-1}\n              autoComplete=\"off\"\n              style={{\n                opacity: 0,\n                width: \"100%\",\n                height: 0,\n                bottom: \"-5px\",\n                position: \"absolute\",\n              }}\n              value={val}\n              onChange={() => {}}\n              onFocus={() => myRef.focus && myRef.focus()}\n              required={required}\n              disabled={disabled}\n            \/>\n          <\/div>\n        );\n      };\n      const DropDown = (props) => {\n        const {\n          options,\n          onChange,\n          disabled,\n          md = 4,\n          type,\n          labelparam = \"label\",\n          selectparam = \"value\",\n          className = \"my-4\",\n          required = true,\n        } = props;\n        return (\n          <Col md={md} className={className}>\n            <FormComponent\n              component={Form.Select}\n              type={type}\n              onChange={onChange}\n              required\n              disabled={disabled}\n              style={{\n                height: \"47px\",\n                borderRadius: \"2px\",\n                backgroundColor: \"#FAFAFA\",\n              }}\n              {...props}\n            >\n              {options.map((option) => (\n                <option\n                  value={option[selectparam]}\n                  key={option[labelparam] + option[selectparam]}\n                >\n                  {option[labelparam]}\n                <\/option>\n              ))}\n            <\/FormComponent>\n          <\/Col>\n        );\n      };\n\n      const PlacesInput = (props) => {\n        const {\n          onChange,\n          md = 4,\n          id,\n          required,\n          disabled,\n          value,\n          onChangeValue,\n        } = props;\n\n        useEffect(() => {\n          \/\/center around montreal\n          const center = { lat: 45.49959233117481, lng: -73.56980872646967 };\n          const defaultBounds = {\n            north: center.lat + 1.0,\n            south: center.lat - 1.0,\n            east: center.lng + 1.0,\n            west: center.lng - 1.0,\n          };\n\n          const autocomplete = new google.maps.places.Autocomplete(\n            document.getElementById(id),\n            {\n              language: \"fr-CA\",\n              fields: [\n                \"name\",\n                \"geometry.location\",\n                \"place_id\",\n                \"formatted_address\",\n                \"address_components\",\n              ],\n              componentRestrictions: {\n                country: [\"ca\"],\n              },\n              bounds: defaultBounds,\n              strictBounds: false,\n              radius: \"1000\",\n            }\n          );\n          google.maps.event.addListener(autocomplete, \"place_changed\", () => {\n            onChange(autocomplete.getPlace());\n          });\n        }, []);\n        return (\n          <Col md={md} className=\"my-3\">\n            <Form.Control\n              id={id}\n              required={required}\n              type=\"text\"\n              className=\"form-control\"\n              placeholder=\"Recherchez votre adresse*\"\n              style={{\n                height: \"47px\",\n                borderRadius: \"2px\",\n                backgroundColor: \"#FAFAFA\",\n              }}\n              disabled={disabled}\n              value={value}\n              onChange={onChangeValue}\n              role=\"presentation\"\n              autoComplete=\"off\"\n            \/>\n          <\/Col>\n        );\n      };\n\n      const LocalizedDateInput = ({ ...props }) => {\n        return (\n          <SemanticUiCalendarReact.DateInput {...props} localization=\"fr\" \/>\n        );\n      };\n\n      const DateInput = React.memo((props) => {\n        const {\n          onChange,\n          id,\n          placeholder,\n          md = 4,\n          style = {},\n          required = true,\n          max,\n          min,\n          value,\n          name,\n          autoComplete = \"off\",\n          inputMode,\n          label = \"Select Date\",\n          dateFormat = \"YYYY-MM-DD\",\n          useInitialDate = true,\n        } = props;\n        const [dateKey, setDateKey] = useState(0);\n        const [canHandleBlur, setCanHandleBlur] = useState(true);\n        moment.locale(\"fr\");\n        const reset = (resetBlurHandler) => {\n          onChange(\"\", { value: undefined });\n          setTimeout(() => {\n            setDateKey(dateKey + 1);\n          }, 50);\n          setCanHandleBlur(!resetBlurHandler);\n        };\n\n        useEffect(() => {\n          reset();\n        }, [max, min]);\n\n        const applyTripDateAutocompleteHints = useCallback(() => {\n          if (id !== \"date-leaving\" && id !== \"date-returning\") {\n            return;\n          }\n          setTimeout(() => {\n            const dateInput = document.getElementById(id);\n            if (!dateInput) return;\n            const innerInput =\n              dateInput.tagName === \"INPUT\"\n                ? dateInput\n                : dateInput.querySelector && dateInput.querySelector(\"input\");\n            if (!innerInput) return;\n            innerInput.setAttribute(\"name\", id === \"date-returning\" ? \"travel-return-day\" : \"travel-departure-day\");\n            innerInput.setAttribute(\"autocomplete\", \"new-password\");\n            innerInput.setAttribute(\"inputmode\", \"numeric\");\n            innerInput.setAttribute(\"data-form-type\", \"other\");\n            innerInput.setAttribute(\"data-lpignore\", \"true\");\n            innerInput.setAttribute(\"data-1p-ignore\", \"true\");\n            innerInput.setAttribute(\"aria-autocomplete\", \"none\");\n          }, 0);\n        }, [id]);\n\n        useEffect(() => {\n          applyTripDateAutocompleteHints();\n        }, [applyTripDateAutocompleteHints, dateKey, value]);\n\n        const onChangeDate = (value, actionMeta) => {\n          applyTripDateAutocompleteHints();\n          onChange(value, actionMeta);\n        };\n\n        const checkTypedDate = () => {\n          if (value) {\n            if (value.length === dateFormat.length) {\n              const date = moment(value, dateFormat);\n              if (date.isValid()) {\n                if (\n                  (!max && !min) ||\n                  (max && min && !date.isAfter(max) && !date.isBefore(min)) ||\n                  (max && !min && !date.isAfter(max)) ||\n                  (min && !max && !date.isBefore(min))\n                ) {\n                  onChange(\"\", { value: date.format(dateFormat) });\n                } else {\n                  reset(true);\n                }\n              } else {\n                reset(true);\n              }\n            } else {\n              reset(true);\n            }\n          } else {\n            reset(true);\n          }\n        };\n\n        return (\n          <Col md={md} className=\"my-3 text-start\">\n            <span style={{ position: \"relative\", bottom: 19 }}>{label}<\/span>\n            <Form.Control\n              key={id + dateKey}\n              id={id}\n              name={name}\n              className=\"form-control border-0 p-0\"\n              placeholder={placeholder || dateFormat}\n              required={required}\n              onChange={onChangeDate}\n              style={{\n                height: \"47px\",\n                borderRadius: \"2px\",\n                width: \"100%\",\n                bottom: 19,\n                ...style,\n              }}\n              as={LocalizedDateInput}\n              value={value}\n              popupPosition=\"bottom center\"\n              closable\n              hideMobileKeyboard\n              autoComplete={autoComplete}\n              inputMode={inputMode}\n              minDate={min &#038;&#038; moment(min, dateFormat)}\n              maxDate={max &#038;&#038; moment(max, dateFormat)}\n              dateFormat={dateFormat}\n              initialDate={useInitialDate &#038;&#038; min &#038;&#038; moment(min, dateFormat)}\n              onBlur={() => {\n                if (canHandleBlur) {\n                  checkTypedDate();\n                }\n              }}\n              onFocus={() => {\n                applyTripDateAutocompleteHints();\n                setCanHandleBlur(true);\n              }}\n            \/>\n          <\/Col>\n        );\n      });\n\n      const Input = (props) => {\n        const {\n          options,\n          onChange,\n          id,\n          name,\n          placeholder,\n          minLength,\n          maxLength,\n          type = \"text\",\n          pattern,\n          autoComplete,\n          inputMode,\n          md = 4,\n          style = {},\n          as,\n          required = true,\n          max,\n          min,\n          value,\n          onKeyDown,\n          hasError,\n          error,\n        } = props;\n\n        const isValid = () => {\n          const input = document.getElementById(id);\n          if (input && input.value !== \"\") {\n            return input.checkValidity();\n          }\n          return true;\n        };\n\n        const errorMessage = () => {\n          const input = document.getElementById(id);\n          if (input && input.value !== \"\") {\n            return input.validationMessage;\n          }\n          return \"\";\n        };\n\n        return (\n          <Col md={md} className=\"my-3\">\n            <Form.Control\n              id={id}\n              name={name}\n              type={type}\n              autoComplete={autoComplete}\n              inputMode={inputMode}\n              className={`form-control ${hasError ? \"text-danger\" : \"\"}`}\n              placeholder={placeholder}\n              required={required}\n              onChange={onChange}\n              minLength={minLength}\n              maxLength={maxLength}\n              pattern={pattern}\n              style={{\n                height: \"47px\",\n                borderRadius: \"2px\",\n                backgroundColor: \"#FAFAFA\",\n                ...style,\n              }}\n              as={as}\n              max={max}\n              min={min}\n              value={value}\n              onKeyDown={onKeyDown}\n              isInvalid={!isValid()}\n            \/>\n            <Form.Control.Feedback type={isValid() ? \"valid\" : \"invalid\"}>\n              {errorMessage()}\n            <\/Form.Control.Feedback>\n            {!!error && hasError && (\n              <div className=\"text-start text-danger\">{error}<\/div>\n            )}\n          <\/Col>\n        );\n      };\n\n      const Radio = (props) => {\n        const { label, onClick, value, index } = props;\n        return (\n          <div key={`default-Radio}`} className=\"mb-3 text-start\">\n            <Form.Check\n              type={\"radio\"}\n              name=\"locations\"\n              value={value}\n              label={label}\n              onClick={onClick}\n              required\n              defaultChecked={!index}\n            \/>\n          <\/div>\n        );\n      };\n\n      const Checkbox = (props) => {\n        const { label, onClick, value, required, defaultChecked } = props;\n        return (\n          <div className=\"text-start d-flex my-2\">\n            <Form.Check\n              name=\"sameAsContact\"\n              required={required}\n              defaultChecked={defaultChecked}\n              onClick={onClick}\n              value={value}\n            \/>\n            <span className=\"px-3\" style={{ color: \"#6D757D\" }}>\n              {label}\n            <\/span>\n          <\/div>\n        );\n      };\n\n      const TermsConditions = () => {\n        const handleTermsClick = (e) => {\n          e.preventDefault();\n          Swal.fire({\n            title: \"Termes et conditions\",\n            html: `<iframe src=\"https:\/\/cliniquevoyageur.ca\/termes-et-conditions-pour-les-tests-virtuels-de-covid\/?lang=fr\" style=\"width:100%; height:70vh; border:none; border-radius:8px;\" title=\"Termes et conditions\"><\/iframe>`,\n            width: \"90%\",\n            padding: \"1rem\",\n            showCloseButton: true,\n            showConfirmButton: true,\n            confirmButtonText: \"Fermer\",\n            confirmButtonColor: \"#0058a4\",\n            customClass: {\n              popup: \"terms-modal-popup\",\n              htmlContainer: \"terms-modal-container\"\n            },\n            didOpen: () => {\n              \/\/ Ensure iframe is responsive on mobile\n              const iframe = Swal.getHtmlContainer().querySelector(\"iframe\");\n              if (iframe) {\n                iframe.style.maxWidth = \"100%\";\n                iframe.style.minHeight = \"400px\";\n              }\n            }\n          });\n        };\n\n        return (\n          <div\n            key={`default-terms`}\n            className=\"text-start d-flex my-2\"\n            style={{ color: \"#0058a4\" }}\n          >\n            <Form.Check name=\"terms\" required \/>\n            <a\n              href={`#`}\n              onClick={handleTermsClick}\n              className=\"px-3\"\n              style={{ cursor: \"pointer\" }}\n            >\n              {\"Je suis d'accord avec les termes et conditions\"}\n            <\/a>\n          <\/div>\n        );\n      };\n\n      const LoadingView = () => (\n        <div className=\"spinner-cont d-flex  h-100 justify-content-center align-items-center\">\n          <Spinner animation=\"border\" id=\"loading-spinner\" \/>\n        <\/div>\n      );\n\n      let AppointmentReasons = [{ label: \"Reason For Appointment\", value: \"\" }];\n\n      \/\/ Detect website domain\n      const isLocalSummitHost =\n        window.location.hostname === \"localhost\" ||\n        window.location.hostname === \"127.0.0.1\" ||\n        window.location.hostname.indexOf(\".local\") !== -1;\n      const isSummitHealth =\n        window.location.hostname.includes(\"summithealth.ca\") ||\n        isLocalSummitHost;\n\n      const initialTBDateTime = urlParams.get('initialTBDateTime');\n      const minDateParam = urlParams.get('minDate'); \n      const maxDateParam = urlParams.get('maxDate');\n      \/\/ Id du rendez-vous de lecture TB (le cas \u00e9ch\u00e9ant)\n      const readingAid = urlParams.get('readingAid');\n      const tbReadingId = urlParams.get('TBReadingId');\n\n      const initialUTC = moment.utc(initialTBDateTime);\n      const initialEST = initialUTC.tz('America\/New_York');\n      \/\/ console.log(\"Initial EST:\", initialEST && initialEST.format());\n      \n      const minDate = minDateParam ? moment(minDateParam, \"DD-MM-YYYY\").toDate() : null;\n      const maxDate = maxDateParam ? moment(maxDateParam, \"DD-MM-YYYY\").toDate() : null;\n      \/\/ console.log(\"Min date:\", minDate);\n      \/\/ console.log(\"Max date:\", maxDate);\n      \n      \n      const getAgeLabel = (months) => {\n        if (months >= 12) {\n          const years = months \/ 12;\n          return `${years}${years > 1 ? ' ans' : ' an'}`;\n        }\n        return `${months} mois`;\n      };\n\n      let TbReading =\n        urlParams.get(\"TbReading\") &&\n        urlParams.get(\"TbReading\").toLowerCase() === \"true\";\n      console.log(\"TbReading = \" + TbReading);\n      if (TbReading) {\n        console.log(\"TbReading\");\n        AppointmentReasons = [\n          { \n            label: \"Reason For Appointment\", \n            value: \"\",\n            isVirtual: false, \n            isSinglePatientService: false,\n            minimumAgeMonths: 6,\n            minimunAgeText: \"\"  \n          },\n          {\n            label: \"Test cutan\u00e9 TB \u2013 Rendez-vous de lecture\",\n            value: \"TB Skin Test Reading\",\n            isVirtual: false, \n            isSinglePatientService: false,\n            minimumAgeMonths: 60,\n            isPromoApplicable: true,\n            requiresTripDetails: false,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que le Test Cutan\u00e9 TB \u2013 Rendez-vous initial et Test Cutan\u00e9 TB \u2013 Rendez-vous de lecture sont offerts aux enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            } \n          },\n        ];\n      } else {\n        AppointmentReasons = [\n          {\n            label: \"Raison du rendez-vous\",\n            value: \"\",\n            isVirtual: false,\n            isSinglePatientService: false,\n            minimumAgeMonths: 6,\n            minimunAgeText: \"\"\n          },\n          {\n            label: \"Consultation Vaccination Voyage\",\n            value: \"Travel Consult\",\n            isVirtual: false,\n            isSinglePatientService: false,\n            minimumAgeMonths: 6,\n            isPromoApplicable: true,\n            requiresTripDetails: true,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que nous vaccinons les enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            }\n          },\n          {\n            label: \"Consultation Vaccination Voyage Virtuel\",\n            value: \"Telehealth Consultation\",\n            isVirtual: true,\n            isSinglePatientService: false,\n            minimumAgeMonths: 6,\n            isPromoApplicable: true,\n            requiresTripDetails: true,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que nous vaccinons les enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            }\n          },\n          {\n            label: \"Vaccin de rappel\",\n            value: \"Booster\",\n            isVirtual: false, \n            isSinglePatientService: false,\n            minimumAgeMonths: 6,\n            isPromoApplicable: true,\n            requiresTripDetails: false,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que nous vaccinons les enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            }\n          },\n          { \n            label: \"Vaccin hors-voyage\", \n            value: \"Private Vaccine\",\n            isVirtual: false, \n            isSinglePatientService: false,\n            minimumAgeMonths: 6,\n            isPromoApplicable: true,\n            requiresTripDetails: false,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que nous vaccinons les enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            } \n          },\n          {\n            label: \"Test cutan\u00e9 TB - Rendez-vous initial\",\n            value: \"TB Skin Test Initial Appointment\",\n            isVirtual: false, \n            isSinglePatientService: false,\n            minimumAgeMonths: 60,\n            isPromoApplicable: true,\n            requiresTripDetails: false,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que le Test cutan\u00e9 TB \u2013 Rendez-vous initial et Test cutan\u00e9 TB \u2013 Rendez-vous de lecture \u2013 sont offerts aux enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            } \n          },\n          {\n            label: \"Test Rapide de Streptocoque\",\n            value: \"Rapid Strep Test\",\n            isVirtual: false, \n            isSinglePatientService: false,\n            minimumAgeMonths: 36,\n            isPromoApplicable: true,\n            requiresTripDetails: false,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que le Test Rapide de Streptocoque est offert aux enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            } \n          },\n          {\n            label: \"Bilan pr\u00e9-biologique\",\n            value: \"Pre-Biological Assessment\",\n            isVirtual: false, \n            isSinglePatientService: false,\n            minimumAgeMonths: 6,\n            isPromoApplicable: true,\n            requiresTripDetails: false,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que le Bilan Pr\u00e9-Biologique est offert aux enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            }  \n          },\n          {\n            label: \"\u00c9valuation Virtuelle de l'Herp\u00e8s Labial\",\n            value: \"Cold Sore\",\n            isVirtual: true, \n            isSinglePatientService: true,\n            minimumAgeMonths: 6,\n            isPromoApplicable: true,\n            requiresTripDetails: false,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que les \u00c9valuations Virtuelles de l'Herp\u00e8s Labial sont offertes aux enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            } \n          },\n          {\n            label: \"Consultation Virtuelle pour une Piq\u00fbre de Tique\",\n            value: \"Tick Bite Exposure\",\n            isVirtual: true, \n            isSinglePatientService: true,\n            minimumAgeMonths: 96,\n            isPromoApplicable: true,\n            requiresTripDetails: false,\n            get minimunAgeText() {\n              const ageLabel = getAgeLabel(this.minimumAgeMonths);\n              return `Il est important de noter que les Consultations Virtuelles pour une Piq\u00fbre de Tique sont offertes aux enfants \u00e2g\u00e9s de ${ageLabel} et plus. Si votre enfant a moins de ${ageLabel}, veuillez choisir une date de rendez-vous ult\u00e9rieure, lorsqu'il ou elle aura plus de ${ageLabel}, ou appelez-nous au 1-888-224-8809.`;\n            } \n          },\n          {\n            label: \"Test Rapide Grippe\/COVID-19\",\n            value: \"In-Person Flu Assessment\",\n            isVirtual: false, \n            isSinglePatientService: true,\n            minimumAgeMonths: 12,\n            isPromoApplicable: true,\n            requiresTripDetails: false,\n            get minimunAgeText() {\n              return `Il est important de noter que les Tests Rapides Grippe\/COVID-19 sont offerts aux patients \u00e2g\u00e9s de 1 an et plus. Si vous avez moins de 1 an, veuillez nous appeler au 1-888-224-8809.`;\n            } \n          }\n\n        ];\n        \n        \/\/ Filter appointment types based on domain\n        \/\/ Only show Cold Sore and Tick Bite on summithealth.ca\n        if (!isSummitHealth) {\n          AppointmentReasons = AppointmentReasons.filter(\n            (reason) => \n              reason.value !== \"Cold Sore\" && \n              reason.value !== \"Tick Bite Exposure\"\n          );\n        }\n      }\n\n      const NumberOfPeople = [\n        { label: \"Nombre de patients\", value: \"\" },\n        { label: \"Un\", value: \"1\" },\n        { label: \"Deux\", value: \"2\" },\n        { label: \"Trois\", value: \"3\" },\n        { label: \"Quatre\", value: \"4\" },\n        { label: \"Cinq\", value: \"5\" },\n      ];\n\n      const LanguageOptions = [\n        \/\/ { label: \"Langue\", value: \"\" },\n        { label: \"Fran\u00e7ais\", value: \"french\" },\n        { label: \"Anglais\", value: \"english\" },\n      ];\n      const STANDARD_PROGRESS_LABELS = [\n        \"S\u00e9lectionnez votre heure\",\n        \"Fournissez vos coordonn\u00e9es\",\n        \"Compl\u00e9tez votre achat\",\n      ];\n\n      const ONLINE_PROGRESS_LABELS = [\n        \"S\u00e9lectionnez votre heure\",\n        \"Fournissez vos coordonn\u00e9es\",\n        \"Compl\u00e9tez votre achat\",\n      ];\n\n      const ErrorView = ({ error, showErrorMessage }) => {\n        if (showErrorMessage) {\n          return <div className=\"d-flex align-self-center\">{error}<\/div>;\n        }\n        const renderErrorMessage = () => {\n          \/\/ If error is a string and not just a code, show it directly\n          if (typeof error === 'string' && error.length > 1 && error !== \"e\") {\n            \/\/ Check if it's a descriptive error message (not just a code)\n            if (error.includes(\" \") || error.length > 3) {\n              return <div>{error}<\/div>;\n            }\n          }\n          \n          switch (error) {\n            case \"1\":\n              return (\n                <div>\n                  \u00c9chec du paiement, veuillez utiliser une carte valide.\n                <\/div>\n              );\n            case \"NSF\":\n            case \"DECLINED: NSF\":\n              return <div>Paiement refus\u00e9 : Fonds insuffisants. Veuillez utiliser une autre m\u00e9thode de paiement ou contacter votre banque.<\/div>;\n            case \"DECLINED\":\n              return <div>Paiement refus\u00e9. Veuillez v\u00e9rifier les d\u00e9tails de votre carte ou utiliser une autre m\u00e9thode de paiement.<\/div>;\n            case \"e\":\n              return (\n                <div>\n                  Impossible de trouver des cliniques pr\u00e8s de vous. Veuillez essayer une autre adresse ou{\" \"}\n                  <a href={`tel:8882248809`}>appeler le 1-888-224-8809<\/a> pour obtenir de l'aide.\n                <\/div>\n              );\n            default:\n              return (\n                <div>\n                  Il semble qu'il y ait eu une erreur s'il vous pla\u00eet appelez{\" \"}\n                  <a href={`tel:8882248809`}>1-888-224-8809<\/a>\n                <\/div>\n              );\n          }\n        };\n        return (\n          <div className=\"d-flex align-self-center\">{renderErrorMessage()}<\/div>\n        );\n      };\n      const LocationMap = (props) => {\n        const {\n          location,\n          serviceId,\n          selectedDate,\n          onSelectDate,\n          onSelectSlot,\n          selectedLocationId,\n          selectedTime,\n          refetch,\n          isRemoteService,\n          isTelehealthCovidTest,\n          isTelehealthConsultation,\n          minDate,\n          maxDate,\n          time,\n          isFirstInitialTbConfirmed,\n          setIsFirstInitialTbConfirmed,\n          firstTbInitialAppointment,\n          language,\n          serviceType={serviceType},\n          preFetchedVirtualAvailabilities=null,\n          preFetchedVirtualAvailabilitiesPromise=null\n        } = props;\n        \n        const distance = location.geolocation && location.geolocation.distance;\n        const [times, setTimeSlots] = useState([]);\n        const [availableExpert, setAvailableExpert] = useState({});\n        const [allAvailableExperts, setAllAvailableExperts] = useState();\n        const [filteredExperts, setFilteredExperts] = useState({});\n        const [isLoadingSlots, setIsLoadingSlots] = useState({});\n        const [isLoadingAllSlots, setIsLoadingAllSlots] = useState(false);\n        const [showError, setError] = useState(false);\n        const [sortedDates, setSortedDates] = useState([]);\n        const [virtualLocationId, setVirtualLocationId] = useState(null);\n\n        const mainAction = async (date) => {\n          const response = await getAvailabilities({\n            locationId: location.id,\n            serviceId,\n            selectedDate: formatAvailabilityDateForApi(date),\n          });\n          \/\/ Check if response contains an error\n          if (response.errorCode || response.errorMessage) {\n            throw new Error(response.errorMessage || `Error ${response.errorCode}: Failed to load availabilities`);\n          }\n          if (!response || !response.content) {\n            throw new Error(\"No availability data returned from server\");\n          }\n          if (date) {\n            setAvailableExpert(response.content.availableSlots || {});\n          } else {\n            setAllAvailableExperts(response.content.availableSlots);\n          }\n        };\n\n        const virtualAction = async (date) => {\n          console.log('langiuage:',language);\n          console.log('selectedDate:',date);\n          const response = await getVirtualAvailabilities({\n            serviceId,\n            selectedDate: date,\n            language: language,\n          });\n          \/\/ Check if response contains an error\n          if (response.errorCode || response.errorMessage) {\n            throw new Error(response.errorMessage || `Error ${response.errorCode}: Failed to load availabilities`);\n          }\n          if (!response) {\n            throw new Error(\"No availability data returned from server\");\n          }\n          if (date) {\n            setAvailableExpert(response || {});\n          } else {\n            setAllAvailableExperts(response);\n          }\n          const experts = Object.values(response).filter((val) => val.length);\n          const locationId =\n            experts[0] && experts[0][0] && experts[0][0].locationId;\n          setVirtualLocationId(locationId);\n        };\n\n        const loadCurrentDateAvailabilities = async (date) => {\n          setIsLoadingSlots((prevState) => ({\n            ...prevState,\n            [date]: true,\n          }));\n          setError(\"\");\n          try {\n            if (\n              isRemoteService ||\n              isTelehealthCovidTest ||\n              isTelehealthConsultation\n            ) {\n              await virtualAction(date);\n            } else {\n              await mainAction(date);\n            }\n          } catch (err) {\n            console.error(\"Error loading availabilities:\", err);\n            const errorMsg = err.errorMessage || err.message || \"Impossible de charger les cr\u00e9neaux horaires disponibles. Veuillez v\u00e9rifier votre connexion Internet et r\u00e9essayer, ou appeler le 1-888-224-8809 pour obtenir de l'aide.\";\n            setError(errorMsg);\n          }\n          setIsLoadingSlots((prevState) => ({\n            ...prevState,\n            [date]: false,\n          }));\n        };\n\n        const loadAllAvailabilities = async () => {\n          setIsLoadingAllSlots(true);\n          setError(\"\");\n          try {\n            if (\n              isRemoteService ||\n              isTelehealthCovidTest ||\n              isTelehealthConsultation\n            ) {\n              await virtualAction();\n            } else {\n              await mainAction();\n            }\n          } catch (err) {\n            console.error(\"Error loading all availabilities:\", err);\n            const errorMsg = err.errorMessage || err.message || \"Impossible de charger les cr\u00e9neaux horaires disponibles. Veuillez v\u00e9rifier votre connexion Internet et r\u00e9essayer, ou appeler le 1-888-224-8809 pour obtenir de l'aide.\";\n            setError(errorMsg);\n          }\n          setIsLoadingAllSlots(false);\n        };\n        \/\/ For virtual consultations, load virtual availabilities\n        useEffect(() => {\n          if (isRemoteService || isTelehealthCovidTest || isTelehealthConsultation) {\n            \/\/ For virtual consultations, use pre-fetched availabilities if available\n            if (preFetchedVirtualAvailabilities && !allAvailableExperts) {\n              setAllAvailableExperts(preFetchedVirtualAvailabilities);\n              \/\/ Extract virtual location ID if available\n              const experts = Object.values(preFetchedVirtualAvailabilities).filter((val) => val.length);\n              const locationId = experts[0] && experts[0][0] && experts[0][0].locationId;\n              if (locationId) {\n                setVirtualLocationId(locationId);\n              }\n              return;\n            }\n            \/\/ If there's a pending pre-fetch promise, wait for it instead of making a new request\n            if (preFetchedVirtualAvailabilitiesPromise && !allAvailableExperts) {\n              \/\/ Show loading spinner while waiting for pre-fetch\n              setIsLoadingAllSlots(true);\n              preFetchedVirtualAvailabilitiesPromise.then(availResponse => {\n                if (availResponse && !availResponse.errorCode && !availResponse.errorMessage) {\n                  setAllAvailableExperts(availResponse);\n                  \/\/ Extract virtual location ID if available\n                  const experts = Object.values(availResponse).filter((val) => val.length);\n                  const locationId = experts[0] && experts[0][0] && experts[0][0].locationId;\n                  if (locationId) {\n                    setVirtualLocationId(locationId);\n                  }\n                }\n                setIsLoadingAllSlots(false);\n              }).catch(err => {\n                console.error(\"Error waiting for pre-fetched virtual availabilities:\", err);\n                setIsLoadingAllSlots(false);\n                \/\/ If pre-fetch fails, fall back to regular fetch\n                if (serviceId && !allAvailableExperts) {\n                  loadAllAvailabilities();\n                }\n              });\n              return;\n            }\n            \/\/ Otherwise, load availabilities when serviceId is available\n            if (serviceId && !allAvailableExperts) {\n              loadAllAvailabilities();\n            }\n            return;\n          }\n          \n          \/\/ For in-person consultations, always fetch fresh - no caching\n          const locationId = location.id || location.Id;\n          if (!locationId) {\n            return; \/\/ Don't fetch if location is invalid\/empty\n          }\n          \n          if (!allAvailableExperts) {\n            loadCurrentDateAvailabilities(selectedDate);\n          }\n        }, [location, serviceId, selectedDate, allAvailableExperts, refetch, isRemoteService, isTelehealthCovidTest, isTelehealthConsultation, preFetchedVirtualAvailabilities, preFetchedVirtualAvailabilitiesPromise]);\n\n        useEffect(() => {\n          \/\/ Skip for virtual consultations - handled in the first useEffect\n          if (isRemoteService || isTelehealthCovidTest || isTelehealthConsultation) {\n            return;\n          }\n          \n          \/\/ Check if location is valid (has an ID)\n          const locationId = location.id || location.Id;\n          if (!locationId) {\n            return; \/\/ Don't fetch if location is invalid\/empty\n          }\n          \n          \/\/ Always fetch fresh - no caching\n          if (!allAvailableExperts) {\n            loadAllAvailabilities();\n          }\n        }, [location, serviceId, refetch]);\n\n        const sortDates = (dates) => {\n          const sorted = Object.keys(dates).sort(\n            (a, b) =>\n              moment(a, \"D_M_yyyy\").valueOf() - moment(b, \"D_M_yyyy\").valueOf()\n          );\n          setSortedDates(sorted);\n        };\n\n        let mergedDates = useMemo(() => {\n          let dates = {};\n          let experts = {};\n          const preProcessedExpert = allAvailableExperts || availableExpert;\n          for (let key in preProcessedExpert) {\n            let expert = preProcessedExpert[key];\n            expert = removeBookedSlot(expert);\n            experts[key] = expert;\n          }\n          setFilteredExperts(experts);\n          if (preProcessedExpert) {\n            let mergedExperts = _(experts).toArray().flatten().value();\n            mergedExperts.forEach((value) => {\n              const existingSlots = dates[value.strDate]\n                ? dates[value.strDate].slots\n                : [];\n              dates[value.strDate] = {\n                ...value,\n                slots: [...existingSlots, ...value.slots],\n              };\n            });\n          }\n          sortDates(dates);\n          return dates;\n        }, [availableExpert, allAvailableExperts]);\n\n        const getNextAvailableDate = useCallback(() => {\n          const dateHasSlots = (dateStr) => {\n            const dateSlots = mergedDates[dateStr] && mergedDates[dateStr].slots;\n            if (!dateSlots || !dateSlots.length) return false;\n            \/\/ For TB reading: apply same 48-72h filter as TimeSlots modifiedSlots\n            if (isFirstInitialTbConfirmed && firstTbInitialAppointment && firstTbInitialAppointment.slot && minDate) {\n              const referenceTime = firstTbInitialAppointment.slot.split(\"_\")[0];\n              const [hours, minutes] = referenceTime.split(\":\").map((p) => parseInt(p));\n              let inputHour = hours;\n              if (referenceTime.includes(\"PM\") && hours !== 12) inputHour += 12;\n              else if (referenceTime.includes(\"AM\") && hours === 12) inputHour = 0;\n              const inputTime = new Date(2000, 0, 1, inputHour, minutes);\n              const isMinDate = dateStr === moment(minDate).format(\"D_M_YYYY\");\n              const hasPassingSlot = dateSlots.some((s) => {\n                if (!s.slot || s.isBookedSlot) return false;\n                const slotTimeStr = s.slot.split(\"_\")[0];\n                const [objHours, objMinutes] = slotTimeStr.split(\":\").map((p) => parseInt(p));\n                let objHour = objHours;\n                if (slotTimeStr.includes(\"PM\") && objHours !== 12) objHour += 12;\n                else if (slotTimeStr.includes(\"AM\") && objHours === 12) objHour = 0;\n                const objTime = new Date(2000, 0, 1, objHour, objMinutes);\n                return isMinDate ? objTime >= inputTime : objTime <= inputTime;\n              });\n              return hasPassingSlot;\n            }\n            return true;\n          };\n          let val = sortedDates.find((value) => {\n            if (!dateHasSlots(value)) return false;\n            let isSameOrBeforeMaxDate = true;\n            if (maxDate) {\n              isSameOrBeforeMaxDate = moment(value, \"D_M_yyyy\").isSameOrBefore(\n                maxDate\n              );\n            }\n            return (\n              isSameOrBeforeMaxDate &&\n              moment(value, \"D_M_yyyy\").isAfter(\n                moment(selectedDate, \"D_M_yyyy\")\n              )\n            );\n          });\n          const date = moment(val, \"D_M_yyyy\").toDate();\n          return val ? date : null;\n        }, [sortedDates, selectedDate, maxDate, mergedDates, isFirstInitialTbConfirmed, firstTbInitialAppointment, minDate]);\n\n        const onSelectParentSlot = (e) => {\n          setParentSlot(e.target.value);\n          setTimeSlots([]);\n        };\n\n        const onSelect = async (time) => {\n          for (let key in filteredExperts) {\n            const selectedDateSlot = filteredExperts[key].find(\n              (val) => val.strDate == selectedDate\n            );\n            const found =\n              selectedDateSlot &&\n              selectedDateSlot.slots.find((slot) => slot.slot === time);\n            if (found) {\n              const isVirtualBooking =\n                isRemoteService ||\n                isTelehealthCovidTest ||\n                isTelehealthConsultation;\n              const loc = isVirtualBooking\n                ? virtualLocationId || location.id\n                : location.id || virtualLocationId;\n              onSelectSlot(loc, time, key, isVirtualBooking);\n              break;\n            }\n          }\n        };\n\n        const renderMap = () => (\n          <div>\n            <div className=\"pb-1 text-start locationName\">{location.name}<\/div>\n            <div className=\"text-start locationAddress\">\n              <i className=\"fas fa-map-marked-alt\"><\/i> {location.street},{\" \"}\n              {location.city}, {location.province}, {location.postalCode}\n            <\/div>\n            <div className=\"text-start locationPhone\">\n              <i className=\"fas fa-phone\"><\/i>{\" \"}\n              <a href={`tel:\/${location.phone}`}>{location.phone}<\/a>\n            <\/div>\n            {distance && (\n              <div className=\"pb-1 text-start\">\n                <i className=\"fas fa-route\"><\/i> {distance.text} away\n              <\/div>\n            )}\n            <div\n              id=\"map-container-google-2\"\n              className=\"z-depth-1-half map-container mb-3\"\n              style={{ height: `250px` }}\n            >\n              <iframe\n                src={`https:\/\/www.google.com\/maps\/embed\/v1\/place?key=AIzaSyDFO90WdM-sPpr--JJQlP3PikKyuFvfwfc&#038;q=${location.street}+${location.city}+${location.province}`}\n                frameBorder=\"0\"\n                style={{ border: 0 }}\n                allowFullScreen\n              \/>\n            <\/div>\n          <\/div>\n        );\n\n        const renderRemoteServiceIntro = () => (\n          <div className=\"p-3 py-5\">\n            <Image\n              style={{\n                height: \"135px\",\n                width: \"135px\",\n                marginBottom: \"20px\",\n                display: \"none\",\n              }}\n              src={\"\/step2.png\"}\n            \/>\n            <div className=\"text-start\">\n              Une fois votre rendez-vous pris, nous vous enverrons un lien vers\n              votre salle de consultation virtuelle.\n            <\/div>\n          <\/div>\n        );\n        return (\n          <div className=\"map-cont pb-3\">\n            <Row className=\"row entryRow\">\n              <Col lg={4}>\n                {isRemoteService ? renderRemoteServiceIntro() : renderMap()}\n              <\/Col>\n              <Col lg={8}>\n                {isLoadingSlots[selectedDate] ? (\n                  <LoadingView \/>\n                ) : (\n                  <div className=\"pt-3 h-100 justify-content-center d-flex\">\n                    {!showError ? (\n                      <TimeSlots\n                        slots={\n                          mergedDates &#038;&#038;\n                          mergedDates[selectedDate] &#038;&#038;\n                          mergedDates[selectedDate].slots\n                        }\n                        onSelectTime={onSelect}\n                        locationIsActive={selectedLocationId === location.id}\n                        selectedTime={selectedTime}\n                        nextAvailableDate={getNextAvailableDate()}\n                        onSelectDate={onSelectDate}\n                        isLoadingAllSlots={isLoadingAllSlots}\n                        selectedDate={selectedDate}\n                        minDate={minDate}\n                        maxDate={maxDate}\n                        selectedLocationId={selectedLocationId}\n                        locationId={location.id}\n                        time={time}\n                        isFirstInitialTbConfirmed={isFirstInitialTbConfirmed}\n                        firstTbInitialAppointment={firstTbInitialAppointment}\n                        location={location}\n                        isVirtual={\n                          isRemoteService ||\n                          isTelehealthCovidTest ||\n                          isTelehealthConsultation\n                        }\n                      \/>\n                    ) : (\n                      <ErrorView error={showError} showErrorMessage={!!showError} \/>\n                    )}\n                  <\/div>\n                )}\n              <\/Col>\n            <\/Row>\n          <\/div>\n        );\n      };\n\n      const TimeSlots = React.memo((props) => {\n        const {\n          slots,\n          onSelectTime,\n          locationIsActive,\n          selectedTime,\n          nextAvailableDate,\n          onSelectDate,\n          isLoadingAllSlots,\n          selectedDate,\n          minDate,\n          maxDate,\n          locationId,\n          selectedLocationId,\n          time,\n          isFirstInitialTbConfirmed,\n          firstTbInitialAppointment,\n          location,\n          isVirtual,\n        } = props;\n\n        \n        const isToday = useMemo(() => {\n          return moment(selectedDate, \"D_M_YYYY\").isSame(moment(), \"day\");\n        }, [selectedDate]);\n\n        const lessThen30MinutesFromNow = (slot) => {\n          const currentTime = moment();\n          const slotTime = moment(slot.name, \"hh:mm A\");\n          return slotTime.subtract(30, \"minutes\").isAfter(currentTime);\n        };\n\n\n        let slotsArray = useMemo(() => {\n          if (selectedLocationId != locationId) {\n            return (\n              (slots &&\n                _(slots)\n                  .uniqBy(\"slot\")\n                  .map((slot) => ({\n                    name: slot.slot.split(\"_\")[0],\n                    value: slot.slot,\n                  }))\n                  .sort(\n                    (a, b) =>\n                      new Date(\"1970\/01\/01 \" + a.name) -\n                      new Date(\"1970\/01\/01 \" + b.name)\n                  ).filter(isToday && isVirtual ? lessThen30MinutesFromNow : _.identity)\n                  .value()) ||\n              []\n            );\n          } else {\n            return (\n              (slots &&\n                _(slots)\n                  .uniqBy(\"slot\")\n                  .map((slot) => ({\n                    name: slot.slot.split(\"_\")[0],\n                    value: slot.slot,\n                  }))\n                  .sort(\n                    (a, b) =>\n                      new Date(\"1970\/01\/01 \" + a.name) -\n                      new Date(\"1970\/01\/01 \" + b.name)\n                  ).filter(isToday && isVirtual ? lessThen30MinutesFromNow : _.identity)\n                  .value()) ||\n              []\n            );\n          }\n        }, [\n          slots,\n          selectedLocationId,\n          locationId,\n          minDate,\n          selectedDate,\n          maxDate,\n        ]);\n\n        const modifiedSlots = useMemo(() => {\n          if (isFirstInitialTbConfirmed) {\n            const referenceTime =\n              firstTbInitialAppointment.slot &&\n              firstTbInitialAppointment.slot.split(\"_\")[0];\n            console.log(referenceTime);\n\n            \/\/ Array of times\n            const times = slotsArray;\n            \/\/ Your time string\n\n            \/\/ Parse input time string\n            const [hours, minutes] = referenceTime\n              .split(\":\")\n              .map((part) => parseInt(part));\n            let inputHour = hours;\n            if (referenceTime.includes(\"PM\") && hours !== 12) {\n              inputHour += 12;\n            } else if (referenceTime.includes(\"AM\") && hours === 12) {\n              inputHour = 0;\n            }\n\n            \/\/ Convert input time to Date object\n            const inputTime = new Date(2000, 0, 1, inputHour, minutes);\n\n            \/\/ Filter the array to get times after the input time\n            const filteredArray = slotsArray.filter((obj) => {\n              \/\/ Parse object's time string\n              const [objHours, objMinutes] = obj.name\n                .split(\":\")\n                .map((part) => parseInt(part));\n              let objHour = objHours;\n              if (obj.name.includes(\"PM\") && objHours !== 12) {\n                objHour += 12;\n              } else if (obj.name.includes(\"AM\") && objHours === 12) {\n                objHour = 0;\n              }\n\n              \/\/ Convert object's time to Date object\n              const objTime = new Date(2000, 0, 1, objHour, objMinutes);\n\n              \/\/ Compare the times\n              return selectedDate == moment(minDate).format(\"D_M_YYYY\")\n                ? objTime >= inputTime\n                : objTime <= inputTime;\n            });\n\n            return filteredArray;\n          }\n          return slotsArray;\n        }, [slotsArray, firstTbInitialAppointment, minDate, selectedDate]);\n\n        return !!modifiedSlots.length ? (\n          <div className=\"d-flex flex-wrap pb-3 align-content-start\">\n            {isLoadingAllSlots ? (\n              <LoadingView \/>\n            ) : (\n              modifiedSlots.map((slot, index) => {\n                const selected =\n                  locationIsActive && selectedTime === slot.value;\n                return (\n                  <Button\n                    onClick={() => onSelectTime(slot.value)}\n                    className={`m-1 p-1 time-slot ${selected && \"selected\"}`}\n                    variant={selected ? \"primary\" : \"outline-primary\"}\n                    key={index + slot.name}\n                  >\n                    {slot.name}\n                  <\/Button>\n                );\n              })\n            )}\n          <\/div>\n        ) : nextAvailableDate ? (\n          <Button\n            onClick={() => onSelectDate(nextAvailableDate)}\n            className={`m-1 mb-3 p-2 px-5 align-self-center`}\n            variant=\"primary\"\n          >\n            La premi\u00e8re date disponible est{\" \"}\n            {moment(nextAvailableDate).format(\"MMM DD\")} {\">>\"}\n          <\/Button>\n        ) : isLoadingAllSlots ? (\n          <div className=\"align-self-center\">\n            <div>Aucun rendez-vous disponible pour cette journ\u00e9e..<\/div>\n            <div>\n              Veuillez patienter pendant que nous trouvons le prochain jour\n              disponible...\n            <\/div>\n            <div className=\"py-3\">\n              <LoadingView \/>\n            <\/div>\n          <\/div>\n        ) : (\n          <div\n            className=\"d-flex align-self-center\"\n            style={{ color: \"#0058a4\" }}\n          >\n            Pas d'horaires \u00e0 venir disponibles.\n          <\/div>\n        );\n      });\n\n      const Step = ({ iconName, title, alt, description }) => (\n        <Col md={6} lg={3} className=\"my-4\">\n          <div className=\"fs-5 book fw-bold mb-3\">\n            <i\n              className={iconName}\n              style={{ marginBottom: \"10px\", fontSize: \"50px\" }}\n            ><\/i>{\" \"}\n            <br \/>\n            {title}\n          <\/div>\n          <div className=\"text-start\">{description}<\/div>\n        <\/Col>\n      );\n\n      const RemoteServiceDescription = () => (\n        <div className=\"mt-5\">\n          <div className=\"fs-4 book fw-bold mb-4\">\n            Comment Fonctionne Le Test Covid Virtuel\n          <\/div>\n          <div className=\"text-start mb-5\">\n            Les test COVID virtuels offrent le moyen le plus s\u00e0\u00bbr et le plus\n            simple de se faire tester. Dans les tests virtuels, des tests\n            rapides d'antig\u00e8ne vous sont envoy\u00e9es et effectu\u00e9s via un \u00c2\u00ab chat\n            vid\u00e9o \u00c2\u00bb avec l'une de nos infirmi\u00e8res autoris\u00e9es.\n          <\/div>\n          <Row>\n            <Step\n              iconName=\"fa-solid fa-cart-shopping\"\n              title=\"Planifiez votre consultation virtuelle\"\n              alt=\"step 1\"\n              description=\"Achetez votre test \u00e0  distance en toute s\u00e9curit\u00e9 en fournissant votre adresse et en prenant un rendez-vous vid\u00e9o avec l'un de nos professionnels de la sant\u00e9.\"\n            \/>\n            <Step\n              iconName=\"fa-solid fa-truck-fast\"\n              title=\"Recevez vos test par la poste\"\n              alt=\"step 2\"\n              description=\"Vos tests arriveront en toute s\u00e9curit\u00e9 par la poste. Nous nous assurons de toujours envoyer deux tests pour nous assurer qu'il n'y a pas de probl\u00e8mes et pour \u00e9viter tout stress inutile avant votre voyage.\"\n            \/>\n            <Step\n              iconName=\"fa-solid fa-video\"\n              title=\"Rejoindre votre appel vid\u00e9o\"\n              alt=\"step 3\"\n              description=\"Pendant votre appel vid\u00e9o, l'un de nos professionnels de la sant\u00e9 vous expliquera comment effectuer votre test en toute s\u00e9curit\u00e9. Une fois termin\u00e9, vous prendrez une photo de votre test et de votre identification pour v\u00e9rifier les r\u00e9sultats.\"\n            \/>\n            <Step\n              iconName=\"fa-solid fa-file-medical\"\n              title=\"Recevez vos r\u00e9sultats par voie \u00e9lectronique\"\n              alt=\"step 4\"\n              description=\"Une fois votre test v\u00e9rifi\u00e9 par notre \u00e9quipe, vos r\u00e9sultats vous seront envoy\u00e9s par voie \u00e9lectronique et pr\u00e0\u00aat \u00e0  voyager.\"\n            \/>\n          <\/Row>\n        <\/div>\n      );\n\n      const RemoteSupervisionServiceDescription = () => (\n        <div className=\"mt-5\">\n          <div className=\"fs-4 book fw-bold mb-4\">\n            Comment Fonctionnent Les Tests Supervis\u00e9s Avec La T\u00e9l\u00e9sant\u00e9.\n          <\/div>\n          <div className=\"text-start mb-5\">\n            Les tests de t\u00e9l\u00e9sant\u00e9 COVID offrent le moyen le plus s\u00e0\u00bbr et le\n            plus simple de se faire tester. En utilisant les tests d\u00e9sign\u00e9s\n            achet\u00e9s aupr\u00e8s de Summit Health. Lors de votre rendez-vous de\n            t\u00e9l\u00e9sant\u00e9, une infirmi\u00e8re autoris\u00e9e vous guidera par\n            vid\u00e9o-conf\u00e9rence pour administrer le test d'antig\u00e8ne. Vos r\u00e9sultats\n            sont valid\u00e9s et une attestation de r\u00e9sultat \u00e9lectronique vous est\n            directement envoy\u00e9e.\n          <\/div>\n          <Row>\n            <Step\n              iconName=\"fa-solid fa-cart-shopping\"\n              title=\"Planifiez votre consultation virtuelle\"\n              alt=\"step 1\"\n              description=\"Achetez vos tests en toute s\u00e9curit\u00e9 dans l'une de nos cliniques ou en ligne via notre formulaire.\"\n            \/>\n            <Step\n              iconName=\"fa-solid fa-truck-fast\"\n              title=\"Recevez vos test par la poste\"\n              alt=\"step 2\"\n              description=\"Lorsque vous avez vos tests et que vous \u00e0\u00aates pr\u00e0\u00aat \u00e0  prendre rendez-vous avec l'infirmi\u00e8re, utilisez le lien unique qui vous a \u00e9t\u00e9 fourni.\"\n            \/>\n            <Step\n              iconName=\"fa-solid fa-video\"\n              title=\"Rejoindre votre appel vid\u00e9o\"\n              alt=\"step 3\"\n              description=\"Au cours de votre appel vid\u00e9o, l'un de nos professionnels de la sant\u00e9 vous expliquera comment effectuer votre test en toute s\u00e9curit\u00e9. Une fois termin\u00e9, vous soumettrez une photo de votre test et une pi\u00e8ce d'identit\u00e9 pour v\u00e9rification.\"\n            \/>\n            <Step\n              iconName=\"fa-solid fa-file-medical\"\n              title=\"Recevez vos r\u00e9sultats par voie \u00e9lectronique\"\n              alt=\"step 4\"\n              description=\"Une fois votre test v\u00e9rifi\u00e9 par notre \u00e9quipe, vos r\u00e9sultats vous seront envoy\u00e9s par voie \u00e9lectronique. Ce document sera une attestation de vos r\u00e9sultats, qui pourra \u00e0\u00aatre utilis\u00e9 pour les voyages o\u00e0\u00b9 les tests antig\u00e9niques sont accept\u00e9s.\"\n            \/>\n          <\/Row>\n        <\/div>\n      );\n\n      const RemoteConsultationDescription = () => (\n        <div className=\"mt-5\">\n          <div className=\"fs-4 book fw-bold mb-4\">\n            Comment Fonctionnent Les Consultations Avec La T\u00e9l\u00e9sant\u00e9\n          <\/div>\n          <div className=\"text-start mb-5\">\n            Connectez-vous avec l'\u00e9quipe m\u00e9dicale de Summit Health via un appel vid\u00e9o s\u00e9curis\u00e9, accessible depuis votre ordinateur ou appareil mobile, o\u00f9 que vous soyez. Notre service de consultation virtuelle garantit une \u00e9valuation approfondie et des conseils personnalis\u00e9s pour vos pr\u00e9occupations de sant\u00e9.\n          <\/div>\n          <Row>\n            <Step\n              iconName=\"fa-regular fa-calendar-check\"\n              title=\"\u00c9tape 1\"\n              alt=\"step 1\"\n              description=\"R\u00e9servez votre consultation en ligne\"\n            \/>\n            <Step\n              iconName=\"fa-regular fa-rectangle-list\"\n              title=\"\u00c9tape 2\"\n              alt=\"step 2\"\n              description=\"Compl\u00e9tez votre questionnaire medical\"\n            \/>\n            <Step\n              iconName=\"fa-solid fa-video\"\n              title=\"\u00c9tape 3\"\n              alt=\"step 3\"\n              description=\"Joignez-vous \u00e0 l'appel vid\u00e9o pour votre consultation\"\n            \/>\n            <Step\n              iconName=\"fa-solid fa-suitcase-rolling\"\n              title=\"\u00c9tape 4\"\n              alt=\"step 4\"\n              description=\"Obtenez Vos Vaccins Ou Votre Prescription.\"\n            \/>\n          <\/Row>\n        <\/div>\n      );\n\n      const Modal = ({\n        message,\n        isOpen,\n        onClose,\n        onCancel,\n        onConfirm,\n        confirmButtonLabel = \"CONFIRMER\",\n        cancelButtonLabel = \"ANNULER\",\n        showCancel = true,\n      }) => {\n        const confirm = () => {\n          const shouldClose = onConfirm();\n          if (shouldClose !== false) {\n            onClose();\n          }\n        };\n        return (\n          <ReactModal\n            isOpen={isOpen}\n            style={{\n              overlay: {\n                backgroundColor: \"rgba(255, 255, 255, 0.75)\",\n                zIndex: 2,\n              },\n              content: {\n                border: \"0px\",\n                background: \"#0000\",\n              },\n            }}\n          >\n            <div className=\"h-100 d-flex justify-content-center align-items-center\">\n              <div\n                style={{\n                  width: \"350px\",\n                  backgroundColor: \"rgba(255,255,255)\",\n                  border: \"1px solid #ccc\",\n                  borderRadius: \"4px\",\n                  zIndex: 100,\n                }}\n                className=\"px-4 py-4\"\n              >\n                <Container fluid style={{ zIndex: 5 }}>\n                  <div className=\"mb-4 text-center\">{message()}<\/div>\n                  <div className=\"d-flex justify-content-center\">\n                    <Button className=\"mx-3 button\" onClick={confirm}>\n                      {confirmButtonLabel}\n                    <\/Button>\n                    {showCancel && (\n                      <Button\n                        variant=\"warning\"\n                        className=\"mx-3\"\n                        onClick={() => {\n                          onCancel();\n                          onClose();\n                        }}\n                      >\n                        {cancelButtonLabel}\n                      <\/Button>\n                    )}\n                  <\/div>\n                <\/Container>\n              <\/div>\n            <\/div>\n          <\/ReactModal>\n        );\n      };\n\n      const CARD_ELEMENT_OPTIONS = {\n        style: {\n          base: {\n            color: \"#32325d\",\n            fontFamily: '\"Helvetica Neue\", Helvetica, sans-serif',\n            fontSmoothing: \"antialiased\",\n            fontSize: \"16px\",\n            \"::placeholder\": {\n              color: \"#6D757D\",\n            },\n          },\n          invalid: {\n            color: \"#fa755a\",\n            iconColor: \"#fa755a\",\n          },\n        },\n      };\n\n      const ProgressCheckMark = () => (\n        <div\n          className=\"d-flex align-items-center justify-content-center\"\n          style={{\n            border: \"1px solid\",\n            height: \"45px\",\n            width: \"45px\",\n            borderRadius: \"25px\",\n            position: \"absolute\",\n            borderColor: \"gray\",\n            borderLeft: \"0px\",\n          }}\n        >\n          <div\n            className=\"d-flex align-items-center justify-content-center\"\n            style={{\n              border: \"1px solid\",\n              height: \"43px\",\n              width: \"43px\",\n              borderRadius: \"30px\",\n              borderColor: \"#D9D9D9\",\n              backgroundColor: \"#59a1ff\",\n              borderLeft: \"0px\",\n            }}\n          >\n            <div\n              className=\"fas fa-check white\"\n              style={{ fontSize: \"26px\", color: \"white\" }}\n            \/>\n          <\/div>\n        <\/div>\n      );\n\n      const StyledProgressBar = ({ progress, labels = [] }) => {\n        const now = (progress * 100) \/ labels.length;\n        return (\n          <div className=\"mt-4\">\n            <div className=\"d-flex flex-row align-items-center justify-content-end\">\n              <div className=\"w-100\">\n                <ProgressBar\n                  now={now}\n                  style={{\n                    height: \"20px\",\n                    borderRadius: \"10px\",\n                    backgroundColor: \"#D9D9D9\",\n                    padding: \"1px 2px\",\n                    border: \"1px solid #969696\",\n                  }}\n                \/>\n              <\/div>\n              {now >= 100 && <ProgressCheckMark \/>}\n            <\/div>\n            <div className=\"flex-row d-flex justify-content-around \">\n              {labels.map((label, index) => (\n                <span\n                  className=\"p-3 progressLabel fw-bold\"\n                  style={{\n                    width: \"220px\",\n                    color: progress > index ? \"#0058a4\" : \"#969696\",\n                  }}\n                  key={label + index}\n                >\n                  {label}\n                <\/span>\n              ))}\n            <\/div>\n          <\/div>\n        );\n      };\n\n      const HeaderUtil = ({ title, renderSubtitle }) => (\n        <div>\n          <h1 className=\"fs-3 book fw-bold mb-4\">{title}<\/h1>\n          {renderSubtitle()}\n        <\/div>\n      );\n\n      const renderDefaultHeaderSubtitle = () => (\n        <span className=\"text-sm-end tip\">\n          Pour d\u00e9buter, svp s\u00e9lectionner le type de rendez-vous et le nombre de\n          personnes. Nous vous montrerons les disponibilit\u00e9s des cliniques les\n          plus proches de votre emplacement. Besoin d'aide ? Contactez nous{\" \"}\n          <a href=\"tel:\/(438) 266-0855\">(438) 266-0855<\/a>.{\" \"}\n          <b>\n            Veuillez noter que les annulations dans les 48 heures avant votre\n            rendez-vous sont{\" \"}\n            <a href=\"\/politique-dannulation\/?lang=fr\">\n              assujetties \u00e0 des frais d'annulation{\" \"}\n            <\/a>\n          <\/b>\n        <\/span>\n      );\n\n      const renderRemoteHeaderSubtitle = () => (\n        <div\n          className=\"pb-4 d-md-flex align-items-center\"\n          style={{ borderBottom: \"1px solid #CCCCCC\" }}\n        >\n          <Image\n            src={\"\/step1.png\"}\n            className=\"mx-3 mb-3 mb-md-0\"\n            style={{ height: \"80px\", width: \"80px\" }}\n          \/>\n          <div className=\"text-start \">\n            <span className=\"text-sm-end tip\">\n              Pour commencer, trouvons d'abord un moment opportun pour r\u00e9server\n              votre appel vid\u00e9o en ligne avec l'un de nos professionnels de la\n              sant\u00e9. Nous vous indiquerons les disponibilit\u00e9s d'heure et de\n              date, Besoin d'aide ? Veuillez appeler le{\" \"}\n              <a href=\"tel:\/(438) 266-0855\">(438) 266-0855<\/a>.{\" \"}\n              <b>\n                Veuillez noter que les consultations virtuelles peuvent \u00e0\u00aatre\n                rec\u00e9dul\u00e9es mais pas annul\u00e9es conform\u00e9ment \u00e0 la{\" \"}\n                <a href=\"\/politique-dannulation\/?lang=fr\">\n                  politique d'annulation{\" \"}\n                <\/a>\n              <\/b>\n            <\/span>\n          <\/div>\n        <\/div>\n      );\n\n      const PlacesForm = ({ onChange, id }) => {\n        const [selectedPlace, setSelectedPlace] = useState(null);\n        const [place, setPlace] = useState(\"\");\n        const [locationInfo, setLocationInfo] = useState({});\n        const [city, setCity] = useState(\"\");\n\n        const validatePostalCode = () => {\n          let postal = document.getElementById(`pos${id}`);\n          if (postal.validity.patternMismatch) {\n            postal.setCustomValidity(\"Please enter valid postal code\");\n          } else {\n            postal.setCustomValidity(\"\");\n          }\n        };\n        const onChangeItem = (e, key) => {\n          validatePostalCode();\n          setLocationInfo({ ...locationInfo, [key]: e.target.value });\n        };\n\n        useEffect(() => {\n          if (selectedPlace) {\n            const streetNumber = getAddressAttribute(\n              selectedPlace,\n              \"street_number\"\n            );\n            const route = getAddressAttribute(\n              selectedPlace,\n              \"route\",\n              \"\",\n              false\n            );\n            const street = streetNumber + (route ? ` ${route}` : \"\");\n            const postCode = getAddressAttribute(selectedPlace, \"postal_code\");\n            const postal_code_suffix =\n              postCode +\n              getAddressAttribute(selectedPlace, \"postal_code_suffix\");\n            const city = getAddressAttribute(selectedPlace, \"locality\");\n            const province = getAddressAttribute(\n              selectedPlace,\n              \"administrative_area_level_1\"\n            );\n            setLocationInfo({\n              street,\n              postCode: postal_code_suffix,\n              city,\n              province,\n            });\n            setPlace(\"\");\n          }\n        }, [selectedPlace]);\n\n        useEffect(() => {\n          const st = locationInfo.street || place;\n          const info = {\n            ...locationInfo,\n            street: st && st.trim(),\n          };\n          onChange(info);\n        }, [locationInfo]);\n\n        const onChangePlaces = (e) => {\n          setLocationInfo({ ...locationInfo, street: \"\" });\n          setPlace(e.target.value);\n        };\n        return (\n          <Row className=\"align-items-center \">\n            <PlacesInput\n              onChange={setSelectedPlace}\n              md={8}\n              id={id}\n              required\n              value={locationInfo.street || place}\n              onChangeValue={onChangePlaces}\n            \/>\n            <Row className=\"mx-0 px-0\">\n              <Input\n                placeholder=\"Apartment, unit, suite, or floor #\"\n                id={\"apt\"}\n                onChange={(e) => onChangeItem(e, \"apartment\")}\n                required={false}\n              \/>\n              <Input\n                placeholder=\"City\"\n                id={\"cit\"}\n                onChange={(e) => onChangeItem(e, \"city\")}\n                value={locationInfo.city}\n              \/>\n            <\/Row>\n            <Input\n              placeholder=\"State\/Province\"\n              id={\"state\"}\n              onChange={(e) => onChangeItem(e, \"province\")}\n              value={locationInfo.province}\n            \/>\n            <Input\n              placeholder=\"Postal Code\"\n              id={\"pos\" + id}\n              onChange={(e) => onChangeItem(e, \"postCode\")}\n              value={locationInfo.postCode}\n              pattern={\n                \"[ABCEGHJ-NPRSTVXYabceghj-nprstvxy][0-9][ABCEGHJ-NPRSTV-Zabceghj-nprstv-z][ -]?[0-9][ABCEGHJ-NPRSTV-Zabceghj-nprstv-z][0-9]\"\n              }\n            \/>\n          <\/Row>\n        );\n      };\n\n      const PaymentInputs = React.memo(\n        ({ setPaymentData, shouldShowErrors, dismissErrors }) => {\n          const [cardNumber, setCardNumber] = useState(\"\");\n          const [expirationDate, setExpirationDate] = useState(\"\");\n          const [cvv, setCvv] = useState(\"\");\n\n          const [cardError, setCardError] = useState(\"\");\n          const [expirationDateError, setExpirationDateError] = useState(\"\");\n\n          const handleChangeCardNumber = useCallback((e) => {\n            const val = e.target.value.replace(\/[^\\dA-Z]\/g, \"\").trim();\n            setCardNumber(val);\n            const isValid = validateCard(val);\n            setCardError(isValid ? \"\" : \"Num\u00e9ro de carte invalide\");\n          }, []);\n\n          const onExpiryKeyDown = useCallback((e) => {\n            const value = e.target.value;\n            if (value.length === 3 && e.key === \"Backspace\") {\n              setExpirationDate(value.slice(0, -1));\n            }\n          }, []);\n\n          const normalizeExpiryValue = (value) => {\n            const raw = String(value || \"\");\n            const slashParts = raw.split(\"\/\").map((part) => part.replace(\/[^\\d]\/g, \"\"));\n            if (slashParts.length >= 2 && slashParts[0]) {\n              const month = slashParts[0].padStart(2, \"0\").slice(-2);\n              const yearRaw = slashParts[1] || \"\";\n              const year = yearRaw.length > 2 ? yearRaw.slice(-2) : yearRaw;\n              return year ? `${month}\/${year}`.slice(0, 5) : `${month}\/`;\n            }\n\n            const digits = raw.replace(\/[^\\d]\/g, \"\").slice(0, 6);\n            if (digits.length <= 2) {\n              return digits;\n            }\n\n            const month = digits.slice(0, 2);\n            const yearRaw = digits.slice(2);\n            const year = yearRaw.length > 2 ? yearRaw.slice(-2) : yearRaw;\n            return `${month}\/${year}`.slice(0, 5);\n          };\n\n          const handleChangeExpiry = useCallback((e) => {\n            let date = normalizeExpiryValue(e.target.value);\n            let parts = date.split(\"\/\");\n            let month = parseInt(parts[0], 10);\n            let year = parseInt(parts[1], 10);\n            let current_year = parseInt(new Date().getFullYear().toString().slice(-2), 10);\n            if (\n              !(month >= 1 && month <= 12) ||\n              !(year >= current_year && year <= current_year + 10)\n            ) {\n              setExpirationDateError(\"Date d'expiration invalide\");\n            } else {\n              setExpirationDateError(\"\");\n            }\n\n            setExpirationDate(date);\n          }, []);\n\n          const handleChangeCVV = useCallback(\n            (e) => setCvv(e.target.value.replace(\/[^\\d]\/g, \"\")),\n            []\n          );\n          const cardValue = useMemo(() => cardNumber, [cardNumber]);\n\n          useEffect(() => {\n            const isValid =\n              (cardNumber.length === 16 || cardNumber.length === 15) &&\n              expirationDate.length === 5 &&\n              cvv.length > 2 &&\n              !cardError &&\n              !expirationDateError;\n\n            dismissErrors();\n\n            setPaymentData({\n              cardNumber,\n              expirationDate,\n              cvv,\n              isValid,\n            });\n          }, [cardNumber, expirationDate, cvv, cardError, expirationDateError, shouldShowErrors]);\n\n          const shouldHighlightErrors = shouldShowErrors && (!cardNumber || !expirationDate || !cvv);\n\n          return (\n            <Row>\n              <Input\n                placeholder=\"Num\u00e9ro de carte\"\n                id={\"card-number\"}\n                name=\"cc-number\"\n                autoComplete=\"cc-number\"\n                inputMode=\"numeric\"\n                md={6}\n                onChange={handleChangeCardNumber}\n                maxLength={16}\n                value={cardValue}\n                hasError={\n                  (cardNumber.length === 16 || shouldShowErrors) &#038;&#038; cardError\n                }\n                error={cardError || \"Entrez un num\u00e9ro de carte valide\"}\n                style={shouldHighlightErrors &#038;&#038; !cardNumber ? { border: \"2px solid rgb(214, 0, 0)\", borderRadius: \"4px\" } : {}}\n              \/>\n              <Input\n                placeholder=\"MM \/ AA\"\n                id={\"month-year\"}\n                name=\"cc-exp\"\n                autoComplete=\"cc-exp\"\n                inputMode=\"numeric\"\n                md={3}\n                onKeyDown={onExpiryKeyDown}\n                onChange={handleChangeExpiry}\n                value={expirationDate}\n                maxLength={7}\n                hasError={\n                  (expirationDate.length === 5 || shouldShowErrors) &#038;&#038;\n                  expirationDateError\n                }\n                style={shouldHighlightErrors &#038;&#038; !expirationDate ? { border: \"2px solid rgb(214, 0, 0)\", borderRadius: \"4px\" } : {}}\n              \/>\n              <Input\n                placeholder=\"CVV\"\n                id={\"card-cvv\"}\n                name=\"cc-csc\"\n                autoComplete=\"cc-csc\"\n                inputMode=\"numeric\"\n                md={3}\n                value={cvv}\n                onChange={handleChangeCVV}\n                maxLength={4}\n                hasError={shouldShowErrors &#038;&#038; cvv.length < 3}\n                style={shouldHighlightErrors &#038;&#038; !cvv ? { border: \"2px solid rgb(214, 0, 0)\", borderRadius: \"4px\" } : {}}\n              \/>\n            <\/Row>\n          );\n        }\n      );\n\n      ReactModal.setAppElement(document.getElementById(\"book-form\"));\n\n      const SummitHealth = (props) => {\n        \/* Input Handling States *\/\n        moment.locale(\"fr\");\n        const [service, setService] = useState({});\n        const [serviceId, setServiceId] = useState(\"\");\n        const [serviceType, setServiceType] = useState(\"\");\n        const [patientCount, setPatientCount] = useState(\"1\");\n        const [countries, setCountries] = useState([\n          { CountryNameFR: \"S\u00e9lectionner des destinations\", Id: \"\" },\n        ]);\n        const [countriesById, setCountriesById] = useState({});\n        const [selectedCountries, setSelectedCountries] = useState([]);\n\n        function getInitialLanguage() {\n          const savedLang = localStorage.getItem('selectedLanguage');\n          return savedLang || 'french';\n        }\n\n        const [language, setLanguage] = useState(getInitialLanguage());\n\n        const [vaccines, setVaccines] = useState([\n          { name: \"S\u00e9lectionnez les vaccins\", Id: \"\" },\n        ]);\n        const [vaccinesById, setVaccinesById] = useState({});\n        const [selectedVaccines, setSelectedVaccines] = useState([]);\n\n        const [existingAppointment, setExistingAppointment] = useState({});\n\n        const [date, setDate] = useState(new Date());\n        const [time, setTime] = useState(\"\");\n\n        const [location, setLocation] = useState(\"\");\n        const [locations, setLocations] = useState([]);\n        const [visibleLocationsCount, setVisibleLocationsCount] = useState(10);\n        const [selectedLocationId, setSelectedLocationId] = useState(\"\");\n        const [selectedExpert, setSelectedExpert] = useState(\"\");\n\n        const [contacts, setContacts] = useState([]);\n        const [userLocation, setUserLocation] = useState({});\n        const [shippingLocation, setShippingLocation] = useState({});\n        const [sameAsContactLocation, setSameAsContactLocation] =\n          useState(true);\n        const [specialInstructions, setSpecialInstructions] = useState(\"\");\n        const [departureDate, setDepartureDate] = useState(\"\");\n        const [returnDate, setReturnDate] = useState(\"\");\n\n        const [promoCode, setPromoCode] = useState(urlParams.get(\"promoCode\") || \"\");\n        const [promoCodeData, setPromoCodeData] = useState({});\n        const [autoValidatePromo, setAutoValidatePromo] = useState(!!urlParams.get(\"promoCode\"));\n\n        const [paymentData, setPaymentData] = useState({});\n        const [shouldShowPaymentErrors, setShouldShowPaymentErrors] =\n          useState(false);\n        const [paymentError, setPaymentError] = useState(\"\");\n        const [bookResponse, setBookResponse] = useState({});\n        const [saveCreditCard, setSaveCreditCard] = useState(true);\n        \/* Error Handling States *\/\n        const [showNoLocationSelectedError, setNoLocationSelectedError] =\n          useState(false);\n        const [error, setError] = useState(\"\");\n        const [promoCodeError, setPromoCodeError] = useState(\"\");\n\n        \/* loading Handling States *\/\n        const [isLoadingLocations, setIsLoadingLocations] = useState(false);\n        \n        \/\/ Background fetch state\n        const [backgroundFetchPromise, setBackgroundFetchPromise] = useState(null);\n        const [backgroundFetchCompleted, setBackgroundFetchCompleted] = useState(false);\n        const [backgroundFetchResult, setBackgroundFetchResult] = useState(null);\n        \n        const [isLoadingAppointment, setIsLoadingAppointment] = useState(false);\n        const [isSubmitting, setIsSubmitting] = useState(false);\n        const isSubmittingRef = useRef(false); \/\/ Ref for synchronous submission tracking\n        const bookingRequestIdRef = useRef(typeof crypto !== \"undefined\" && crypto.randomUUID ? crypto.randomUUID() : `br-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`);\n        const [isValidatingPromoCode, setIsValidatingPromoCode] =\n          useState(false);\n\n        \/* UI *\/\n        const [mainFormTabKey, setMainFormTabKey] = useState(\"main\");\n        const [progress, setProgress] = useState(0);\n\n        const [selectedServiceNote, setSelectedServiceNote] = useState(null);\n        const [isServiceNoteModalOpen, setIsServiceNoteModalOpen] = useState(false);\n        const [serviceNoteCheckboxes, setServiceNoteCheckboxes] = useState([]);\n\n        const [checkboxError, setCheckboxError] = useState(\"\");\n        const [isServiceNoteConfirmed, setIsServiceNoteConfirmed] = useState(false);\n\n        const [isPrivateVaccineNoteModalOpen, setPrivateVaccineNoteModalOpen] =\n          useState(false);\n        const [isSlotModalOpen, setIsSlotModalOpen] = useState(false);\n        const [isRescheduleFeeModalOpen, setIsRescheduleFeeModalOpen] =\n          useState(false);\n        const [isTbReadingSupportModalOpen, setIsTbReadingSupportModalOpen] =\n          useState(false);\n        const [isBookingModalOpen, setIsBookingModalOpen] = useState(false);\n        const [slotNotAvailableModalOpen, setSlotNotAvailableModalOpen] =\n          useState(false);\n        const [isDateModalOpen, setIsDateModalOpen] = useState(false);\n        const [showEmail, setShowEmail] = useState(false);\n        const [key, setKey] = useState(\"appointment\");\n\n        const [refetchSlots, setRefetchSlots] = useState(false);\n\n        const [firstTbInitialAppointment, setFirstTbInitialAppointment] =\n          useState(null);\n        const [SecondTbInitialAppointment, setSecondTbInitialAppointment] =\n          useState(null);\n        const [isFirstInitialTbConfirmed, setIsFirstInitialTbConfirmed] =\n          useState(false);\n        const firstTbDate = useRef(null);\n\n        \/* End of States *\/\n\n        \/* Refs *\/\n        const selectSlotRef = useRef();\n        const serviceTypeRef = useRef(serviceType);\n        const serviceFetchIdRef = useRef(0);\n\n        const isRemoteService = serviceType === \"Covid Remote Test\";\n        const isTravelConsult = serviceType === \"Travel Consult\";\n\n        const isBooster = serviceType === \"Booster\";\n        const isPrivateVaccine = serviceType === \"Private Vaccine\";\n        const shouldShowVaccineSection = isBooster || isPrivateVaccine;\n        const [selectedInitialtbTestDate, setSelectedInitialTbTestDate] =\n          useState(null);\n\n        const isTelehealthCovidTest = serviceType === \"Telehealth COVID Test\";\n        const isTelehealthNonTravelConsultation = serviceType === \"Cold Sore\";\n\n        const selectedReason = AppointmentReasons.find(r => r.value === serviceType) || {};\n        \/\/ Check both serviceType and existingAppointment.serviceType for virtual consultations\n        const isTelehealthConsultation = !!selectedReason.isVirtual || \n          (existingAppointment && existingAppointment.serviceType && existingAppointment.serviceType.includes(\"Telehealth Consultation\"));\n        const isSinglePatientService = !!selectedReason.isSinglePatientService;\n\n        const urlSearchParams = new URLSearchParams(window.location.search);\n        const params = Object.fromEntries(urlSearchParams.entries());\n        const isEditing = !!params.aid;\n        const passedAppointmentType = !!params.aType;\n\n        const [isRebooking, setIsRebooking] = useState(false);\n\n        const servicePopups = {\n          \"Booster\": {\n            description: () => (\n              <span>Ce rendez\u2011vous est pour un <strong>vaccin de rappel<\/strong>. Veuillez confirmer les points suivants:<\/span>\n            ),\n            checkboxes: [\n              <span>Je comprends qu\u2019il ne s\u2019agit pas d\u2019une consultation voyage.<\/span>,\n              <span>J\u2019ai d\u00e9j\u00e0 effectu\u00e9 une consultation voyage et ceci est un suivi.<\/span>\n            ]\n          },\n          \"Cold Sore\": {\n            description: () => (\n              <span>Ce rendez\u2011vous est pour une <strong>\u00e9valuation virtuelle de l'herp\u00e8s labial<\/strong>. Veuillez confirmer les points suivants:<\/span>\n            ),\n            checkboxes: [\n              <span>J\u2019ai actuellement un feu sauvage qui n\u00e9cessite une \u00e9valuation.<\/span>,\n              <span>Je comprends qu\u2019une ordonnance ne sera \u00e9mise que si cela est jug\u00e9 appropri\u00e9.<\/span>\n            ]\n          },\n          \"Blood Test\": {\n            description: () => (\n              <span>Ce rendez\u2011vous est pour une <strong>prise de sang<\/strong>. Veuillez confirmer les points suivants:<\/span>\n            ),\n            checkboxes: [\n              <span>Je dispose d\u2019une requ\u00eate d\u2019un professionnel de sant\u00e9.<\/span>,\n              <span>Je comprends qu\u2019il ne s\u2019agit pas d\u2019une consultation et qu\u2019aucun diagnostic ne sera pos\u00e9.<\/span>\n            ]\n          },\n           \"Rapid Strep Test\": {\n          description: () => (\n            <span>\n              Ce rendez-vous est uniquement destin\u00e9 aux patients qui r\u00e9pondent \u00e0 <strong>tous<\/strong> les crit\u00e8res ci-dessous. Si vous pr\u00e9sentez d'autres sympt\u00f4mes ou ne remplissez pas ces conditions, veuillez consulter votre m\u00e9decin de famille ou une clinique m\u00e9dicale locale.\n            <\/span>\n          ),\n          checkboxes: [\n            <span>Je n'ai pas re\u00e7u d'antibiotiques au cours du dernier mois.<\/span>,\n            <span>Je ne suis pas immunosupprim\u00e9(e) et je ne suis pas un(e) receveur(se) de greffe.<\/span>,\n          ]\n        },\n          \"Tick Bite Exposure\": {\n            description: () => (\n              <span>\n                Ce rendez-vous s\u2019adresse uniquement aux patients qui r\u00e9pondent \u00e0 <strong>tous<\/strong> les crit\u00e8res ci-dessous.  \n                Si vous pr\u00e9sentez des sympt\u00f4mes de la maladie de Lyme (comme une rougeur, de la fi\u00e8vre ou des douleurs articulaires),  \n                veuillez plut\u00f4t consulter votre m\u00e9decin de famille ou une clinique m\u00e9dicale locale.\n              <\/span>\n            ),\n            checkboxes: [\n              <span>Je ne suis pas enceinte et je ne pense pas l\u2019\u00eatre.<\/span>,\n              <span>La tique est probablement rest\u00e9e accroch\u00e9e pendant 24 heures ou plus, ou je ne suis pas certain de la dur\u00e9e.<\/span>,\n              <span>Je ne pr\u00e9sente pas de sympt\u00f4mes de la maladie de Lyme (comme une rougeur, de la fi\u00e8vre ou des douleurs articulaires).<\/span>,\n              <span>La tique \u00e9tait encore accroch\u00e9e ou je suis certain qu\u2019il s\u2019agissait d\u2019une tique.<\/span>,\n              <span>Je n\u2019ai aucun ant\u00e9c\u00e9dent de r\u00e9action allergique \u00e0 un antibiotique de type t\u00e9tracycline (ex. : doxycycline, minocycline ou t\u00e9tracycline).<\/span>\n            ]\n          },\n          \"Private Vaccine\": {\n            description: () => (\n              <span>\n                Veuillez noter que ce rendez-vous est pour les vaccins non\n                li\u00e9s au voyage tels que le zona (Shingrix) et la pneumonie\n                (Prevnar, Pneumovax). Si vous recherchez un vaccin li\u00e9 au\n                voyage (par exemple: la fi\u00e8vre jaune, la typho\u00efde),{\" \"}\n\n                <span style={{ textDecoration: 'underline', cursor: 'pointer' }} onClick={defaultTravelConsult}>\n                  veuillez cliquer ici pour s\u00e9lectionner une consultation de voyage \u00e0 la place.\n                <\/span>\n\n                {\" \"} Si vous avez des questions, n'h\u00e9sitez pas \u00e0 nous contacter.\n              <\/span>\n            ),\n            checkboxes: []\n          },\n          \/\/ COMMENTED OUT: Flu bookings temporarily disabled\n          \/\/ \"Virtual Flu Assessment\": {\n          \/\/   description: () => (\n          \/\/     <span>Ce rendez-vous est pour une <strong>\u00e9valuation virtuelle de la grippe<\/strong>. Veuillez confirmer les points suivants:<\/span>\n          \/\/   ),\n          \/\/   checkboxes: [\n          \/\/     <span>Je comprends que cette consultation ne garantit pas que je recevrai un m\u00e9dicament.<\/span>,\n          \/\/     <span>Je comprends que cette consultation est non remboursable.<\/span>,\n          \/\/     <span>Je confirme que j'ai effectu\u00e9 un test COVID-19 avec un r\u00e9sultat n\u00e9gatif.<\/span>,\n          \/\/     <span>Je comprends que ce service est destin\u00e9 aux personnes qui sont \u00e0 risque de complications ou qui vivent avec quelqu'un qui est \u00e0 risque de complications.<\/span>\n          \/\/   ]\n          \/\/ },\n          \/\/ \"In-Person Flu Assessment\": {\n          \/\/   description: () => (\n          \/\/     <span>Ce rendez-vous est pour un <strong>test rapide de la grippe\/COVID-19<\/strong>. Veuillez confirmer les points suivants:<\/span>\n          \/\/   ),\n          \/\/   checkboxes: [\n          \/\/     <span>Je comprends que les r\u00e9sultats de mon test rapide de la grippe ne garantissent pas que je recevrai un m\u00e9dicament.<\/span>,\n          \/\/     <span>Je comprends que cette consultation est non remboursable.<\/span>\n          \/\/   ]\n          \/\/ }\n        };\n\n        const handleConfirmServiceNote = () => {\n          const allChecked = serviceNoteCheckboxes.every(Boolean);\n          if (!allChecked) {\n            setCheckboxError(\"Veuillez confirmer tous les \u00e9l\u00e9ments pour continuer.\");\n            return false;\n          }\n\n          setCheckboxError(\"\");\n          setIsServiceNoteConfirmed(true);\n          setIsServiceNoteModalOpen(false);\n          return true; \n        };\n\n        function resetForm() {\n          setServiceType(\"\");\n          setPatientCount(\"\");\n          setLocation(null);\n          setKey(\"appointment\");\n          setProgress(0);\n          setService({});\n          setServiceId(\"\");\n        }\n\n        function handleCancelServiceNote() {\n          setCheckboxError(\"\");\n          setIsServiceNoteModalOpen(false);\n          setPrivateVaccineNoteModalOpen(false)\n          resetForm(); \n        }\n\n        function handleCloseServiceNote() {\n          setCheckboxError(\"\");\n          setIsServiceNoteModalOpen(false);\n          setPrivateVaccineNoteModalOpen(false)\n        }\n\n        const shouldShowRescheduleFeeOpen = useMemo(() => {\n          if (!isEditing || !existingAppointment || !existingAppointment.startTime) {\n            return false;\n          }\n          \n          \/\/ Check if appointment was rescheduled by support - if so, don't show fee popup\n          const rescheduleBySupport = existingAppointment.rescheduleBySupport;\n          \n          \/\/ Handle boolean true, string \"true\", numeric 1, and string \"1\"\n          const isRescheduledBySupport = rescheduleBySupport === true || \n                                        rescheduleBySupport === \"true\" || \n                                        rescheduleBySupport === 1 ||\n                                        rescheduleBySupport === \"1\";\n          \n          \/\/ Don't show fee if rescheduled by support - this takes precedence over the 48-hour rule\n          if (isRescheduledBySupport) {\n            return false;\n          }\n          \n          \/\/ Check if appointment is within 48 hours (using UTC for consistency)\n          const appointmentTime = moment.utc(existingAppointment.startTime);\n          const now = moment.utc();\n          const hoursUntilAppointment = appointmentTime.diff(now, 'hours');\n          return hoursUntilAppointment <= 48;\n        }, [isEditing, existingAppointment]);\n\n        const loadExistingAppointment = async (id) => {\n          setIsLoadingAppointment(true);\n          setKey(\"location\");\n          setProgress(1);\n          let appointment = await getAppointment({ id, accessToken: params.accessToken || null });\n          const { locationLon, locationLat, serviceId, startTime, locationId } =\n            appointment;\n          \n          \/\/ Allow rescheduling past appointments when appointment was rescheduled by support\n          const rescheduleBySupport = appointment.rescheduleBySupport;\n          const isRescheduledBySupport = rescheduleBySupport === true ||\n                                        rescheduleBySupport === \"true\" ||\n                                        rescheduleBySupport === 1 ||\n                                        rescheduleBySupport === \"1\";\n          const isInPast = startTime && moment.utc(startTime).isBefore(moment.utc());\n          if (isInPast && !isRescheduledBySupport) {\n            setError(\"Ce rendez-vous est dans le pass\u00e9 et ne peut pas \u00eatre reprogramm\u00e9.\");\n            setIsLoadingAppointment(false);\n            return;\n          }\n          \n          setExistingAppointment(appointment);\n          setServiceId(appointment.serviceId);\n          setDate(new Date());\n          setSelectedLocationId(locationId);\n          if (appointment.serviceType.includes(\"Virtual Antigen Test\")) {\n            setServiceType(\"Covid Remote Test\");\n            setLocations([{ id: \"virtual\", name: \"Virtual Consultation\" }]);\n          } else if (appointment.serviceType.includes(\"Telehealth Consultation\")) {\n            setServiceType(\"Telehealth Consultation\");\n            setLocations([{ id: \"virtual\", name: \"Virtual Consultation\" }]);\n          } else {\n            await fetchLocations(locationLat, locationLon);\n          }\n          setIsLoadingAppointment(false);\n        };\n\n        const loadCountries = async () => {\n          const res = await getCountries();\n          res.countries.sort(function (a, b) {\n            return a.CountryNameFR.localeCompare(b.CountryNameFR);\n          });\n          setCountries((prev) => [...prev, ...res.countries]);\n          setCountriesById(_.keyBy(res.countries, \"Id\"));\n        };\n\n        const loadVaccines = async () => {\n          const res = await getVaccines();\n          setVaccines((prev) => [...prev, ...res.countries]);\n          setVaccinesById(_.keyBy(res.countries, \"Id\"));\n        };\n\n        useEffect(() => {\n          if (isEditing) {\n            loadExistingAppointment(params.aid);\n          }\n        }, [isEditing]);\n\n        useEffect(() => {\n          if (\n            serviceType &&\n            serviceType == \"TB Skin Test Initial Appointment\"\n          ) {\n            Swal.fire({\n              title: \"Test cutan\u00e9 anti-TB une autre fois\",\n              html: `\n                  <hr\/>\n                  <div style=\"text-align:left\">\n                    <p>A <b style=\"color:red\">Test cutan\u00e9 anti-TB une autre fois<\/b> consiste en <span style=\"font-weight:bold;text-decoration:underline\">deux<\/span> rendez-vous: <\/p>\n                    <p> <span style=\"color:red;font-weight: bold;\">Un premier rendez-vous (80$)<\/span><\/p>\n                    <p style=\"color:red;font-weight: bold;\">et un rendez-vous de lecture (40$)<\/p>\n                    <p><span style=\"font-weight:bold;color:red;\">La lecture<\/span> doit \u00eatre r\u00e9serv\u00e9 <span style=\"font-weight:bold;color:red;\">48-72 heures<\/span>apr\u00e8s le <span style=\"color:red;font-weight:bold\">premier rendez-vous<\/span><\/p>\n                    <p style=\"font-weight:bold\">Sur les \u00e9crans suivants, vous s\u00e9lectionnerez les deux rendez-vous<\/p>\n                    <p style=\"font-weight:bold\">Pour plus d'informations, veuillez visiter ce lien : <a href=\"https:\/\/cliniquevoyageur.ca\/tuberculosis-testing\/\" target=\"_blank\">Lien<\/a><\/p>\n                    <p style=\"font-weight:bold;color:blue;text-align:center\">ou appeler 1-888-224-8809<\/p>\n                    <\/div>\n      `,\n              icon: \"warning\",\n            });\n          }\n        }, [serviceType]);\n\n        useEffect(() => {\n          if (passedAppointmentType) {\n            const appointment_type = AppointmentReasons.find(\n              (reason) => reason.value === params.aType\n            );\n            if (appointment_type) {\n              setServiceType(appointment_type.value);\n            }\n          }\n        }, [passedAppointmentType]);\n\n        useEffect(() => {\n          if (serviceType && patientCount) {\n            \/\/ Clear stale slot\/location when switching services (prevents 60-min slot from Telehealth being sent with 40-min Travel Consult)\n            setTime(\"\");\n            setSelectedLocationId(null);\n            const fetchId = ++serviceFetchIdRef.current;\n            getServiceForOnlineBooking({ serviceType, patientCount }).then(\n              (res) => {\n                if (fetchId !== serviceFetchIdRef.current) return;\n                const result = res && res[0];\n                if (result && result.Id) {\n                  setService(result);\n                  setServiceId(result.Id);\n                } else {\n                  console.error(\"Failed to load service information:\", res);\n                  setError(\"Impossible de charger les informations du service. Veuillez reessayer ou appeler le 1-888-224-8809.\");\n                }\n              }\n            ).catch((err) => {\n              if (fetchId !== serviceFetchIdRef.current) return;\n              console.error(\"Error loading service:\", err);\n              setError(\"Impossible de charger les informations du service. Veuillez reessayer ou appeler le 1-888-224-8809.\");\n            });\n            const initialData = new Array(Number(patientCount))\n              .fill()\n              .map(Object);\n            setContacts(initialData);\n            \/\/ Reset validation state when patient count changes\n            setEmptyFields([]);\n            setErrorFields([]);\n            setValidationErrors([]);\n          }\n        }, [serviceType, patientCount]);\n\n        \/\/ Reset visible locations count when locations change (e.g., new search)\n        useEffect(() => {\n          setVisibleLocationsCount(10);\n        }, [locations]);\n\n        \/\/ Pre-fetch virtual availabilities when on the remote service description screen\n        const [preFetchedVirtualAvailabilities, setPreFetchedVirtualAvailabilities] = useState(null);\n        const [preFetchedVirtualAvailabilitiesPromise, setPreFetchedVirtualAvailabilitiesPromise] = useState(null);\n        \n        useEffect(() => {\n          \/\/ Only pre-fetch for virtual consultations when on the remote description screen\n          if (mainFormTabKey === \"remoteDescription\" && \n              (isRemoteService || isTelehealthCovidTest || isTelehealthConsultation) &&\n              serviceId && \n              !preFetchedVirtualAvailabilitiesPromise &&\n              !preFetchedVirtualAvailabilities) {\n            \n            \/\/ Pre-fetch virtual availabilities\n            const virtualAvailPromise = getVirtualAvailabilities({\n              serviceId: serviceId,\n              language: language,\n              \/\/ Don't pass selectedDate - fetch all availabilities\n            });\n            \n            setPreFetchedVirtualAvailabilitiesPromise(virtualAvailPromise);\n            \n            virtualAvailPromise.then(availResponse => {\n              if (availResponse && !availResponse.errorCode && !availResponse.errorMessage) {\n                setPreFetchedVirtualAvailabilities(availResponse);\n              }\n            }).catch(err => {\n              console.error(\"Error pre-fetching virtual availabilities:\", err);\n              \/\/ Don't show error to user - this is just a background optimization\n            }).finally(() => {\n              \/\/ Clear the promise so we can fetch again if serviceId or language changes\n              setPreFetchedVirtualAvailabilitiesPromise(null);\n            });\n          }\n        }, [mainFormTabKey, serviceId, language, isRemoteService, isTelehealthCovidTest, isTelehealthConsultation, preFetchedVirtualAvailabilitiesPromise, preFetchedVirtualAvailabilities]);\n\n        \/* Helper Functions *\/\n\n        const onChangeValue = (e, setter) => {\n          setter(e.target.value);\n        };\n\n        const onChangePlacesSearch = (setter, e) => {\n          setter(e);\n          \n          \/\/ If a valid place is selected, start fetching locations in the background\n          if (e && e.geometry && e.geometry.location) {\n            const lat = e.geometry.location.lat();\n            const lon = e.geometry.location.lng();\n            \n            \/\/ Start background fetch\n            const promise = getLocations({ lat, lon });\n            setBackgroundFetchPromise(promise);\n            setBackgroundFetchCompleted(false);\n            setBackgroundFetchResult(null);\n            \n            \/\/ Handle the promise\n            promise.then(response => {\n              setBackgroundFetchResult(response);\n              setBackgroundFetchCompleted(true);\n            }).catch(err => {\n              setBackgroundFetchResult({ error: err });\n              setBackgroundFetchCompleted(true);\n            });\n          }\n        };\n\n        const isValidEmail = (email) => {\n          if (!email || email.trim() === \"\") return false;\n          const trimmedEmail = email.trim();\n          \n          \/\/ Check for consecutive dots (..) anywhere in the email\n          if (trimmedEmail.includes('..')) {\n            return false;\n          }\n          \n          \/\/ Check for dots at the start or end of local or domain parts\n          const parts = trimmedEmail.split('@');\n          if (parts.length !== 2) return false;\n          const [localPart, domainPart] = parts;\n          if (localPart.startsWith('.') || localPart.endsWith('.') ||\n              domainPart.startsWith('.') || domainPart.endsWith('.')) {\n            return false;\n          }\n          \n          \/\/ Comprehensive email validation regex\n          const emailRegex = \/^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$\/;\n          \/\/ Additional check: no commas, spaces, or other invalid characters\n          if (trimmedEmail.includes(',') || trimmedEmail.includes(' ') || trimmedEmail.includes(';') || trimmedEmail.includes(':')) {\n            return false;\n          }\n          return emailRegex.test(trimmedEmail);\n        };\n\n        const validateEmails = () =>\n          contacts.forEach((c, index) => {\n            let email = document.getElementById(`email${index}`);\n            if (email && email.value) {\n              if (!isValidEmail(email.value)) {\n                \/\/ Provide specific error message for consecutive dots\n                if (email.value.includes('..')) {\n                  email.setCustomValidity(\"Le courriel ne peut pas contenir de points cons\u00e9cutifs (..). Veuillez v\u00e9rifier les erreurs de frappe.\");\n                } else {\n                  email.setCustomValidity(\"Ce courriel est invalide. Veuillez v\u00e9rifier le format.\");\n                }\n                email.reportValidity();\n              } else {\n                email.setCustomValidity(\"\");\n              }\n            }\n          });\n\n        const onChangeContact = (e, param, index) => {\n          validateEmails();\n          let value;\n          if (param == \"newsletter\") {\n            value = e;\n          } else {\n            value = e.target.value;\n          }\n          let currentContacts = [...contacts];\n          currentContacts[index][param] = value;\n          setContacts(currentContacts);\n        };\n\n        const onSubmit = (e, action) => {\n          const currentTarget = e && e.currentTarget;\n          const form =\n            currentTarget && currentTarget.tagName === \"FORM\"\n              ? currentTarget\n              : currentTarget && typeof currentTarget.closest === \"function\"\n                ? currentTarget.closest(\"form\")\n                : null;\n\n          if (!form) {\n            e.preventDefault();\n            return;\n          }\n\n          if (form.checkValidity() === false) {\n            e.stopPropagation();\n          } else {\n            action();\n          }\n          e.preventDefault();\n        };\n\n        const verifySlotAvailability = async () => {\n          try {\n            const formattedDate = moment(date).format(\"D_M_yyyy\");\n            const response = await getAvailabilities({\n              locationId: selectedLocationId,\n              serviceId,\n              selectedDate: formattedDate,\n            });\n            \n            \/\/ Check if response is valid and has the expected structure\n            if (!response || !response.content || !response.content.availableSlots) {\n              console.warn(\"Slot verification failed: Invalid response structure\", response);\n              \/\/ If we can't verify, allow booking to proceed (network\/CORS issues shouldn't block booking)\n              \/\/ Return true to allow booking, but log the issue\n              return true;\n            }\n            \n            let dates = getMergedDates(response.content.availableSlots);\n            const dateData = dates[formattedDate];\n            if (!dateData || !dateData.slots) {\n              return false; \/\/ No availability data for this date\n            }\n            const isSlotStillAvailable = dateData.slots.some(\n              (slot) => slot.slot === time && !slot.isBookedSlot\n            );\n            return isSlotStillAvailable;\n          } catch (error) {\n            \/\/ Handle network errors, CORS errors, etc.\n            console.error(\"Error verifying slot availability:\", error);\n            \/\/ If verification fails due to network issues, allow booking to proceed\n            \/\/ The backend will handle slot availability validation\n            return true;\n          }\n        };\n\n        const getPromoParams = () => {\n          if (!promoCodeData.Id) return {};\n\n          const promoCode = promoCodeData.Id;\n          const totalAmount = service.prePaymentPrice || 0;\n          \n          \/\/ Handle null\/undefined discount values - default to 0 if not provided\n          let discountAmount = 0;\n          if (promoCodeData.discountType === \"Percentage\" && promoCodeData.promoAmount != null) {\n            discountAmount = totalAmount * (promoCodeData.promoAmount \/ 100);\n          } else if (promoCodeData.promoAmount != null) {\n            discountAmount = promoCodeData.promoAmount;\n          }\n          \n          \/\/ Ensure discountAmount is a valid number\n          discountAmount = Number(discountAmount) || 0;\n          \n          const totalPaidAmount = Number(\n            (totalAmount - discountAmount).toFixed(2)\n          );\n          window.dataLayer = window.dataLayer || [];\n          window.dataLayer.push({\n            appointmentTotal: totalPaidAmount,\n          });\n          \/\/window.dataLayer.push({'event': 'appointmentBooked'});\n          console.log(\n            \"finished pushing total paid amount to the datalayer\" +\n              totalPaidAmount\n          );\n          return {\n            promoCode,\n            totalPaidAmount,\n            discountAmount,\n          };\n        };\n\n        const getShippingLocationParams = () => {\n          if (!isRemoteService) return {};\n          const loc = sameAsContactLocation ? userLocation : shippingLocation;\n\n          return {\n            shippingStreet: loc.street,\n            shippingApartment: loc.apartment,\n            shippingCity: loc.city,\n            shippingState: loc.province,\n            shippingPostalCode: loc.postCode,\n          };\n        };\n\n        const validatePromo = async () => {\n          setIsValidatingPromoCode(true);\n          setPromoCodeData({});\n          setPromoCodeError(\"\");\n          try {\n            const promoData = await validatePromoCode({\n              code: promoCode,\n              serviceId: serviceId,\n              locationId: selectedLocationId,\n            });\n            console.log(\"validatePromo params:\", promoData);\n\n            const normalizedPromo = Array.isArray(promoData)\n              ? promoData[0]\n              : (promoData && promoData.promoCode) || promoData;\n\n            if (!normalizedPromo || (!normalizedPromo.Id && !normalizedPromo.id)) {\n              setPromoCodeError(\n                (promoData && promoData.message) ||\n                  (promoData && promoData.errorMessage) ||\n                  (promoData && promoData.error) ||\n                  \"err\"\n              );\n            } else {\n              setPromoCodeData({\n                ...normalizedPromo,\n                Id: normalizedPromo.Id || normalizedPromo.id,\n                Name: normalizedPromo.Name || normalizedPromo.code || promoCode,\n                promoAmount:\n                  normalizedPromo.promoAmount != null\n                    ? normalizedPromo.promoAmount\n                    : normalizedPromo.discountAmount,\n              });\n            }\n          } catch (err) {\n            setPromoCodeError(\"err\");\n          }\n          setIsValidatingPromoCode(false);\n        };\n\n        useEffect(() => {\n          const selectedReason = AppointmentReasons.find(r => r.value === serviceType);\n          const isPromoApplicable = (selectedReason && selectedReason.isPromoApplicable) || false;\n\n          if (autoValidatePromo && promoCode && serviceId && isPromoApplicable) {\n            validatePromo();\n            setAutoValidatePromo(false);\n          }\n        }, [promoCode, serviceId, serviceType, autoValidatePromo]);\n\n        const getPaymentParams = () => {\n          const { cardNumber, expirationDate, cvv, isValid } = paymentData;\n          return {\n            cardNumber,\n            exp: expirationDate,\n            cvv,\n            billingFirstName: contacts[0] && contacts[0].firstName,\n            billingLastName: contacts[0] && contacts[0].lastName,\n          };\n        };\n\n        const onSelectDate = (date) => {\n          setDate(date);\n          setTime(\"\");\n        };\n\n        const onSelectSlot = async (location, time, expertId, isVirtual) => {\n          setTime(time);\n          if (isVirtual) {\n            console.log(\n              \"should call the API to get the proper location based on the key\/expert as this is virtual\",\n              expertId\n            );\n            console.log(\"old loc: \", location);\n            let result = await post(\"calendar\/location-from-expert\", {\n              expertId: expertId,\n            });\n            console.log(result);\n            location = result.locationId;\n            console.log(\"new loc: \", location);\n          }\n          setSelectedLocationId(location);\n          setSelectedExpert(expertId);\n          setIsSlotModalOpen(true);\n        };\n\n        const countryList = useMemo(\n          () =>\n            countries.filter(\n              (country) => !selectedCountries.includes(country.Id)\n            ),\n          [selectedCountries, countries]\n        );\n\n        const handleSelectCountry = (newCountryId) => {\n          if (newCountryId) {\n            let newSelectedCountries = [...selectedCountries];\n            if (newSelectedCountries.includes(newCountryId)) {\n              newSelectedCountries = newSelectedCountries.filter(\n                (id) => id !== newCountryId\n              );\n            } else {\n              newSelectedCountries.push(newCountryId);\n            }\n            setSelectedCountries(newSelectedCountries);\n          }\n        };\n\n        const vaccineList = useMemo(\n          () =>\n            vaccines.filter(\n              (vaccine) => !selectedVaccines.includes(vaccine.Id)\n            ),\n          [selectedVaccines, vaccines]\n        );\n\n        const handleSelectVaccine = (newVaccineId) => {\n          if (newVaccineId) {\n            let newSelectedVaccines = [...selectedVaccines];\n            if (newSelectedVaccines.includes(newVaccineId)) {\n              newSelectedVaccines = newSelectedVaccines.filter(\n                (id) => id !== newVaccineId\n              );\n            } else {\n              newSelectedVaccines.push(newVaccineId);\n            }\n            setSelectedVaccines(newSelectedVaccines);\n          }\n        };\n\n        useEffect(() => {\n          console.log(\"isEditing:\", isEditing);\n          console.log(\"existingAppointment:\", existingAppointment);\n          \n          if (\n            isEditing &&\n            existingAppointment &&\n            existingAppointment.serviceType === \"TB Skin Test Reading\" &&\n            initialTBDateTime\n          ) {\n            const newInitialAppointment = {\n              Id: existingAppointment.Id,\n              patientName: existingAppointment.patientName,\n              serviceType: \"TB Skin Test Initial\",\n              startTime: initialTBDateTime,\n              endTime: null,\n              locationId: existingAppointment.locationId,\n              serviceId: existingAppointment.serviceId,\n              isBooster: existingAppointment.isBooster,\n              patientId: existingAppointment.patientId,\n              locationName: existingAppointment.locationName,\n            };\n\n            setFirstTbInitialAppointment(newInitialAppointment);\n            setSelectedInitialTbTestDate(moment(initialTBDateTime));\n            console.log(\"initialTBDateTime:\", initialTBDateTime);\n          }\n          }, [isEditing, existingAppointment, initialTBDateTime]);\n\n        useEffect(() => {\n          console.log(\"firstTbInitialAppointment:\", firstTbInitialAppointment);\n        }, [firstTbInitialAppointment]);\n\n        const datePickerIntervals = useMemo(() => {\n          let datePickerMinDate, datePickerMaxDate;\n\n          if (\n            isRemoteService ||\n            isTelehealthCovidTest ||\n            isTelehealthConsultation\n          ) {\n            const dateInstance = new Date();\n            const now = new Date(\n              dateInstance.getFullYear(),\n              dateInstance.getMonth(),\n              dateInstance.getDate()\n            );\n\n            datePickerMinDate = moment(now).add(NO_BYPASSED_DAYS, \"days\");\n          } else {\n            const [minBookingInterval, maxBookingInterval] =\n              params.bookingInterval ? params.bookingInterval.split(\":\") : [];\n\n            const minInterval =\n              minBookingInterval || service.minimumBookingInterval;\n\n            const maxInterval =\n              maxBookingInterval || service.maximumBookingInterval;\n\n            const bookingDate = params.bookingDate\n              ? moment(params.bookingDate, \"D\/M\/YYYY-HH:mm\")\n              : moment();\n            datePickerMinDate = minInterval\n              ? moment(bookingDate).add(Number(minInterval), \"h\")\n              : null;\n            datePickerMaxDate = maxInterval\n              ? moment(bookingDate).add(Number(maxInterval), \"h\")\n              : null;\n          }\n\n          if (firstTbInitialAppointment && selectedInitialtbTestDate) {\n            datePickerMinDate = moment(selectedInitialtbTestDate).add(48, \"h\");\n            datePickerMaxDate = moment(selectedInitialtbTestDate).add(72, \"h\");\n          }\n\n          \/\/ console.log(\"datePickerMinDate:\", datePickerMinDate && datePickerMinDate.format());\n          \/\/ console.log(\"datePickerMaxDate:\", datePickerMaxDate && datePickerMaxDate.format());\n          \n          if (minDate && maxDate) {\n            return {\n              datePickerMinDate: moment(minDate),\n              datePickerMaxDate: moment(maxDate)\n            };\n          }\n\n          return { datePickerMinDate, datePickerMaxDate };\n        }, [\n          isRemoteService,\n          isTelehealthCovidTest,\n          isTelehealthConsultation,\n          service,\n          firstTbInitialAppointment,\n          selectedInitialtbTestDate,\n          serviceType,\n          minDate, \n          maxDate\n        ]);\n\n        const { datePickerMinDate, datePickerMaxDate } = datePickerIntervals;\n\n        \/* End Helper Functions *\/\n        useEffect(() => {\n          if (serviceTypeRef.current !== serviceType) {\n            if (\n              (isTravelConsult || isTelehealthConsultation) &&\n              countries.length < 2\n            ) {\n              loadCountries();\n            } else if (shouldShowVaccineSection &#038;&#038; vaccines.length < 2) {\n              loadVaccines();\n            }\n\n            if (servicePopups[serviceType]) {\n              setSelectedServiceNote(serviceType);\n              setServiceNoteCheckboxes(Array(servicePopups[serviceType].checkboxes.length).fill(false));\n              if (serviceType === \"Private Vaccine\") {\n                setPrivateVaccineNoteModalOpen(true);\n              } else {\n                setIsServiceNoteModalOpen(true);\n              }\n            }\n            \n            serviceTypeRef.current = serviceType;\n          }\n\n          if (datePickerIntervals.datePickerMinDate) {\n            setDate(datePickerIntervals.datePickerMinDate.toDate());\n          } else {\n            setDate(new Date());\n          }\n        }, [serviceType, datePickerIntervals]);\n\n        \/* Main Functions *\/\n\n        const onSearchRemoteService = () => {\n          setBookResponse({});\n          setTime(\"\");\n          setIsSubmitting(false);\n          setSelectedLocationId(null);\n          \/\/ For virtual consultations, use a dummy location object so LocationMap can render\n          \/\/ LocationMap will handle virtual consultations differently (no getAvailabilities calls)\n          setLocations([{ id: 'virtual', isVirtual: true }]);\n          setKey(\"location\");\n          setProgress(1);\n        };\n\n        const onSearchLocation = async () => {\n          \/\/ Show loading spinner immediately\n          setIsLoadingLocations(true);\n          setBookResponse({});\n          setTime(\"\");\n          setIsSubmitting(false);\n          setSelectedLocationId(null);\n          \n          \/\/ First, ensure we have the correct service for the selected patient count\n          if (serviceType && patientCount) {\n            try {\n              const serviceResponse = await getServiceForOnlineBooking({ serviceType, patientCount });\n              const correctService = serviceResponse && serviceResponse[0];\n              \n              if (!correctService) {\n                setError(\"Aucun service disponible pour le nombre de patients s\u00e9lectionn\u00e9\");\n                return;\n              }\n              \n              \/\/ Update serviceId to the correct one before proceeding\n              setService(correctService);\n              setServiceId(correctService.Id);\n            } catch (error) {\n              setError(\"\u00c9chec du chargement des informations du service\");\n              return;\n            }\n          }\n          \n          if (!location) {\n            setIsLoadingLocations(false);\n            setNoLocationSelectedError(true);\n          } else {\n            setNoLocationSelectedError(false);\n            \n            \/\/ Check if background fetch completed\n            if (backgroundFetchCompleted && backgroundFetchResult && !backgroundFetchResult.error) {\n              \/\/ Use completed background fetch result\n              setLocations(backgroundFetchResult);\n              \n              if (!backgroundFetchResult.length) {\n                setError(\"Aucune clinique trouv\u00e9e pr\u00e8s de l'adresse s\u00e9lectionn\u00e9e. Veuillez essayer une autre adresse ou appeler le 1-888-224-8809 pour obtenir de l'aide.\");\n                setIsLoadingLocations(false);\n              } else {\n                setKey(\"location\");\n                setProgress(1);\n                setIsLoadingLocations(false);\n              }\n              \/\/ Reset background fetch state\n              setBackgroundFetchPromise(null);\n              setBackgroundFetchCompleted(false);\n              setBackgroundFetchResult(null);\n            } else if (backgroundFetchPromise) {\n              \/\/ Background fetch is still running, wait for it\n              try {\n                const response = await backgroundFetchPromise;\n                setLocations(response);\n                \n                if (!response.length) {\n                  setError(\"Aucune clinique trouv\u00e9e pr\u00e8s de l'adresse s\u00e9lectionn\u00e9e. Veuillez essayer une autre adresse ou appeler le 1-888-224-8809 pour obtenir de l'aide.\");\n                  setIsLoadingLocations(false);\n                } else {\n                  setKey(\"location\");\n                  setProgress(1);\n                  setIsLoadingLocations(false);\n                }\n              } catch (err) {\n                setError(\"\u00c9chec du chargement des cliniques. Veuillez v\u00e9rifier votre connexion Internet et r\u00e9essayer, ou appeler le 1-888-224-8809 pour obtenir de l'aide.\");\n                setIsLoadingLocations(false);\n              }\n              \/\/ Reset background fetch state\n              setBackgroundFetchPromise(null);\n              setBackgroundFetchCompleted(false);\n              setBackgroundFetchResult(null);\n            } else {\n              \/\/ No background fetch, proceed normally\n              const lat = location.geometry.location.lat();\n              const lon = location.geometry.location.lng();\n              await fetchLocations(lat, lon);\n            }\n          }\n        };\n\n\n        const fetchLocations = async (lat, lon) => {\n          setIsLoadingLocations(true);\n          setError(\"\");\n          try {\n            const response = await getLocations({ lat, lon });\n            setLocations(response);\n            \n            if (!response.length) {\n              setError(\"Aucune clinique trouv\u00e9e pr\u00e8s de l'adresse s\u00e9lectionn\u00e9e. Veuillez essayer une autre adresse ou appeler le 1-888-224-8809 pour obtenir de l'aide.\");\n            } else {\n              setKey(\"location\");\n              setProgress(1);\n            }\n          } catch (e) {\n            setError(\"\u00c9chec du chargement des cliniques. Veuillez v\u00e9rifier votre connexion Internet et r\u00e9essayer, ou appeler le 1-888-224-8809 pour obtenir de l'aide.\");\n          }\n          setIsLoadingLocations(false);\n        };\n\n        const onBook = async (serviceId, appointment) => {\n          return new Promise(async (resolve, reject) => {\n            setIsSubmitting(true);\n            isSubmittingRef.current = true; \/\/ Also set ref\n            setBookResponse({});\n            setShowEmail(true);\n            try {\n              let bookingReference = urlParams.get(\"bookingReference\");\n\n              \/\/ Removed frontend availability check - backend will validate slot availability\n              let totalPaidAmount;\n              try {\n                window.dataLayer = window.dataLayer || [];\n                window.dataLayer.push({ event: \"appointmentBooked\" });\n              } catch (e) {}\n              \/\/ If promo code waives prepayment, set amount to 0\n              if (promoCodeData.doNotRequirePrepayment) {\n                totalPaidAmount = 0;\n              } else {\n                \/\/ Calculate base amount\n                totalPaidAmount = appointment\n                  ? appointment.amount\n                  : service.prePaymentPrice;\n                \/\/ Apply discount if any\n                if (getPromoParams().discountAmount > 0) {\n                  totalPaidAmount = Number(\n                    (totalPaidAmount - getPromoParams().discountAmount).toFixed(\n                      2\n                    )\n                  );\n                }\n              }\n              var googleClickBooking;\n              try {\n                var gclidRecord;\n                var gclid =\n                  gclidRecord || JSON.parse(localStorage.getItem(\"gclid\"));\n                var isGclidValid =\n                  gclid && new Date().getTime() < gclid.expiryDate;\n                if (isGclidValid) {\n                  googleClickBooking = gclid.value;\n                }\n              } catch (e) {}\n              var microsoftClickBooking;\n              try {\n                var msclidRecord;\n                var msclid =\n                  msclidRecord || JSON.parse(localStorage.getItem(\"mscklid\"));\n                var isMsclidValid =\n                  msclid &#038;&#038; new Date().getTime() < msclid.expiryDate;\n                if (isMsclidValid) {\n                  microsoftClickBooking = msclid.value;\n                }\n              } catch (e) {}\n              \/\/ Validate birthdates before submission\n              const birthdateRegex = \/^\\d{4}-\\d{2}-\\d{2}$\/;\n              const invalidBirthdates = contacts\n                .filter(c => {\n                  \/\/ Check if birthdate is missing, empty, or doesn't match the required format\n                  return !c.birthdate || \n                         c.birthdate.trim() === '' || \n                         !birthdateRegex.test(c.birthdate) ||\n                         c.birthdate.includes('--'); \/\/ Catch incomplete dates like \"1949--\"\n                })\n                .map(c => ({ \n                  name: `${c.firstName} ${c.lastName}`, \n                  email: c.email, \n                  birthdate: c.birthdate || 'missing' \n                }));\n              \n              if (invalidBirthdates.length > 0) {\n                const errorMessage = invalidBirthdates.length === 1\n                  ? `Date de naissance invalide ou incompl\u00e8te pour ${invalidBirthdates[0].name}. Veuillez vous assurer que la date est compl\u00e8te au format AAAA-MM-JJ (p. ex., 1949-04-03).`\n                  : `Date de naissance invalide ou incompl\u00e8te pour ${invalidBirthdates.length} patient(s). Veuillez vous assurer que toutes les dates sont compl\u00e8tes au format AAAA-MM-JJ (p. ex., 1949-04-03).`;\n                \n                Swal.fire({\n                  icon: \"error\",\n                  title: \"Date de naissance invalide\",\n                  text: errorMessage,\n                  confirmButtonText: \"OK\"\n                }).then(() => {\n                  isSubmittingRef.current = false;\n                  setIsSubmitting(false);\n                });\n                reject({ errorMessage: \"Invalid birthdate format\" });\n                return;\n              }\n\n              let locationToUse = appointment\n                ? appointment.locationId\n                : selectedLocationId;\n              let expertToUse = appointment ? appointment.expertId : selectedExpert;\n              if (\n                !appointment &&\n                (isRemoteService || isTelehealthCovidTest || isTelehealthConsultation) &&\n                time\n              ) {\n                try {\n                  const virtualAvailabilityByExpert = await getVirtualAvailabilities({\n                    serviceId,\n                    selectedDate: moment(date).format(\"D_M_yyyy\"),\n                    language,\n                  });\n                  for (let key in virtualAvailabilityByExpert || {}) {\n                    const selectedDateSlot = virtualAvailabilityByExpert[key].find(\n                      (val) => val.strDate == moment(date).format(\"D_M_yyyy\")\n                    );\n                    const matchedSlot =\n                      selectedDateSlot &&\n                      selectedDateSlot.slots.find((slot) => slot.slot === time);\n                    if (matchedSlot) {\n                      expertToUse = expertToUse || key;\n                      if (!locationToUse || locationToUse === \"virtual\") {\n                        locationToUse = selectedDateSlot.locationId || locationToUse;\n                      }\n                      break;\n                    }\n                  }\n                } catch (err) {\n                  console.error(\"Error reloading virtual availability:\", err);\n                }\n              }\n              if (\n                !appointment &&\n                (isRemoteService || isTelehealthCovidTest || isTelehealthConsultation) &&\n                expertToUse\n              ) {\n                if (locationToUse === \"virtual\" || !locationToUse) {\n                  try {\n                    const result = await post(\"calendar\/location-from-expert\", {\n                      expertId: expertToUse,\n                    });\n                    locationToUse = result.locationId;\n                  } catch (err) {\n                    console.error(\"Error getting location from expert:\", err);\n                  }\n                }\n              }\n\n              if (!locationToUse || !expertToUse) {\n                Swal.fire({\n                  icon: \"error\",\n                  title: \"Erreur de r\u00e9servation\",\n                  text: \"Nous n'avons pas pu confirmer le cr\u00e9neau s\u00e9lectionn\u00e9. Veuillez choisir l'heure de nouveau.\",\n                  confirmButtonText: \"OK\"\n                }).then(() => {\n                  isSubmittingRef.current = false;\n                  setIsSubmitting(false);\n                });\n                reject({ errorMessage: \"Missing booking location or expert\" });\n                return;\n              }\n\n              let bookingParams = {\n                locationId: locationToUse,\n                serviceId: appointment ? appointment.serviceId : serviceId,\n                slot: appointment ? appointment.slot : time,\n                expertId: expertToUse,\n                date: appointment\n                  ? appointment.date\n                  : moment(date).format(\"D_M_yyyy\"),\n                contacts,\n                language: language.charAt(0).toUpperCase() + language.slice(1), \/\/doing this because virtual availabilities uses a lowercase but we need uppercase for appointment booking\n                departureDate,\n                returnDate,\n                countries: selectedCountries,\n                vaccines: selectedVaccines,\n                totalPaidAmount,\n                ...getPromoParams(),\n                ...getShippingLocationParams(),\n                ...getPaymentParams(),\n                additionalInformation: specialInstructions,\n                gclid: googleClickBooking,\n                mscklid: microsoftClickBooking,\n                saveCreditCard,\n                bookingReference: bookingReference,\n                requestId: bookingRequestIdRef.current,\n                domain: window.location.hostname,\n              };\n              \n              \/\/ Detect if running locally\n              const isLocal = window.location.protocol === 'file:' || \n                             window.location.hostname === 'localhost' || \n                             window.location.hostname === '127.0.0.1' ||\n                             window.location.hostname.includes('local');\n              \n              let response;\n              try {\n                if (isLocal) {\n                  \/\/ Bypass reCAPTCHA for local testing\n                  console.log(\"Local environment detected, bypassing reCAPTCHA...\");\n                  response = await book(bookingParams);\n                } else {\n                  \/\/ Use reCAPTCHA for production\n                  console.log(\"Production environment, using reCAPTCHA...\");\n                  response = await useRecaptcha(book, bookingParams);\n                }\n              } catch (apiError) {\n                \/\/ Handle API errors (network errors, etc.)\n                console.log(\"API call error:\", apiError);\n                throw apiError;\n              }\n              \n              \/\/ Check for error responses from backend\n              \/\/ Check for nested error structure from compatibility payloads\n              const hasNestedError = response.content && (response.content.success === false || !response.content.Id);\n              const nestedErrorMessage = response.content && response.content.message;\n              const partialFailure =\n                response.partialFailure ||\n                (response.content && response.content.partialFailure);\n              const partialFailureMessage =\n                (partialFailure && partialFailure.userMessage) ||\n                \"Votre rendez-vous a \u00e9t\u00e9 r\u00e9serv\u00e9, mais certains d\u00e9tails n'ont pas pu \u00eatre enregistr\u00e9s. Notre \u00e9quipe doit v\u00e9rifier la r\u00e9servation avant votre visite. Veuillez contacter la clinique si vous n'avez pas de nouvelles.\";\n              \n              if (response.errorMessage || response.errorCode || !response.Id || hasNestedError) {\n                let errorTitle = \"Erreur de r\u00e9servation\";\n                let errorText = response.errorMessage || nestedErrorMessage || response.message || \"Une erreur s'est produite lors de la r\u00e9servation de votre rendez-vous. Veuillez r\u00e9essayer ou contacter le support.\";\n                \n                \/\/ Slot no longer available: send user back to choose another time, keep their info\n                const isSlotNoLongerAvailable = response.errorCode === \"SLOT_NO_LONGER_AVAILABLE\" ||\n                  (response.errorMessage && response.errorMessage.toLowerCase().includes(\"no longer available\"));\n                if (isSlotNoLongerAvailable) {\n                  errorTitle = \"Cr\u00e9neau non disponible\";\n                  errorText = \"Ce cr\u00e9neau n'est plus disponible. Veuillez en choisir un autre. Vos informations ont \u00e9t\u00e9 conserv\u00e9es.\";\n                  Swal.fire({\n                    icon: \"warning\",\n                    title: errorTitle,\n                    text: errorText,\n                    confirmButtonText: \"Choisir un autre cr\u00e9neau\"\n                  }).then(() => {\n                    setKey(\"appointment\");\n                    setProgress(0);\n                    setPaymentError(\"\");\n                    isSubmittingRef.current = false;\n                    setIsSubmitting(false);\n                  });\n                  isSubmittingRef.current = false;\n                  setIsSubmitting(false);\n                  reject(response);\n                  return;\n                }\n                \n                \/\/ Extract specific decline reason from error message\n                if (response.errorMessage && response.errorMessage.includes(\"DECLINED: NSF\")) {\n                  errorTitle = \"Paiement refus\u00e9\";\n                  errorText = \"Fonds insuffisants. Veuillez utiliser une autre m\u00e9thode de paiement ou contacter votre banque.\";\n                } else if (response.errorMessage && response.errorMessage.includes(\"DECLINED\")) {\n                  errorTitle = \"Paiement refus\u00e9\";\n                  errorText = \"Votre paiement a \u00e9t\u00e9 refus\u00e9. Veuillez v\u00e9rifier les d\u00e9tails de votre carte ou utiliser une autre m\u00e9thode de paiement.\";\n                } else if (!response.Id || hasNestedError) {\n                  errorTitle = \"R\u00e9servation \u00e9chou\u00e9e\";\n                  errorText = nestedErrorMessage || \"Votre rendez-vous n'a pas pu \u00eatre r\u00e9serv\u00e9. Veuillez v\u00e9rifier vos informations et r\u00e9essayer, ou contacter le support si le probl\u00e8me persiste.\";\n                }\n                \n                \/\/ Check if error is related to email validation\n                const isEmailError = nestedErrorMessage && nestedErrorMessage.includes(\"INVALID_EMAIL_ADDRESS\");\n                if (isEmailError) {\n                  errorTitle = \"Adresse courriel invalide\";\n                  errorText = \"L'adresse courriel que vous avez saisie est invalide. Veuillez v\u00e9rifier les erreurs de frappe (p. ex., points cons\u00e9cutifs, caract\u00e8res manquants) et r\u00e9essayer.\";\n                }\n                \n                Swal.fire({\n                  icon: \"error\",\n                  title: errorTitle,\n                  text: errorText,\n                  confirmButtonText: \"OK\"\n                }).then(() => {\n                  \/\/ After user acknowledges error, allow them to go back and fix it\n                  \/\/ Don't change the key\/progress - keep them on the contact form so they can correct errors\n                  setPaymentError(response.errorMessage || nestedErrorMessage || errorText);\n                  \/\/ Reset loading state\n                  isSubmittingRef.current = false;\n                  setIsSubmitting(false);\n                });\n                \n                setPaymentError(response.errorMessage || nestedErrorMessage || errorText);\n                \/\/ Reset loading state before rejecting\n                isSubmittingRef.current = false;\n                setIsSubmitting(false);\n                reject(response);\n                return;\n              }\n\n              if (response.Id && partialFailure) {\n                console.warn(\"Booking completed with partial failure:\", partialFailure);\n                const responseWithWarning = {\n                  ...response,\n                  partialFailure: {\n                    ...(typeof partialFailure === \"object\" ? partialFailure : {}),\n                    userMessage: partialFailureMessage,\n                  },\n                };\n                setBookResponse(responseWithWarning);\n                setKey(\"complete\");\n                setProgress(3);\n                \/\/ Rotate the idempotency key so a subsequent booking on the same page gets a fresh key\n                bookingRequestIdRef.current = typeof crypto !== \"undefined\" && crypto.randomUUID ? crypto.randomUUID() : `br-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n                isSubmittingRef.current = false;\n                setIsSubmitting(false);\n                Swal.fire({\n                  icon: \"warning\",\n                  title: \"R\u00e9servation effectu\u00e9e - v\u00e9rification requise\",\n                  text: partialFailureMessage,\n                  confirmButtonText: \"OK\"\n                });\n                resolve(response.Id);\n                return;\n              }\n              \n              \/\/ Success - verify we have a valid appointment ID\n              if (response.Id) {\n                setBookResponse(response);\n                setKey(\"complete\");\n                setProgress(3);\n                \/\/ Rotate the idempotency key so a subsequent booking on the same page gets a fresh key\n                bookingRequestIdRef.current = typeof crypto !== \"undefined\" && crypto.randomUUID ? crypto.randomUUID() : `br-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n                resolve(response.Id);\n              } else {\n                \/\/ This shouldn't happen, but handle it just in case\n                Swal.fire({\n                  icon: \"error\",\n                  title: \"R\u00e9servation \u00e9chou\u00e9e\",\n                  text: \"Votre rendez-vous n'a pas pu \u00eatre confirm\u00e9. Veuillez contacter le support.\",\n                  confirmButtonText: \"OK\"\n                });\n                reject({ errorMessage: \"Aucun ID de rendez-vous retourn\u00e9 par le serveur\" });\n              }\n            } catch (err) {\n              console.log(\"Error booking appointment:\", err);\n              let errorTitle = \"Erreur de r\u00e9servation\";\n              let errorText = \"Une erreur s'est produite lors de la r\u00e9servation de votre rendez-vous. Veuillez r\u00e9essayer ou contacter le support.\";\n              \n              if (err.errorMessage) {\n                errorText = err.errorMessage;\n                \/\/ Extract specific decline reason from error message\n                if (err.errorMessage.includes(\"DECLINED: NSF\")) {\n                  errorTitle = \"Paiement refus\u00e9\";\n                  errorText = \"Fonds insuffisants. Veuillez utiliser une autre m\u00e9thode de paiement ou contacter votre banque.\";\n                } else if (err.errorMessage.includes(\"DECLINED\")) {\n                  errorTitle = \"Paiement refus\u00e9\";\n                  errorText = \"Votre paiement a \u00e9t\u00e9 refus\u00e9. Veuillez v\u00e9rifier les d\u00e9tails de votre carte ou utiliser une autre m\u00e9thode de paiement.\";\n                }\n                setPaymentError(err.errorMessage);\n              } else if (err.errorCode) {\n                errorTitle = \"Erreur de r\u00e9servation\";\n                errorText = err.message || `Code d'erreur : ${err.errorCode}. Veuillez contacter le support si ce probl\u00e8me persiste.`;\n                setError(err.errorCode);\n              } else if (err.message) {\n                errorText = err.message;\n              } else if (typeof err === 'string') {\n                errorText = err;\n              }\n              \n              \/\/ Check if error is related to email validation\n              const isEmailError = err.errorMessage && err.errorMessage.includes(\"INVALID_EMAIL_ADDRESS\");\n              if (isEmailError) {\n                errorTitle = \"Adresse courriel invalide\";\n                errorText = \"L'adresse courriel que vous avez saisie est invalide. Veuillez v\u00e9rifier les erreurs de frappe (p. ex., points cons\u00e9cutifs, caract\u00e8res manquants) et r\u00e9essayer.\";\n              }\n              \n              Swal.fire({\n                icon: \"error\",\n                title: errorTitle,\n                text: errorText,\n                confirmButtonText: \"OK\"\n              }).then(() => {\n                \/\/ After user acknowledges error, allow them to go back and fix it\n                \/\/ Reset loading state\n                isSubmittingRef.current = false;\n                setIsSubmitting(false);\n              });\n              \n              reject(err);\n            }\n            isSubmittingRef.current = false; \/\/ Reset ref\n            setIsSubmitting(false);\n          });\n        };\n\n        const onBeginBook = async () => {\n          setPaymentError(\"\");\n          setError(\"\");\n          if (\n            !promoCodeData.doNotRequirePrepayment &&\n            service.prePaymentPrice > 0 &&\n            !paymentData.isValid\n          ) {\n            setShouldShowPaymentErrors(true);\n            Swal.fire({\n              icon: \"error\",\n              title: \"Informations de paiement invalides\",\n              text: \"Veuillez v\u00e9rifier les d\u00e9tails de votre carte et r\u00e9essayer.\",\n            });\n            setIsSubmitting(false); \/\/ Reset if validation fails\n            isSubmittingRef.current = false;\n            return;\n          }\n          \/\/ isSubmitting is already set in handleSubmit, but ensure it's set here too\n          setIsSubmitting(true);\n          isSubmittingRef.current = true;\n          try {\n            let token;\n            const response = await checkDuplicateAppointments({\n              email: contacts[0].email,\n              serviceId,\n            });\n            if (response.appointmentCount) {\n              setIsBookingModalOpen(true);\n            } else {\n              if (!firstTbInitialAppointment && !SecondTbInitialAppointment) {\n                await onBook(serviceId, undefined);\n              } else {\n                try {\n                  await onBook(\n                    firstTbInitialAppointment.serviceId,\n                    firstTbInitialAppointment\n                  );\n                  await onBook(\n                    SecondTbInitialAppointment.serviceId,\n                    SecondTbInitialAppointment\n                  );\n                } catch (err) {\n                  const errorMsg = err.errorMessage || err.message || \"Une erreur s'est produite lors de la r\u00e9servation. Veuillez r\u00e9essayer ou appeler le 1-888-224-8809 pour obtenir de l'aide.\";\n                  setError(errorMsg);\n                }\n              }\n            }\n          } catch (err) {\n            const errorMsg = err.errorMessage || err.message || \"Une erreur s'est produite. Veuillez r\u00e9essayer ou appeler le 1-888-224-8809 pour obtenir de l'aide.\";\n            setError(errorMsg);\n          }\n          isSubmittingRef.current = false;\n          setIsSubmitting(false);\n        };\n\n        const onReschedule = async () => {\n          isSubmittingRef.current = true;\n          setIsSubmitting(true);\n          setError(\"\");\n          try {\n            \/\/ Allow rescheduling past appointments when appointment was rescheduled by support\n            const rescheduleBySupport = existingAppointment && existingAppointment.rescheduleBySupport;\n            const isRescheduledBySupport = rescheduleBySupport === true ||\n                                          rescheduleBySupport === \"true\" ||\n                                          rescheduleBySupport === 1 ||\n                                          rescheduleBySupport === \"1\";\n            const isInPast = existingAppointment && existingAppointment.startTime &&\n                            moment.utc(existingAppointment.startTime).isBefore(moment.utc());\n            if (isInPast && !isRescheduledBySupport) {\n              setError(\"Ce rendez-vous est dans le pass\u00e9 et ne peut pas \u00eatre reprogramm\u00e9.\");\n              isSubmittingRef.current = false;\n              setIsSubmitting(false);\n              return;\n            }\n            \n            const isReschedulingInitial = (\n              (existingAppointment && existingAppointment.serviceType && existingAppointment.serviceType.toLowerCase().includes(\"tb skin test initial\")) ||\n              serviceType === \"TB Skin Test Initial Appointment\"\n            );\n\n            if (isReschedulingInitial) {\n              const readingId = (SecondTbInitialAppointment && (SecondTbInitialAppointment.id || SecondTbInitialAppointment.Id)) || tbReadingId || readingAid;\n              if (!readingId) {\n                setIsTbReadingSupportModalOpen(true);\n                isSubmittingRef.current = false;\n                setIsSubmitting(false);\n                return;\n              }\n\n              const initialSlot = (firstTbInitialAppointment && firstTbInitialAppointment.slot) || time;\n              const initialDateStr = (firstTbInitialAppointment && firstTbInitialAppointment.date) || moment(date).format(\"D_M_YYYY\");\n              const initialLocationId = (firstTbInitialAppointment && firstTbInitialAppointment.locationId) || selectedLocationId;\n              const initialExpertId = (firstTbInitialAppointment && firstTbInitialAppointment.expertId) || selectedExpert;\n\n              await Promise.all([\n                rescheduleAppointment({\n                  id: params.aid,\n                  accessToken: params.accessToken || null,\n                  location: initialLocationId,\n                  slot: initialSlot,\n                  room: initialExpertId,\n                  strDate: initialDateStr,\n                }),\n                rescheduleAppointment({\n                  id: readingId,\n                  location: SecondTbInitialAppointment.locationId,\n                  slot: SecondTbInitialAppointment.slot,\n                  room: SecondTbInitialAppointment.expertId,\n                  strDate: SecondTbInitialAppointment.date,\n                })\n              ]);\n\n              setKey(\"complete\");\n              setProgress(3);\n            } else {\n              \/\/ This is the default logic for any other single appointment, including the reading test itself.\n              \/\/ For virtual consultations, ensure we use the correct location and expert from the selected slot\n              let locationToUse = selectedLocationId;\n              let expertToUse = selectedExpert;\n              \n              \/\/ If it's a virtual consultation and we have the location\/expert from slot selection, use those\n              if ((isRemoteService || isTelehealthCovidTest || isTelehealthConsultation) && selectedExpert) {\n                \/\/ The selectedExpert should already contain the expert ID from slot selection\n                \/\/ And selectedLocationId should have been set by onSelectSlot after calling getLocationFromExpert\n                \/\/ But if selectedLocationId is still \"virtual\", we need to get it from the expert\n                if (selectedLocationId === \"virtual\" || !selectedLocationId) {\n                  try {\n                    const result = await post(\"calendar\/location-from-expert\", {\n                      expertId: selectedExpert,\n                    });\n                    locationToUse = result.locationId;\n                  } catch (err) {\n                    console.error(\"Error getting location from expert:\", err);\n                    \/\/ Fall back to selectedLocationId if API call fails\n                  }\n                } else {\n                  locationToUse = selectedLocationId;\n                }\n                expertToUse = selectedExpert;\n              }\n              \n              await rescheduleAppointment({\n                id: params.aid,\n                accessToken: params.accessToken || null,\n                location: locationToUse,\n                slot: time,\n                room: expertToUse,\n                strDate: moment(date).format(\"D_M_YYYY\"),\n              });\n              setKey(\"complete\");\n              setProgress(3);\n            }\n          } catch (err) {\n            const errorMsg = err.errorMessage || err.message || \"Une erreur s'est produite lors du changement d'heure. Veuillez r\u00e9essayer ou appeler le 1-888-224-8809 pour obtenir de l'aide.\";\n            setError(errorMsg);\n          }\n          isSubmittingRef.current = false;\n          setIsSubmitting(false);\n        };\n\n        \/* End Main Functions *\/\n\n        const renderMainForm = () => (\n          <Form\n            onSubmit={(e) => {\n              isRemoteService ||\n              isTelehealthCovidTest ||\n              isTelehealthConsultation\n                ? onSubmit(e, () => setMainFormTabKey(\"remoteDescription\"))\n                : onSubmit(e, onSearchLocation);\n            }}\n          >\n            <Row className=\"p-0\">\n              <DropDown\n                options={AppointmentReasons}\n                onChange={(e) => onChangeValue(e, setServiceType)}\n                value={serviceType}\n                Id=\"appointmentDropdown\"\n              \/>\n              {!isRemoteService &&\n                !isTelehealthCovidTest &&\n                !isTelehealthConsultation && (\n                  <Col className=\"py-2\" md={4}>\n                    <PlacesInput\n                      onChange={(e) => onChangePlacesSearch(setLocation, e)}\n                      id=\"service-location\"\n                      required\n                      md={12}\n                    \/>\n                  <\/Col>\n                )}\n              {!isSinglePatientService && (\n                <DropDown\n                  options={NumberOfPeople}\n                  onChange={(e) => onChangeValue(e, setPatientCount)}\n                \/>\n              )}\n              {(isRemoteService || isTelehealthCovidTest || isTelehealthConsultation) && (\n                <DropDown\n                  options={LanguageOptions}\n                  value={language}\n                  onChange={(e) => {\n                    onChangeValue(e, setLanguage);\n                    localStorage.setItem('selectedLanguage', e.target.value);\n                  }}\n                \/>\n              )}\n            <\/Row>\n            <div className=\"d-flex justify-content-end my-5\">\n              <Button\n                type=\"submit\"\n                className=\"button\"\n                id=\"searchLocationsButton\"\n              >\n                Suivant\n              <\/Button>\n            <\/div>\n            {showNoLocationSelectedError && (\n              <div className=\"text-start pr-2 text-danger h6\">\n                Veuillez choisir l'emplacement appropri\u00e9 dans l'option de\n                recherche.\n              <\/div>\n            )}\n            {isServiceNoteModalOpen && (\n              <Modal\n                isOpen={isServiceNoteModalOpen}\n                onClose={handleCloseServiceNote}\n                onCancel={handleCancelServiceNote}\n                onConfirm={handleConfirmServiceNote}\n                message={() => (\n                  <div>\n                    <div className=\"text-center mb-3\">\n                      <p>{servicePopups[selectedServiceNote].description()}<\/p>\n                    <\/div>\n\n                    <div className=\"text-start ps-3 pe-2\">\n                      {servicePopups[selectedServiceNote].checkboxes.map((label, idx) => (\n                        <Form.Check\n                          key={idx}\n                          type=\"checkbox\"\n                          label={label}\n                          checked={serviceNoteCheckboxes[idx]}\n                          onChange={e => {\n                            const upd = [...serviceNoteCheckboxes];\n                            upd[idx] = e.target.checked;\n                            setServiceNoteCheckboxes(upd);\n                          }}\n                          className=\"mb-2\"\n                        \/>\n                      ))}\n                    <\/div>\n\n                    {checkboxError && (\n                      <div className=\"text-danger mt-2\">\n                        {checkboxError}\n                      <\/div>\n                    )}\n                  <\/div>\n                )}\n                confirmButtonLabel=\"Confirmer\"\n                cancelButtonLabel=\"Annuler\"\n              \/>\n            )}\n            {isPrivateVaccineNoteModalOpen && (\n              <Modal\n                isOpen={isPrivateVaccineNoteModalOpen}\n                onClose={handleCloseServiceNote}\n                onCancel={handleCancelServiceNote}\n                onConfirm={handleConfirmServiceNote}\n                confirmButtonLabel=\"Ok\"\n                cancelButtonLabel=\"Cancel\"\n                message={() => servicePopups[\"Private Vaccine\"].description()}\n                showCancel={true}\n              \/>\n            )}\n          <\/Form>\n        );\n\n        function defaultTravelConsult() {\n          console.log(\"sending user to travel consult\");\n          setServiceType(\"Travel Consult\");\n          setPrivateVaccineNoteModalOpen(false);\n        }\n\n        const renderRemoteServiceDescription = () => (\n          <Form onSubmit={(e) => onSubmit(e, onSearchRemoteService)}>\n            <Row>\n              <DropDown\n                options={AppointmentReasons}\n                value={_.find(AppointmentReasons, { value: serviceType }).value}\n                disabled\n              \/>\n              <DropDown\n                options={NumberOfPeople}\n                value={\n                  _.find(NumberOfPeople, {\n                    value: patientCount,\n                  }).value\n                }\n                disabled\n              \/>\n              <DropDown\n                options={LanguageOptions}\n                value={language}\n                onChange={(e) => {\n                  setLanguage(e.target.value);\n                  localStorage.setItem(\"selectedLanguage\", e.target.value);\n                }}\n              \/>\n              <Col className=\"d-flex justify-content-end\">\n                <Button\n                  onClick={() => {\n                    setMainFormTabKey(\"main\");\n                  }}\n                  style={{\n                    backgroundColor: \"white\",\n                    color: \"#1e73be\",\n                    border: \"0px solid white\",\n                  }}\n                >\n                  S\u00e9lectionnez un autre test\n                <\/Button>\n              <\/Col>\n            <\/Row>\n            {isRemoteService && <RemoteServiceDescription \/>}\n            {isTelehealthCovidTest && <RemoteSupervisionServiceDescription \/>}\n            {isTelehealthConsultation && <RemoteConsultationDescription \/>}\n            <div className=\"d-flex justify-content-end my-5\">\n              <Button\n                className=\"button light mx-3\"\n                onClick={() => {\n                  setMainFormTabKey(\"main\");\n                }}\n              >\n                Retour\n              <\/Button>\n              <Button type=\"submit\" className=\"button\" disabled={!serviceId}>\n                R\u00e9servez maintenant\n              <\/Button>\n            <\/div>\n          <\/Form>\n        );\n\n        const renderMainFormTabs = () => (\n          <Tabs\n            id=\"controlled-tab3\"\n            activeKey={mainFormTabKey}\n            onSelect={(k) => setMainFormTabKey(k)}\n            className=\"border-0\"\n          >\n            <Tab eventKey=\"main\" title=\"Book\" disabled tabClassName=\"d-none\">\n              {renderMainForm()}\n            <\/Tab>\n            <Tab\n              eventKey=\"remoteDescription\"\n              title=\"Remote Service Description\"\n              disabled\n              tabClassName=\"d-none\"\n            >\n              {renderRemoteServiceDescription()}\n            <\/Tab>\n          <\/Tabs>\n        );\n\n        useEffect(() => {\n          const timeoutId = setTimeout(async () => {\n            if (contacts[0] && contacts[0].email && serviceId) {\n              try {\n                const duplicateResponse = await checkDuplicateAppointments({\n                  email: contacts[0].email,\n                  serviceId,\n                });\n\n                if (duplicateResponse.appointmentCount > 0) {\n                  console.log(\"Rendez-vous dupliqu\u00e9 existant :\", duplicateResponse);\n                  setIsRebooking(true);\n                }\n              } catch (err) {\n                console.error(\"Erreur lors de la v\u00e9rification des doublons :\", err);\n              }\n            }\n          }, 500); \/\/ Wait 500ms after user stops typing\n\n          return () => clearTimeout(timeoutId);\n        }, [contacts, serviceId]);\n\n        const renderLocationForm = () => {\n          const formattedDate = moment(date).format(\"dddd, MMM D\") || \"\";\n          const t = time.split(\"_\")[0] || \"\";\n          \n          \/\/ Only show first N locations\n          const locationsToShow = locations.slice(0, visibleLocationsCount);\n          const hasMoreLocations = locations.length > visibleLocationsCount;\n\n          return (\n            <Container fluid>\n              {service && !isEditing && <ServiceInfo \/>}\n              {isEditing && <AppointmentInfo tab={\"location\"} \/>}\n              <div\n                className={\"sticky-top\"}\n                style={{\n                  zIndex:\n                    isSlotModalOpen ||\n                    isDateModalOpen ||\n                    isRescheduleFeeModalOpen\n                      ? 2\n                      : 10,\n                }}\n              >\n                <DatePicker\n                  selectedDate={date}\n                  onChange={onSelectDate}\n                  showDateModal={() => setIsDateModalOpen(true)}\n                  minDate={\n                    minDate\n                      ? minDate\n                      : datePickerMinDate\n                        ? datePickerMinDate.toDate()\n                        : null\n                  }\n                  maxDate={\n                    maxDate\n                      ? maxDate\n                      : datePickerMaxDate\n                        ? datePickerMaxDate.toDate()\n                        : null\n                  }\n                  disableNavigation={serviceType === \"TB Skin Test Reading\" || existingAppointment.serviceType === \"TB Skin Test Reading\" }\n                \/>\n              <\/div>\n              <Row className=\"py-3\">\n                <Col lg={12}>\n                  {locationsToShow.filter(loc => loc && (loc.id || loc.Id)).map((loc) => (\n                    <LocationMap\n                      location={loc}\n                      serviceId={serviceId}\n                      serviceType={serviceType}\n                      selectedDate={moment(date).format(\"D_M_yyyy\")}\n                      onSelectDate={setDate}\n                      onSelectSlot={onSelectSlot}\n                      selectedLocationId={selectedLocationId}\n                      selectedTime={time}\n                      key={`${loc.id || loc.Id}-${serviceId}`}\n                      refetch={refetchSlots}\n                      isRemoteService={\n                        isRemoteService ||\n                        isTelehealthCovidTest ||\n                        isTelehealthConsultation\n                      }\n                      preFetchedVirtualAvailabilities={preFetchedVirtualAvailabilities}\n                      preFetchedVirtualAvailabilitiesPromise={preFetchedVirtualAvailabilitiesPromise}\n                      isTelehealthCovidTest={isTelehealthCovidTest}\n                      isTelehealthConsultation={isTelehealthConsultation}\n                      minDate={datePickerMinDate}\n                      maxDate={datePickerMaxDate}\n                      time={time}\n                      setIsFirstInitialTbConfirmed={\n                        setIsFirstInitialTbConfirmed\n                      }\n                      isFirstInitialTbConfirmed={isFirstInitialTbConfirmed}\n                      firstTbInitialAppointment={firstTbInitialAppointment}\n                      language={language} \n                    \/>\n                  ))}\n                  \n                  {\/* Load More Locations Button *\/}\n                  {hasMoreLocations && (\n                    <div className=\"text-center my-4\">\n                      <Button\n                        className=\"button\"\n                        onClick={() => {\n                          \/\/ Load next 10 locations\n                          setVisibleLocationsCount(prev => Math.min(prev + 10, locations.length));\n                        }}\n                        style={{\n                          padding: '12px 24px',\n                          fontSize: '16px',\n                          fontWeight: '500'\n                        }}\n                      >\n                        Charger Plus de Lieux ({locations.length - visibleLocationsCount} restants)\n                      <\/Button>\n                    <\/div>\n                  )}\n                <\/Col>\n              <\/Row>\n              <div className=\"d-flex justify-content-end my-4\">\n                {!params.aid && (\n                  <Button\n                    className=\"button mx-3\"\n                    onClick={() => {\n                      \/\/ When on TB reading step: go back to initial time slots (stay on location)\n                      if (\n                        isFirstInitialTbConfirmed &&\n                        firstTbInitialAppointment &&\n                        (serviceId == \"a0X7V00000JljfeUAB\" ||\n                        serviceId == \"a0X7V00000JljfjUAB\" ||\n                        serviceId == \"a0X7V00000JloC2UAJ\" ||\n                        serviceId == \"a0X7V00000JloBxUAJ\" ||\n                        serviceId == \"a0XOJ000002Ys1d2AC\" ||\n                        serviceId == \"a0XOJ000002Ys6T2AS\")\n                      ) {\n                        const initialServiceId = firstTbInitialAppointment.serviceId;\n                        setIsFirstInitialTbConfirmed(false);\n                        setSelectedInitialTbTestDate(null);\n                        setFirstTbInitialAppointment(null);\n                        setSecondTbInitialAppointment(null);\n                        setServiceType(\"TB Skin Test Initial Appointment\");\n                        setServiceId(initialServiceId);\n                        setTime(\"\");\n                        AppointmentReasons = AppointmentReasons.filter(\n                          (appointment) =>\n                            appointment.value != \"TB Skin Test Reading\"\n                        );\n                        setProgress(1);\n                        return;\n                      }\n                      setKey(\"appointment\");\n                      setProgress(0);\n                      if (\n                        serviceId == \"a0X7V00000JljfeUAB\" ||\n                        serviceId == \"a0X7V00000JljfjUAB\" ||\n                        serviceId == \"a0X7V00000JloC2UAJ\" ||\n                        serviceId == \"a0X7V00000JloBxUAJ\" ||\n                        serviceId == \"a0XOJ000002Ys1d2AC\" ||\n                        serviceId == \"a0XOJ000002Ys6T2AS\"\n                      ) {\n                        setIsFirstInitialTbConfirmed(false);\n                        setSelectedInitialTbTestDate(null);\n                        setFirstTbInitialAppointment(null);\n                        setSecondTbInitialAppointment(null);\n                        setServiceType(\"TB Skin Test Initial Appointment\");\n                        AppointmentReasons = AppointmentReasons.filter(\n                          (appointment) =>\n                            appointment.value != \"TB Skin Test Reading\"\n                        );\n                      }\n                    }}\n                  >\n                    S\u00e9lectionnez un Autre Type de Rendez-vous\n                  <\/Button>\n                )}\n              <\/div>\n              <Modal\n                isOpen={isSlotModalOpen}\n                onConfirm={async () => {\n                  if (\n                    serviceId == \"a0X7V00000JljfeUAB\" ||\n                    serviceId == \"a0X7V00000JljfjUAB\" ||\n                    serviceId == \"a0X7V00000JloC2UAJ\" ||\n                    serviceId == \"a0X7V00000JloBxUAJ\" ||\n                    serviceId == \"a0XOJ000002Ys1d2AC\" ||\n                    serviceId == \"a0XOJ000002Ys6T2AS\"\n                  ) {\n                    if (isEditing && existingAppointment && existingAppointment.serviceType === \"TB Skin Test Reading\") {\n                      setKey(\"confirm\");\n                      setProgress(2);\n                      window.scroll({ top: 0, left: 0, behavior: \"instant\" });\n                      return;\n                    }\n\n                    if (!firstTbInitialAppointment) {\n                      setSelectedInitialTbTestDate(moment(date));\n                      const currentService =\n                        serviceType &&\n                        _.find(AppointmentReasons, [\"value\", serviceType])\n                          .label;\n                      const loc = _.find(locations, [\"id\", selectedLocationId]);\n                      setFirstTbInitialAppointment({\n                        locationId: selectedLocationId,\n                        serviceId,\n                        slot: time,\n                        expertId: selectedExpert,\n                        date: moment(date).format(\"D_M_yyyy\"),\n                        language: \"French\",\n                        saveCreditCard,\n                        service: currentService,\n                        location: loc,\n                        amount: service.prePaymentPrice,\n                        discountAmount: getPromoParams().discountAmount,\n                      });\n                      setIsFirstInitialTbConfirmed(true);\n                      AppointmentReasons.push({\n                        label: \"TB Skin Test Reading\",\n                        value: \"TB Skin Test Reading\",\n                      });\n                      setServiceType(\"TB Skin Test Reading\");\n                      \/\/ Await TB Reading service fetch so availability uses 15-min slots (not 20-min Initial)\n                      \/\/ Must complete before modal closes to avoid race: user selecting date before serviceId updates\n                      try {\n                        const res = await getServiceForOnlineBooking({\n                          serviceType: \"TB Skin Test Reading\",\n                          patientCount,\n                        });\n                        const readingService = res && res[0];\n                        if (readingService && readingService.Id) {\n                          setService(readingService);\n                          setServiceId(readingService.Id);\n                        }\n                      } catch (err) {\n                        console.error(\"Failed to fetch TB Reading service:\", err);\n                      }\n                      return;\n                    }\n                    \/\/ When selecting the Reading service, explicitly fetch the TB Reading serviceId\n                    \/\/ to ensure we use 15-min slots (not the 20-min Initial serviceId)\n                    if (serviceType === \"TB Skin Test Reading\") {\n                      const currentService =\n                        serviceType &&\n                        _.find(AppointmentReasons, [\"value\", serviceType])\n                          .label;\n                      const loc = _.find(locations, [\"id\", selectedLocationId]);\n\n                      let readingServiceId = serviceId;\n                      let readingAmount = service.prePaymentPrice;\n                      try {\n                        const readingServiceRes = await getServiceForOnlineBooking({\n                          serviceType: \"TB Skin Test Reading\",\n                          patientCount,\n                        });\n                        const readingService = readingServiceRes && readingServiceRes[0];\n                        if (readingService && readingService.Id) {\n                          readingServiceId = readingService.Id;\n                          readingAmount = readingService.prePaymentPrice;\n                          setService(readingService);\n                          setServiceId(readingServiceId);\n                        }\n                      } catch (err) {\n                        console.error(\"Failed to fetch TB Reading service:\", err);\n                      }\n\n                      setSecondTbInitialAppointment((prev) => ({\n                        ...(prev || {}),\n                        id: (prev && (prev.id || prev.Id)) || tbReadingId || readingAid || undefined,\n                        locationId: selectedLocationId,\n                        serviceId: readingServiceId,\n                        slot: time,\n                        expertId: selectedExpert,\n                        date: moment(date).format(\"D_M_YYYY\"),\n                        language: \"French\",\n                        saveCreditCard,\n                        service: currentService,\n                        location: loc,\n                        amount: readingAmount,\n                        discountAmount: getPromoParams().discountAmount,\n                      }));\n                    }\n                    setKey(isEditing ? \"confirm\" : \"contact\");\n                    setProgress(2);\n                    window.scroll({ top: 0, left: 0, behavior: \"instant\" });\n                  } else {\n                    if (shouldShowRescheduleFeeOpen) {\n                      setIsRescheduleFeeModalOpen(true);\n                    } else {\n                      if (time) {\n                        setKey(isEditing ? \"confirm\" : \"contact\");\n                        setProgress(2);\n                        window.scroll({ top: 0, left: 0, behavior: \"instant\" });\n                      }\n                    }\n                  }\n                }}\n                onCancel={() => setIsSlotModalOpen(false)}  \n                onClose={() => setIsSlotModalOpen(false)}\n                message={() => {\n                  if (\n                    serviceType &&\n                    (serviceType == \"TB Skin Test Initial Appointment\" ||\n                      serviceType == \"TB Skin Test Reading\")\n                  ) {\n                    return (\n                      <span>\n                        Vous \u00eates maintenant sur le point de r\u00e9server votre{\" \"}\n                        <span style={{ fontWeight: \"bold\" }}>rendez-vous<\/span>{\" \"}\n                        <span className=\"text-danger\">{`${formattedDate} @ ${t}`}<\/span>{\" \"}\n                        {serviceType == \"TB Skin Test Initial Appointment\" ? (\n                          \"comme heure de votre test cutan\u00e9 TB initial\"\n                        ) : (\n                          <span>\n                            <span style={{ fontWeight: \"bold\" }}>\n                              comme heure du rendez-vous de lecture TB\n                            <\/span>{\" \"}\n                            si vous ne trouvez aucune disponibilit\u00e9, veuillez\n                            cliquer{\" \"}\n                            <span style={{ fontWeight: \"bold\" }}>\"retour\"<\/span>{\" \"}\n                            au bas de la page\n                          <\/span>\n                        )}\n                      <\/span>\n                    );\n                  } else {\n                    return (\n                      <span>\n                        Veuillez confirmer{\" \"}\n                        <span className=\"text-danger\">{`${formattedDate} @ ${t}`}<\/span>{\" \"}\n                        comme heure de votre rendez-vous.\n                      <\/span>\n                    );\n                  }\n                }}\n              \/>\n              <Modal\n                isOpen={isDateModalOpen}\n                onConfirm={() => {\n                  setIsDateModalOpen(false);\n                }}\n                confirmButtonLabel=\"Ok\"\n                onCancel={() => setIsSlotModalOpen(false)}\n                onClose={() => setIsSlotModalOpen(false)}\n                message={() => (\n                  <span>\n                    Vous ne pouvez prendre rendez-vous que{\" \"}\n                    {datePickerMinDate &&\n                      datePickerMinDate.diff(moment(), \"days\") + 1}{\" \"}\n                    jours \u00e0 l'avance\n                  <\/span>\n                )}\n                showCancel={false}\n              \/>\n              <Modal\n                isOpen={isRescheduleFeeModalOpen}\n                onConfirm={() => {\n                  setKey(isEditing ? \"confirm\" : \"contact\");\n                  setProgress(2);\n                  window.scroll({ top: 0, left: 0, behavior: \"instant\" });\n                }}\n                confirmButtonLabel=\"Ok\"\n                onCancel={() => setIsRescheduleFeeModalOpen(false)}\n                onClose={() => setIsRescheduleFeeModalOpen(false)}\n                message={() => (\n                  <span>\n                    Veuillez noter que des frais seront appliqu\u00e9s pour reporter\n                    ce rendez-vous conform\u00e9ment \u00e0 notre politique de\n                    r\u00e9\u00e9chelonnement.\n                  <\/span>\n                )}\n                showCancel={false}\n              \/>\n              <Modal\n                isOpen={isTbReadingSupportModalOpen}\n                onConfirm={() => {\n                  setIsTbReadingSupportModalOpen(false);\n                }}\n                confirmButtonLabel=\"Ok\"\n                onCancel={() => setIsTbReadingSupportModalOpen(false)}\n                onClose={() => setIsTbReadingSupportModalOpen(false)}\n                message={() => (\n                  <span>\n                    Pour reprogrammer votre rendez-vous initial du test cutan\u00e9 TB, veuillez appeler notre \u00e9quipe d'assistance au{\" \"}\n                    <a href=\"tel:8882248809\">(888) 224-8809<\/a> afin de coordonner votre rendez-vous initial et votre rendez-vous de lecture.\n                  <\/span>\n                )}\n                showCancel={false}\n              \/>\n            <\/Container>\n          );\n        };\n\n        const [tripErrors, setTripErrors] = useState({\n          selectedCountries: false,\n          departureDate: false,\n          returnDate: false,\n        });\n\n        const validateTripDetails = () => {\n          const selectedReason = AppointmentReasons.find(r => r.value === serviceType);\n          \n          \/\/ If requiresTripDetails is false or undefined, skip validation\n          if (!(selectedReason && selectedReason.requiresTripDetails)) {\n            return true;\n          }\n\n          \/\/ Otherwise, validate trip details\n          const errors = {\n            selectedCountries: selectedCountries.length === 0,\n            departureDate: !departureDate,\n            returnDate: !returnDate,\n          };\n\n          setTripErrors(errors);\n          return !Object.values(errors).includes(true);\n        };\n\n        const renderTripDetails = () => (\n          <div\n            className=\"text-start px-0 mx-0\"\n            style={{\n              borderTop: \"1px solid #CCCCCC\",\n              padding: \"20px 0\",\n            }}\n          >\n            <div className=\"fw-bold tip text-start\">\n              Entrez les d\u00e9tails du voyage\n            <\/div>\n            <DropDown\n              options={countryList}\n              onChange={(e) => {\n                handleSelectCountry(e.target.value);\n                setTripErrors((prevErrors) => ({ ...prevErrors, selectedCountries: false }));\n              }}\n              value={\"\"}\n              required={!selectedCountries.length}\n              labelparam=\"CountryNameFR\"\n              selectparam=\"Id\"\n              className={`my-3 ${tripErrors.selectedCountries ? \"error-border\" : \"\"}`}\n            \/>\n\n            {selectedCountries.map((id, index) => {\n              return (\n                <span\n                  className=\"chip\"\n                  style={{ border: \"1px solid #eeeeee \" }}\n                  key={index + id}\n                >\n                  {countriesById[id].CountryNameFR}\n                  <span\n                    className=\"closebtn\"\n                    onClick={() => handleSelectCountry(id)}\n                  >\n                    &times;\n                  <\/span>\n                <\/span>\n              );\n            })}\n            <Row className=\"pt-3\">\n              <DateInput\n                label=\"Date de d\u00e9part\"\n                id={\"date-leaving\"}\n                name=\"trip-departure-date\"\n                autoComplete=\"new-password\"\n                inputMode=\"numeric\"\n                onChange={(e, { value }) => {\n                  setDepartureDate(value);\n                  setTripErrors((prevErrors) => ({ ...prevErrors, departureDate: false }));\n                }}\n                required\n                min={moment().format(\"YYYY-MM-DD\")}\n                md={5}\n                value={departureDate}\n                style={tripErrors.departureDate ? { border: \"2px solid rgb(214, 0, 0)\" } : {}}\n              \/>\n              <DateInput\n                label=\"Date de retour\"\n                id={\"date-returning\"}\n                name=\"trip-return-date\"\n                autoComplete=\"new-password\"\n                inputMode=\"numeric\"\n                onChange={(e, { value }) => {\n                  setReturnDate(value);\n                  setTripErrors((prevErrors) => ({ ...prevErrors, returnDate: false }));\n                }}\n                required\n                min={departureDate || moment().format(\"YYYY-MM-DD\")}\n                md={5}\n                value={returnDate}\n                style={tripErrors.returnDate ? { border: \"2px solid rgb(214, 0, 0)\" } : {}}\n              \/>\n            <\/Row>\n          <\/div>\n        );\n\n        const renderRequestedVaccines = () => (\n          <div\n            className=\"text-start px-0 mx-0\"\n            style={{\n              borderTop: \"1px solid #CCCCCC\",\n              padding: \"20px 0\",\n            }}\n          >\n            <div className=\"fw-bold tip text-start\">Vaccins demand\u00e9s<\/div>\n            <DropDown\n              options={vaccineList}\n              onChange={(e) => handleSelectVaccine(e.target.value)}\n              value={\"\"}\n              required={!selectedVaccines.length}\n              labelparam=\"name\"\n              selectparam=\"Id\"\n              className=\"my-3\"\n            \/>\n\n            {selectedVaccines.map((id, index) => {\n              return (\n                <span\n                  className=\"chip\"\n                  style={{ border: \"1px solid #eeeeee \" }}\n                  key={index + id}\n                >\n                  {vaccinesById[id].name}\n                  <span\n                    className=\"closebtn\"\n                    onClick={() => handleSelectVaccine(id)}\n                  >\n                    &times;\n                  <\/span>\n                <\/span>\n              );\n            })}\n          <\/div>\n        );\n\n        const renderShippingInfoSection = () => (\n          <div\n            className=\"text-start px-0 mx-0\"\n            style={{\n              borderTop: \"1px solid #CCCCCC\",\n              padding: \"20px 0\",\n            }}\n          >\n            <div className=\"fw-bold tip text-start\">\n              Enter Shipping Information\n            <\/div>\n            <Checkbox\n              label=\"Same as contact information\"\n              value={sameAsContactLocation}\n              onClick={(e) => setSameAsContactLocation(e.target.checked)}\n              defaultChecked\n            \/>\n            {!sameAsContactLocation && (\n              <PlacesForm\n                onChange={setShippingLocation}\n                id=\"shipping-location\"\n              \/>\n            )}\n            <Input\n              as=\"textarea\"\n              placeholder=\"Ajouter des instructions sp\u00e9ciales\"\n              id={\"specialInstructions\"}\n              md={8}\n              style={{ minHeight: \"140px\", maxHeight: \"180px\" }}\n              onChange={(e) => onChangeValue(e, setSpecialInstructions)}\n              required={false}\n            \/>\n          <\/div>\n        );\n\n        const renderPaymentSection = () => (\n          <div\n            className=\"text-start\"\n            style={{\n              borderTop: \"1px solid #CCCCCC\",\n              borderBottom: \"1px solid #CCCCCC\",\n              maxWidth: \"460px\",\n            }}\n          >\n            <div className=\"fw-bold pb-4 tip text-start\">\n              Saisir les informations de paiement\n            <\/div>\n            <div\n              className=\"fw-bold\"\n              style={{ fontSize: \"13px\", color: \"black\" }}\n            >\n              Utiliser un code\n            <\/div>\n            <div style={{ fontSize: \"13px\", color: \"grey\" }}>\n              Les codes peuvent \u00eatre fournis par une organisation ou offrir des r\u00e9ductions pour les rendez-vous\n            <\/div>\n            <div className=\"d-flex\">\n              <div style={{ width: \"250px\" }}>\n                <Input\n                  placeholder=\"Code\"\n                  id={\"promoCode\"}\n                  onChange={(e) => {\n                    const value = e.target.value;\n                    setPromoCode(value);\n                    if (value.trim() === \"\") {\n                      setPromoCodeError(\"\");\n                    }\n                  }}\n                  md={12}\n                  required={false}\n                  value={promoCode}\n                \/>\n              <\/div>\n              <Button\n                className=\"button align-self-center mx-3\"\n                onClick={validatePromo}\n                disabled={isValidatingPromoCode}\n              >\n                {isValidatingPromoCode ? \"Checking\" : \"Appliquer\"}\n              <\/Button>\n            <\/div>\n            {promoCodeError && promoCode.trim() !== \"\" && (\n              <div className=\"text-danger mb-3\">\n                Le code n'est pas valide\n              <\/div>\n            )}\n            {promoCodeData.Id && (\n              <div className=\"text-success mb-3\">\n                Promo de $\n                {(getPromoParams().discountAmount || 0).toFixed(2) || 0}{\" \"}\n                appliqu\u00e9e\n              <\/div>\n            )}\n            {!promoCodeData.doNotRequirePrepayment && service.prePaymentPrice > 0 && (\n              <PaymentInputs\n                setPaymentData={setPaymentData}\n                shouldShowErrors={shouldShowPaymentErrors || shouldShowErrors}\n                dismissErrors={() => setShouldShowPaymentErrors(false)}\n              \/>\n            )}\n            {!promoCodeData.doNotRequirePrepayment && service.prePaymentPrice > 0 && (\n              <Checkbox\n                label=\"Conservez en toute s\u00e9curit\u00e9 votre carte dans votre dossier patient. Important \u00e0 savoir: votre carte ne sera jamais d\u00e9bit\u00e9e sans votre autorisation\"\n                defaultChecked={true}\n                id={\"saveCreditCard\"}\n                onClick={(e) => {\n                  console.log(\n                    \"updating the saveCredit card with: \",\n                    e.target.checked\n                  );\n                  setSaveCreditCard(e.target.checked);\n                }}\n              \/>\n            )}\n          <\/div>\n        );\n\n        function isBelowMinAge(birthdate, appointmentDate, minMonths) {\n          if (typeof minMonths !== 'number') return false;\n          if (!birthdate || birthdate.length !== 10) return false; \/\/ Expect full date\n          \n          const birthMoment = moment(birthdate, \"YYYY-MM-DD\", true);\n          const appointmentMoment = moment(appointmentDate, \"YYYY-MM-DD\", true);\n        \n          if (!birthMoment.isValid() || !appointmentMoment.isValid()) {\n            return false; \/\/ Can't validate if dates aren't valid\n          }\n        \n          const requiredMinimumDate = birthMoment.clone().add(minMonths, 'months');\n        \n          \/\/ Appointment must be on or after required minimum date\n          return appointmentMoment.isBefore(requiredMinimumDate);\n        }\n\n        function isAboveMaxAge(birthdate, appointmentDate, maxYears) {\n          if (typeof maxYears !== 'number') return false;\n          if (!birthdate || birthdate.length !== 10) return false; \/\/ Expect full date\n          \n          const birthMoment = moment(birthdate, \"YYYY-MM-DD\", true);\n          const appointmentMoment = moment(appointmentDate, \"YYYY-MM-DD\", true);\n        \n          if (!birthMoment.isValid() || !appointmentMoment.isValid()) {\n            return false; \/\/ Can't validate if dates aren't valid\n          }\n        \n          const requiredMaximumDate = birthMoment.clone().add(maxYears, 'years');\n        \n          \/\/ Appointment must be on or before required maximum date\n          return appointmentMoment.isAfter(requiredMaximumDate);\n        }\n\n        const isBelowSix = (birthdate, appointmentDate) => {\n          const birthDate = moment(birthdate);\n          const appointDate = moment(appointmentDate);\n          const diff = appointDate.diff(birthDate, \"months\");\n          return diff < 6;\n        };\n\n        const patientIsBelowSixMonths = (contacts) => {\n          return contacts.some((contact) => isBelowSix(contact.birthdate));\n        };\n\n        const [errorFields, setErrorFields] = useState([]);\n        const [validationErrors, setValidationErrors] = useState([]);\n        const [emptyFields, setEmptyFields] = useState([]);\n\n        const handleDateChange = (e, { value }, index) => {\n          const appointmentDate = moment(date).format('YYYY-MM-DD');\n          const minMonths = selectedReason.minimumAgeMonths || 6; \n          const maxYears = selectedReason.maximumAgeYears;\n          const belowMinAge = isBelowMinAge(value, date, minMonths);\n          const aboveMaxAge = maxYears ? isAboveMaxAge(value, date, maxYears) : false;\n          \/\/ console.log(`[handleDateChange] patient #${index} \u2014 birthdate=${value}, appointmentDate=${appointmentDate}, isBelowSix=${minMonths}, isBelowMinAge(minMonths=${selectedReason.minimumAgeMonths})=${belowMinAge}`);\n  \n          const ageLabel =\n            minMonths >= 12\n              ? `${minMonths \/ 12} ans`\n              : `${minMonths} mois`;\n          \n              const reasonObj = AppointmentReasons.find(\n            r => r.value === serviceType\n          );\n\n          let errorText = reasonObj.minimunAgeText\n            || `Veuillez noter que nous n'acceptons pas de patients de moins de ${getAgeLabel(minMonths)} au moment du rendez-vous. Veuillez selectionner une date ult\u00e9rieure ou appeller le 1-888-224-8809 pour plus d'aide.`;\n\n          if (aboveMaxAge) {\n            errorText = reasonObj.minimunAgeText\n              || `Veuillez noter que nous n'acceptons pas de patients de plus de ${maxYears} ans au moment du rendez-vous. Veuillez appeller le 1-888-224-8809 pour plus d'aide.`;\n          }\n\n          setEmptyFields((prev) => prev.filter((error) => !(error.index === index && error.field === \"birthdate\")));\n\n          setErrorFields((prevErrors) => {\n            if (value && (belowMinAge || aboveMaxAge)) {\n              if (!prevErrors.includes(index)) {\n                Swal.fire({\n                  icon: \"error\",\n                  title: \"Oops...\",\n                  text: errorText,\n                  \/\/ text: `Veuillez noter que nous n'acceptons pas de patients de moins de ${ageLabel} au moment du rendez-vous. Veuillez selectionner une date ult\u00e9rieure ou appeller le 1-888-224-8809 pour plus d'aide.`,\n                });\n                return [...prevErrors, index];\n              }\n            } else {\n              return prevErrors.filter((i) => i !== index);\n            }\n            return prevErrors;\n          });\n\n          \/\/setErrorFields(newErrorFields);\n          onChangeContact({ target: { value } }, \"birthdate\", index);\n        };\n\n        const BirthDateDropdown = ({\n          value,\n          onChange,\n          minYear = 1900,\n          maxYear = new Date().getFullYear(),\n          required = true,\n          style = {},\n          hasError, \n        }) => {\n          const [day, setDay] = useState(\"\");\n          const [month, setMonth] = useState(\"\");\n          const [year, setYear] = useState(\"\");\n          const hasUserInputRef = useRef(false); \/\/ Track if user has made any selections\n          const initializedRef = useRef(false); \/\/ Track if we've initialized from parent value\n\n          \/\/ Only sync from parent on initial mount or when value changes externally (not from user input)\n          useEffect(() => {\n            \/\/ If user has made input, completely ignore parent updates\n            if (hasUserInputRef.current) {\n              return;\n            }\n            \n            \/\/ Initialize from parent value only once or when parent value changes externally\n            if (value && value.match(\/^\\d{4}-\\d{2}-\\d{2}$\/)) {\n              const [y, m, d] = value.split(\"-\");\n              \/\/ Only update if we haven't initialized or if the value is different from current\n              const currentDate = (year && month && day) ? `${year}-${month}-${day}` : null;\n              if (!initializedRef.current || currentDate !== value) {\n                setYear(y || \"\");\n                setMonth(m || \"\");\n                setDay(d || \"\");\n                initializedRef.current = true;\n              }\n            } else if ((value === \"\" || value === null || value === undefined) && !hasUserInputRef.current) {\n              \/\/ Only clear if user hasn't made any input\n              if (!year && !month && !day) {\n                setYear(\"\");\n                setMonth(\"\");\n                setDay(\"\");\n              }\n            }\n          }, [value]); \/\/ Only depend on value, not on year\/month\/day\n\n          const generateDays = () => {\n            if (!year || !month) {\n              return Array.from({ length: 31 }, (_, i) =>\n                (i + 1).toString().padStart(2, \"0\")\n              );\n            }\n            const nbDays = new Date(year, month, 0).getDate();\n            return Array.from({ length: nbDays }, (_, i) =>\n              (i + 1).toString().padStart(2, \"0\")\n            );\n          };\n\n          const days = generateDays();\n          const months = Array.from({ length: 12 }, (_, i) =>\n            (i + 1).toString().padStart(2, \"0\")\n          );\n          const years = Array.from({ length: maxYear - minYear + 1 }, (_, i) =>\n            (maxYear - i).toString()\n          );\n\n          const handleChange = (newYear, newMonth, newDay) => {\n            \/\/ Only call onChange with complete date when all three parts are present\n            \/\/ This allows users to select fields in any order without losing their selections\n            if (newYear && newMonth && newDay) {\n              const updatedValue = `${newYear}-${newMonth}-${newDay}`;\n              \/\/ Defer onChange call to ensure local state is set first\n              \/\/ This prevents the parent update from triggering a re-render that clears local state\n              setTimeout(() => {\n                onChange(updatedValue);\n              }, 0);\n            } else {\n              \/\/ Don't call onChange for incomplete dates - keep local state only\n              \/\/ This prevents clearing the value when user is still selecting\n              \/\/ The local state (year, month, day) will persist until all are selected\n            }\n          };\n\n          \/\/ Only show error styling when hasError is true (after validation has run)\n          \/\/ Don't show error on initial load - let HTML5 required validation handle that\n          const errorStyle = hasError ? { border: \"2px solid rgb(214, 0, 0)\" } : {};\n\n          return (\n            <div\n              className={`date-input-container ${hasError ? 'error-border' : ''}`}\n              style={{\n                display: \"flex\",\n                alignItems: \"center\",\n                gap: \"2px\",\n                margin: \"1rem 0\"\n              }}\n            >\n              <div className=\"birthdate-col-dd\" style={{flex: \"0 0 40%\"}}>\n                <Form.Select\n                  value={year}\n                  onChange={(e) => {\n                    hasUserInputRef.current = true; \/\/ Mark that user has made input - this prevents parent from clearing our state\n                    const newValue = e.target.value;\n                    setYear(newValue);\n                    handleChange(newValue, month, day);\n                  }}\n                  required={required}\n                  style={{\n                    height: \"47px\",\n                    borderRadius: \"2px\",\n                    backgroundColor: \"#FAFAFA\",\n                    width: \"100%\",\n                    ...errorStyle\n                  }}\n                >\n                  <option value=\"\">AAAA<\/option>\n                  {years.map((y) => (\n                    <option key={y} value={y}>\n                      {y}\n                    <\/option>\n                  ))}\n                <\/Form.Select>\n              <\/div>\n              <div className=\"birthdate-col\" style={{flex: \"0 0 35%\" }}>\n                <Form.Select\n                  value={month}\n                  onChange={(e) => {\n                    hasUserInputRef.current = true; \/\/ Mark that user has made input - this prevents parent from clearing our state\n                    const newValue = e.target.value;\n                    setMonth(newValue);\n                    handleChange(year, newValue, day);\n                  }}\n                  required={required}\n                  style={{\n                    height: \"47px\",\n                    borderRadius: \"2px\",\n                    backgroundColor: \"#FAFAFA\",\n                    width: \"100%\",\n                    ...errorStyle\n                  }}\n                >\n                  <option value=\"\">MM<\/option>\n                  {months.map((m) => (\n                    <option key={m} value={m}>\n                      {m}\n                    <\/option>\n                  ))}\n                <\/Form.Select>\n              <\/div>\n              <div className=\"birthdate-col\" style={{flex: \"0 0 35%\" }}>\n                <Form.Select\n                  value={day}\n                  onChange={(e) => {\n                    hasUserInputRef.current = true; \/\/ Mark that user has made input - this prevents parent from clearing our state\n                    const newValue = e.target.value;\n                    setDay(newValue);\n                    handleChange(year, month, newValue);\n                  }}\n                  required={required}\n                  style={{\n                    height: \"47px\",\n                    borderRadius: \"2px\",\n                    backgroundColor: \"#FAFAFA\",\n                    width: \"100%\",\n                    ...errorStyle\n                  }}\n                >\n                  <option value=\"\">JJ<\/option>\n                  {days.map((d) => (\n                    <option key={d} value={d}>\n                      {d}\n                    <\/option>\n                  ))}\n                <\/Form.Select>\n              <\/div>\n            <\/div>\n          );\n        };\n\n        const validateForm = () => {\n          let errors = [];\n          let empty = [];\n\n          contacts.forEach((patient, index) => {\n            if (!patient.firstName) empty.push({ field: \"firstName\", index });\n            if (!patient.lastName) empty.push({ field: \"lastName\", index });\n            if (!patient.phone) empty.push({ field: \"phone\", index });\n            if (!patient.email) {\n              empty.push({ field: \"email\", index });\n            } else if (!isValidEmail(patient.email)) {\n              \/\/ Email is present but invalid format\n              const emailInput = document.getElementById(`email${index}`);\n              if (emailInput) {\n                emailInput.setCustomValidity(\"Veuillez entrer un courriel valide\");\n                emailInput.reportValidity();\n              }\n              empty.push({ field: \"email\", index });\n            }\n            \/\/ Check if birthdate is missing or incomplete (must be YYYY-MM-DD format)\n            if (!patient.birthdate || !patient.birthdate.match(\/^\\d{4}-\\d{2}-\\d{2}$\/)) {\n              empty.push({ field: \"birthdate\", index });\n            }\n            \/\/ if (patient.birthdate && isBelowSix(patient.birthdate, date)) errors.push(index);\n            if (patient.birthdate) {\n              const minMonths = selectedReason.minimumAgeMonths || 6;\n              if (isBelowMinAge(patient.birthdate, date, minMonths)) {\n                errors.push(index);\n              }\n              if (selectedReason.maximumAgeYears && isAboveMaxAge(patient.birthdate, date, selectedReason.maximumAgeYears)) {\n                errors.push(index);\n              }\n            }\n          });\n\n          \/\/ Set validation errors and empty fields FIRST so they're highlighted even if we return early\n          setValidationErrors(errors);\n          setEmptyFields(empty);\n\n          \/\/ Validation des vaccins (apr\u00e8s avoir d\u00e9fini les champs vides pour qu'ils soient mis en \u00e9vidence)\n          if (shouldShowVaccineSection && selectedVaccines.length === 0) {\n            Swal.fire({\n              icon: \"error\",\n              title: \"Veuillez s\u00e9lectionner au moins un vaccin.\",\n            });\n            return false;\n          }\n\n          \/\/ Validation des termes et conditions\n          const termsCheckbox = document.querySelector('input[name=\"terms\"]');\n          if (!termsCheckbox || !termsCheckbox.checked) {\n            Swal.fire({\n              icon: \"error\",\n              title: \"Termes et conditions requis\",\n              text: \"Veuillez accepter les termes et conditions pour continuer.\",\n            });\n            return false;\n          }\n\n          if (errors.length > 0 || empty.length > 0) {\n            Swal.fire({\n              icon: \"warning\",\n              title: \"Erreur de validation\",\n              text: \"Veuillez remplir tous les champs obligatoires et corriger les erreurs avant de soumettre.\",\n            });\n            return false;\n          }\n          return true;\n        };\n\n        const [shouldShowErrors, setShouldShowErrors] = useState(false);\n\n        const handleSubmit = (e) => {\n          e.preventDefault();\n\n          \/\/ Prevent multiple submissions - use ref for synchronous check\n          if (isSubmittingRef.current || isSubmitting) {\n            return;\n          }\n          isSubmittingRef.current = true; \/\/ Set ref immediately (synchronous)\n          setIsSubmitting(true); \/\/ Set state for UI update\n\n          setShouldShowErrors(true);\n\n          \/\/ Validate emails before form validation\n          validateEmails();\n\n          const isFormValid = validateForm(); \n          const isTripValid = validateTripDetails(); \n\n          if (validateForm() && isFormValid && isTripValid) {\n            onSubmit(e, onBeginBook);\n          } else {\n            \/\/ Reset submitting state if validation fails\n            isSubmittingRef.current = false;\n            setIsSubmitting(false);\n          }\n        };\n\n        const isFieldEmpty = (index, field) => {\n          return emptyFields.some((error) => error.index === index && error.field === field);\n        };\n\n        const getFieldStyle = (index, field) => {\n          if (errorFields.includes(index) && field === \"birthdate\") {\n            return { border: \"2px solid rgb(214, 0, 0)\" };\n          }\n          if (isFieldEmpty(index, field)) {\n            return { border: \"2px solid rgb(214, 0, 0)\" };\n          }\n          return {};\n        };\n\n        useEffect(() => {\n          const minMonths = selectedReason.minimumAgeMonths || 6;\n          const maxYears = selectedReason.maximumAgeYears;\n          const ageLabel =\n            minMonths >= 12\n              ? `${minMonths \/ 12} ans`\n              : `${minMonths} mois`;\n          const errors = contacts\n            .map((p, i) => {\n              if (!p.birthdate) return null;\n              if (isBelowMinAge(p.birthdate, date, minMonths)) return i;\n              if (maxYears && isAboveMaxAge(p.birthdate, date, maxYears)) return i;\n              return null;\n            })\n            .filter(i => i !== null);\n          setErrorFields(errors);\n        }, [contacts, date, selectedReason.minimumAgeMonths, selectedReason.maximumAgeYears]);\n\n        const renderUsersForm = () => {\n          const minMonths = selectedReason.minimumAgeMonths || 6;\n          const ageLabel =\n            minMonths >= 12\n              ? `${minMonths \/ 12} ans`\n              : `${minMonths} mois`;\n          const isEmailError = paymentError && (paymentError.includes(\"INVALID_EMAIL_ADDRESS\") || paymentError.includes(\"invalid email\") || paymentError.includes(\"courriel invalide\"));\n          return (\n            <Form\n              onSubmit={handleSubmit}\n              className=\"pt-3\"\n              key={`form-${patientCount}-${serviceId}`}\n            >\n              {paymentError && (\n                <div className=\"alert alert-danger mb-4\" role=\"alert\">\n                  <strong>Erreur de r\u00e9servation :<\/strong> {paymentError}\n                  {isEmailError && (\n                    <div className=\"mt-2\">\n                      <small>Veuillez v\u00e9rifier toutes les adresses courriel pour les erreurs de frappe, en particulier les points cons\u00e9cutifs (..) ou les caract\u00e8res manquants.<\/small>\n                    <\/div>\n                  )}\n                <\/div>\n              )}\n              {contacts.map((patient, index) => (\n                <div className=\"pb-2\" key={`patient-${patientCount}-${index}`}>\n                  <div className=\"fw-bold pb-2 tip text-start\">\n                    Entrer les d\u00e9tails du patient {index + 1}:\n                  <\/div>\n                  <Row>\n                    <Input\n                      placeholder=\"Pr\u00e9nom\"\n                      id={\"firstName\" + index}\n                      onChange={(e) => {\n                        setEmptyFields((prev) => prev.filter((error) => !(error.index === index && error.field === \"firstName\")));\n                        onChangeContact(true, \"newsletter\", index);\n                        onChangeContact(e, \"firstName\", index);\n                      }}\n                      style={getFieldStyle(index, \"firstName\")}\n                    \/>\n                    <Input\n                      placeholder=\"Nom\"\n                      id={\"lastName\" + index}\n                      onChange={(e) => {\n                        setEmptyFields((prev) => prev.filter((error) => !(error.index === index && error.field === \"lastName\")));\n                        onChangeContact(e, \"lastName\", index)\n                      }}\n                      style={getFieldStyle(index, \"lastName\")}\n                    \/>\n                    <Input\n                      placeholder=\"Num\u00e9ro de t\u00e9l\u00e9phone\"\n                      id={\"phone-number\" + index}\n                      onChange={(e) => {\n                        setEmptyFields((prev) => prev.filter((error) => !(error.index === index && error.field === \"phone\")));\n                        onChangeContact(e, \"phone\", index)\n                      }}\n                      minLength={10}\n                      style={getFieldStyle(index, \"phone\")}\n                    \/>\n                    <Input\n                      placeholder=\"Courriel\"\n                      id={\"email\" + index}\n                      onChange={(e) => {\n                        setEmptyFields((prev) => prev.filter((error) => !(error.index === index && error.field === \"email\")));\n                        onChangeContact(e, \"email\", index)\n                      }}\n                      pattern=\"^[a-zA-Z0-9._%+\\-]+@(gmail\\.com|hotmail\\.(com|ca)|(?!gmail\\.|hotmail\\.)[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,})$\"\n                      md={12}\n                      style={getFieldStyle(index, \"email\")}\n                    \/>\n                    {\/* <Input\n                        placeholder=\"Passport\"\n                        id={\"passport\" + index}\n                        onChange={(e) => onChangeContact(e, \"passport\", index)}\n                        required={false}\n                      *\/}\n                    <div className=\"input-container\">\n                      <div className=\"input-wrapper\">\n                        <Col md={4} className=\"my-3 text-start\">\n                          <label>Date de naissance<\/label>\n                          <div style={{ position: \"relative\" }}>\n                            <BirthDateDropdown\n                              value={contacts[index].birthdate}\n                              onChange={(newDateValue) => {\n                                handleDateChange(\n                                  { target: { value: newDateValue } },\n                                  { value: newDateValue },\n                                  index\n                                );\n\n                                onChangeContact({ target: { value: newDateValue } }, \"birthdate\", index);\n                              }}\n                              hasError={\n                                errorFields.includes(index) ||\n                                isFieldEmpty(index, \"birthdate\")\n                              }\n                            \/>\n                          <\/div>\n                          {errorFields.includes(index) && (\n                            <span className=\"error-message\">\n                              La date de naissance entr\u00e9e indique que le patient est \u00e2g\u00e9 de moins de {ageLabel}.\n                            <\/span>\n                          )}\n                        <\/Col>\n                      <\/div>\n                    <\/div>\n                    <Checkbox\n                      label=\"Abonnez-vous \u00e0  notre infolettre pour recevoir des promotions, des rabais et des nouvelles de Summit Health\"\n                      defaultChecked={false}\n                      id={\"newsletter\" + index}\n                      onClick={(e) => {\n                        onChangeContact(e.target.checked, \"newsletter\", index);\n                      }}\n                    \/>\n                    {isRemoteService && !index && (\n                      <PlacesForm onChange={setUserLocation} id=\"user-location\" \/>\n                    )}\n                  <\/Row>\n                <\/div>\n              ))}\n              {selectedReason && selectedReason.requiresTripDetails && renderTripDetails()}\n              {shouldShowVaccineSection && renderRequestedVaccines()}\n              {isRemoteService && renderShippingInfoSection()}\n              {renderPaymentSection()}\n              <TermsConditions \/>\n              <div className=\"d-flex justify-content-end my-4\">\n                <Button\n                  className=\"button light mx-3\"\n                  onClick={() => {\n                    setKey(\"location\");\n                    setProgress(1);\n                  }}\n                >\n                  Retour\n                <\/Button>\n                <Button\n                  type=\"button\"\n                  className=\"button\"\n                  id=\"stripeButton\"\n                  disabled={isSubmitting}\n                  onClick={handleSubmit}\n                >\n                  Terminer la r\u00e9servation\n                <\/Button>\n              <\/div>\n              <Modal\n                isOpen={isBookingModalOpen}\n                onConfirm={onBook}\n                confirmButtonLabel={\n                  !promoCodeData.doNotRequirePrepayment\n                    ? \"Suivant\"\n                    : \"Reserve\"\n                }\n                onCancel={() => setIsBookingModalOpen(false)}\n                onClose={() => setIsBookingModalOpen(false)}\n                message={() => (\n                  <span>\n                    Vous avez des rendez-vous incomplets. Vous pouvez compl\u00e9ter\n                    ceci r\u00e9servation ou contactez notre centre d'appels pour plus\n                    d'informations..\n                  <\/span>\n                )}\n              \/>\n            <\/Form>\n          );\n        };\n\n        const renderUserTab = (count) => {\n          const t = time.split(\"_\")[0];\n          const currentService =\n            serviceType &&\n            _.find(AppointmentReasons, [\"value\", serviceType]).label;\n          const countLabel = `${contacts.length} Person${\n            contacts.length > 1 ? \"s\" : \"\"\n          }`;\n          const loc = _.find(locations, [\"id\", selectedLocationId]);\n          const discountAmount = getPromoParams().discountAmount;\n          let servicePrepaymentPrice = discountAmount\n            ? Number(service.prePaymentPrice - discountAmount).toFixed(2)\n            : service.prePaymentPrice;\n          if (servicePrepaymentPrice < 0) {\n            servicePrepaymentPrice = 0;\n          }\n          return (\n            <Row>\n              <Col lg={4} className={`${isSubmitting &#038;&#038; \"d-none\"} order-lg-1`}>\n                {serviceId == \"a0X7V00000JljfeUAB\" ||\n                serviceId == \"a0X7V00000JljfjUAB\" ||\n                serviceId == \"a0X7V00000JloC2UAJ\" ||\n                serviceId == \"a0X7V00000JloBxUAJ\" ||\n                serviceId == \"a0XOJ000002Ys1d2AC\" ||\n                serviceId == \"a0XOJ000002Ys6T2AS\" ? (\n                  <div className=\"d-flex flex-column gap-3\">\n                    <Card className=\"text-start px-2 px-md-5 py-3 py-md-4\">\n                      <div className=\"fw-bold tip\">\n                        <i className=\"fas fa-calendar-alt\"><\/i> Informations sur\n                        le rendez-vous\n                      <\/div>\n                      <span>{`${\n                        firstTbInitialAppointment &&\n                        moment(\n                          firstTbInitialAppointment.slot,\n                          \"hh:mm A\"\n                        ).format(\"h:mm A\")\n                      }\n                                        ${\n                                          firstTbInitialAppointment &&\n                                          moment(\n                                            firstTbInitialAppointment.date,\n                                            \"D_M_YYYY\"\n                                          ).format(\"dddd, MMM D\")\n                                        }`}<\/span>\n                      <div>\n                        {firstTbInitialAppointment &&\n                          firstTbInitialAppointment.service}\n                      <\/div>\n                      <div>{countLabel}<\/div>\n                      <div className=\"fw-bold tip mt-3\">\n                        {\" \"}\n                        <i className=\"fas fa-map-marked-alt\"><\/i> Emplacement\n                      <\/div>\n                      {isRemoteService ||\n                      isTelehealthCovidTest ||\n                      isTelehealthConsultation ? (\n                        <div>Rendez-vous virtuel<\/div>\n                      ) : (\n                        <div>\n                          {firstTbInitialAppointment &&\n                            firstTbInitialAppointment.location &&\n                            firstTbInitialAppointment.location.name}\n                          <br><\/br>\n                          {firstTbInitialAppointment &&\n                            firstTbInitialAppointment.location &&\n                            firstTbInitialAppointment.location.street}\n                          ,{\" \"}\n                          {firstTbInitialAppointment &&\n                            firstTbInitialAppointment.location &&\n                            firstTbInitialAppointment.location.city}\n                          ,{\" \"}\n                          {firstTbInitialAppointment &&\n                            firstTbInitialAppointment.location &&\n                            firstTbInitialAppointment.location.province}\n                          ,{\" \"}\n                          {firstTbInitialAppointment &&\n                            firstTbInitialAppointment.location &&\n                            firstTbInitialAppointment.location.postalCode}\n                        <\/div>\n                      )}\n                      {!promoCodeData.doNotRequirePrepayment && service.prePaymentPrice > 0 && (\n                        <div>\n                          <div className=\"fw-bold tip mt-3\">\n                            {\" \"}\n                            <i className=\"fas fa-dollar\"><\/i>Prix \u200b\u200bdes services\n                          <\/div>\n                          {firstTbInitialAppointment &&\n                            firstTbInitialAppointment.discountAmount > 0 && (\n                              <del>\n                                ${firstTbInitialAppointment.discountAmount}\n                              <\/del>\n                            )}{\" \"}\n                          <span\n                            style={{\n                              color:\n                                firstTbInitialAppointment &#038;&#038;\n                                firstTbInitialAppointment.discountAmount > 0\n                                  ? \"#1e73be\"\n                                  : \"#000\",\n                            }}\n                          >\n                            $\n                            {(firstTbInitialAppointment &&\n                              firstTbInitialAppointment.amount) ||\n                              0}\n                          <\/span>\n                        <\/div>\n                      )}\n                      {showEmail && (\n                        <div>\n                          <div className=\"fw-bold tip mt-3\">\n                            {\" \"}\n                            <i className=\"fas fa-at\"><\/i> E-mail\n                          <\/div>\n                          {contacts[0].email}\n                        <\/div>\n                      )}\n                      <div>\n                        <div className=\"fw-bold tip mt-3\">\n                          {\" \"}\n                          <i className=\"fas fa-envelope-open\"><\/i> Confirmation\n                        <\/div>\n                        Un email de confirmation sera envoy\u00e9 lorsque le\n                        rendez-vous sera pris\n                      <\/div>\n                    <\/Card>\n                    <Card className=\"text-start px-2 px-md-5 py-3 py-md-4\">\n                      <div className=\"fw-bold tip\">\n                        <i className=\"fas fa-calendar-alt\"><\/i> Informations sur\n                        le rendez-vous\n                      <\/div>\n                      <span>{`${\n                        SecondTbInitialAppointment &&\n                        moment(\n                          SecondTbInitialAppointment.slot,\n                          \"hh:mm A\"\n                        ).format(\"h:mm A\")\n                      }\n                                        ${\n                                          SecondTbInitialAppointment &&\n                                          moment(\n                                            SecondTbInitialAppointment.date,\n                                            \"D_M_YYYY\"\n                                          ).format(\"dddd, MMM D\")\n                                        }`}<\/span>\n                      <div>\n                        {SecondTbInitialAppointment &&\n                          SecondTbInitialAppointment.service}\n                      <\/div>\n                      <div>{countLabel}<\/div>\n                      <div className=\"fw-bold tip mt-3\">\n                        {\" \"}\n                        <i className=\"fas fa-map-marked-alt\"><\/i> Emplacement\n                      <\/div>\n                      {isRemoteService ||\n                      isTelehealthCovidTest ||\n                      isTelehealthConsultation ? (\n                        <div>Rendez-vous virtuel<\/div>\n                      ) : (\n                        <div>\n                          {SecondTbInitialAppointment &&\n                            SecondTbInitialAppointment.location &&\n                            SecondTbInitialAppointment.location.name}\n                          <br><\/br>\n                          {SecondTbInitialAppointment &&\n                            SecondTbInitialAppointment.location &&\n                            SecondTbInitialAppointment.location.street}\n                          ,{\" \"}\n                          {SecondTbInitialAppointment &&\n                            SecondTbInitialAppointment.location &&\n                            SecondTbInitialAppointment.location.city}\n                          ,{\" \"}\n                          {SecondTbInitialAppointment &&\n                            SecondTbInitialAppointment.location &&\n                            SecondTbInitialAppointment.location.province}\n                          ,{\" \"}\n                          {SecondTbInitialAppointment &&\n                            SecondTbInitialAppointment.location &&\n                            SecondTbInitialAppointment.location.postalCode}\n                        <\/div>\n                      )}\n                      {!promoCodeData.doNotRequirePrepayment && service.prePaymentPrice > 0 && (\n                        <div>\n                          <div className=\"fw-bold tip mt-3\">\n                            {\" \"}\n                            <i className=\"fas fa-dollar\"><\/i>Prix \u200b\u200bdes services\n                          <\/div>\n                          {SecondTbInitialAppointment &&\n                            SecondTbInitialAppointment.discountAmount > 0 && (\n                              <del>\n                                ${SecondTbInitialAppointment.discountAmount}\n                              <\/del>\n                            )}{\" \"}\n                          <span\n                            style={{\n                              color:\n                                SecondTbInitialAppointment &#038;&#038;\n                                SecondTbInitialAppointment.discountAmount > 0\n                                  ? \"#1e73be\"\n                                  : \"#000\",\n                            }}\n                          >\n                            $\n                            {(SecondTbInitialAppointment &&\n                              SecondTbInitialAppointment.amount) ||\n                              0}\n                          <\/span>\n                        <\/div>\n                      )}\n                      {showEmail && (\n                        <div>\n                          <div className=\"fw-bold tip mt-3\">\n                            {\" \"}\n                            <i className=\"fas fa-at\"><\/i> E-mail\n                          <\/div>\n                          {contacts[0].email}\n                        <\/div>\n                      )}\n                      <div>\n                        <div className=\"fw-bold tip mt-3\">\n                          {\" \"}\n                          <i className=\"fas fa-envelope-open\"><\/i> Confirmation\n                        <\/div>\n                        Un email de confirmation sera envoy\u00e9 lorsque le\n                        rendez-vous sera pris\n                      <\/div>\n                    <\/Card>\n                  <\/div>\n                ) : (\n                  <Card className=\"text-start px-2 px-md-5 py-3 py-md-4\">\n                    <div className=\"fw-bold tip\">\n                      <i className=\"fas fa-calendar-alt\"><\/i> Informations sur\n                      le rendez-vous\n                    <\/div>\n                    <span>{`${moment(t, \"hh:mm A\").format(\"h:mm A\")}\n                                        ${moment(date).format(\n                                          \"dddd, MMM D\"\n                                        )}`}<\/span>\n                    <div>{currentService}<\/div>\n                    <div>{countLabel}<\/div>\n                    <div className=\"fw-bold tip mt-3\">\n                      {\" \"}\n                      <i className=\"fas fa-map-marked-alt\"><\/i> Emplacement\n                    <\/div>\n                    {isRemoteService ||\n                    isTelehealthCovidTest ||\n                    isTelehealthConsultation ? (\n                      <div>Rendez-vous virtuel\n                        <br \/>\n                        <div>Langue: {language === \"english\" ? \"Anglais\" : \"Fran\u00e7ais\"}<\/div>\n                      <\/div>\n                    ) : (\n                      <div>\n                        {loc && loc.name}\n                        <br><\/br>\n                        {loc && loc.street}, {loc && loc.city},{\" \"}\n                        {loc && loc.province}, {loc && loc.postalCode}\n                      <\/div>\n                    )}\n                    {!promoCodeData.doNotRequirePrepayment && service.prePaymentPrice > 0 && (\n                      <div>\n                        <div className=\"fw-bold tip mt-3\">\n                          {\" \"}\n                          <i className=\"fas fa-dollar\"><\/i>Prix \u200b\u200bdes services\n                        <\/div>\n                        {discountAmount > 0 && (\n                          <del>${service.prePaymentPrice}<\/del>\n                        )}{\" \"}\n                        <span\n                          style={{\n                            color: discountAmount > 0 ? \"#1e73be\" : \"#000\",\n                          }}\n                        >\n                          ${servicePrepaymentPrice || 0}\n                        <\/span>\n                      <\/div>\n                    )}\n                    {showEmail && (\n                      <div>\n                        <div className=\"fw-bold tip mt-3\">\n                          {\" \"}\n                          <i className=\"fas fa-at\"><\/i> E-mail\n                        <\/div>\n                        {contacts[0].email}\n                      <\/div>\n                    )}\n                    <div>\n                      <div className=\"fw-bold tip mt-3\">\n                        {\" \"}\n                        <i className=\"fas fa-envelope-open\"><\/i> Confirmation\n                      <\/div>\n                      Un email de confirmation sera envoy\u00e9 lorsque le\n                      rendez-vous sera pris\n                    <\/div>\n                  <\/Card>\n                )}\n              <\/Col>\n              <Col lg={8} className={`${isSubmitting &#038;&#038; \"d-none\"}`}>\n                {renderUsersForm(Number(patientCount))}\n              <\/Col>\n              {isSubmitting && (\n                <div className=\"py-5 my-3\">\n                  <LoadingView \/>\n                <\/div>\n              )}\n              <Modal\n                isOpen={slotNotAvailableModalOpen}\n                onConfirm={() => {\n                  setKey(\"location\");\n                  setProgress(1);\n                  setRefetchSlots(!refetchSlots);\n                  window.scroll({ top: 0, left: 0, behavior: \"instant\" });\n                }}\n                confirmButtonLabel=\"Ok\"\n                onCancel={() => setSlotNotAvailableModalOpen(false)}\n                onClose={() => setSlotNotAvailableModalOpen(false)}\n                message={() => (\n                  <span>\n                    cette heure de rendez-vous est d\u00e9j\u00e0 prise, veuillez\n                    s\u00e9lectionner une autre fois\n                  <\/span>\n                )}\n                showCancel={false}\n              \/>\n            <\/Row>\n          );\n        };\n\n        const ServiceInfo = () => {\n          return (\n            <Card\n              className=\"text-start mb-3 rescheduleDiv\"\n              style={{ padding: \"50px 40px\" }}\n            >\n              <Row>\n                <Col md={9} className=\"service-info\">\n                  <div className=\"book\">Nom du service<\/div>\n                  <div className=\"rescheduleText\">{service.nameFrench}<\/div>\n                  <div className=\"book pt-3\">Description du service<\/div>\n                  <div className=\"rescheduleText\">\n                    {service.descriptionFrench}\n                  <\/div>\n                <\/Col>\n                <Col md={3} className=\"px-lg-5 px-md-3 pt-3 pt-md-0\">\n                  <div>\n                    <div className=\"book\">Prix des services<\/div>\n                    <div className=\"rescheduleText\">\n                      ${service.servicePrice || 0}\n                    <\/div>\n                  <\/div>\n                  {!promoCodeData.doNotRequirePrepayment && service.prePaymentPrice > 0 && (\n                    <div>\n                      <div className=\"book pt-3\">Prix de pr\u00e9paiement<\/div>\n                      <div className=\"rescheduleText\">\n                        ${service.prePaymentPrice || 0}\n                      <\/div>\n                    <\/div>\n                  )}\n                <\/Col>\n              <\/Row>\n            <\/Card>\n          );\n        };\n\n        const AppointmentInfo = ({ tab }) => {\n          const t = time.split(\"_\")[0];\n          const loc = _.find(locations, [\"id\", selectedLocationId]);\n          const isConfirm = tab === \"confirm\";\n          return (\n            <Card className=\"text-start px-2 pb-3 mb-3 rescheduleDiv\">\n              {isConfirm ? (\n                <div className=\"book\">\n                  Confirmez vos nouvelles informations de rendez-vous{\" \"}\n                <\/div>\n              ) : (\n                <div>\n                  <div className=\"book\">\n                    Reprogrammez votre rendez-vous existant\n                  <\/div>\n                  <div className=\"rescheduleText\">\n                    Veuillez s\u00e9lectionner un cr\u00e9neau horaire ci-dessous pour\n                    modifier l'heure ou le lieu de votre rendez-vous\n                  <\/div>\n                <\/div>\n              )}\n              <Row>\n                <Col sm={6}>\n                  <div className=\"fw-bold tip mt-3\">\n                    {\" \"}\n                    <i className=\"fas fa-calendar-alt\"><\/i> Informations sur le\n                    rendez-vous\n                  <\/div>\n                  {isConfirm ? (\n                    <span>{`${moment(t, \"hh:mm A\").format(\"h:mm A\")}\n                                  ${moment(date).format(\"dddd, MMM D\")}`}<\/span>\n                  ) : (\n                    <span>\n                      {moment(existingAppointment.startTime).format(\n                        \"h:mm A dddd, MMM D\"\n                      )}\n                    <\/span>\n                  )}\n                  <div>{existingAppointment.serviceType}<\/div>\n                <\/Col>\n                {!isRemoteService &&\n                  !isTelehealthCovidTest &&\n                  !isTelehealthConsultation && (\n                    <Col sm={6}>\n                      <div className=\"fw-bold tip mt-3\">\n                        <i className=\"fas fa-map-marked-alt\"><\/i> Location\n                      <\/div>\n                      <div>\n                        {isConfirm\n                          ? loc && loc.name\n                          : existingAppointment.locationName}\n                      <\/div>\n                    <\/Col>\n                  )}\n              <\/Row>\n            <\/Card>\n          );\n        };\n\n        const renderConfirmScheduling = () => {\n          return (\n            <Form\n              onSubmit={(e) => {\n                onSubmit(e, onReschedule);\n              }}\n              className=\"pt-3\"\n            >\n              <AppointmentInfo tab=\"confirm\" \/>\n              <div className=\"d-flex justify-content-end my-4\">\n                <Button\n                  className=\"button light mx-3\"\n                  onClick={() => {\n                    setKey(\"location\");\n                    setProgress(1);\n                  }}\n                >\n                  Retour\n                <\/Button>\n                <Button\n                  type=\"submit\"\n                  className=\"button\"\n                  disabled={isSubmitting}\n                >\n                  Confirmer\n                <\/Button>\n              <\/div>\n            <\/Form>\n          );\n        };\n\n        const renderAppointmentName = () => (\n            <div>\n              {bookResponse.Id && (\n                <ReactBootstrap.Card className=\"text-start px-2 py-3 mb-3\">\n                  <div className=\"fw-bold tip mb-3\" id=\"appointmentSuccessDiv\">\n                  {bookResponse.partialFailure\n                    ? \"Votre rendez-vous a \u00e9t\u00e9 r\u00e9serv\u00e9 - v\u00e9rification requise\"\n                    : \"Votre rendez-vous a \u00e9t\u00e9 pris avec succ\u00e8s !\"}\n                <\/div>\n                {bookResponse.partialFailure && (\n                  <div className=\"alert alert-warning my-3\" role=\"alert\">\n                    <strong>V\u00e9rification requise par la clinique:<\/strong>{\" \"}\n                    {bookResponse.partialFailure.userMessage}\n                  <\/div>\n                )}\n                {\/**<div className=\"text-sm tip text-start\">\n                      Id: {bookResponse.Id}\n                    <\/div>\n                    **\/}\n                <div className=\"text-sm tip text-start\">\n                  Num\u00e9ro de rendez-vous: {bookResponse.appointmentName}\n                <\/div>\n                <div className=\"text-sm tip text-start\">\n                  Courriel: {bookResponse.appointmentEmail}\n                <\/div>\n              <\/ReactBootstrap.Card>\n            )}\n            {params.aid && (\n              <div className=\"text-sm tip text-start mb-3\">\n                Votre r\u00e9servation a \u00e9t\u00e9 mise \u00e0 jour.\n              <\/div>\n            )}\n          <\/div>\n        );\n        const renderAppointmentCompleteCard = () => {\n          const t = time.split(\"_\")[0] || \"\";\n          const userAddress = userLocation && getFormattedAddress(userLocation);\n          const shippingAddress = sameAsContactLocation\n            ? userAddress\n            : shippingLocation && getFormattedAddress(shippingLocation);\n          const { discountAmount, totalPaidAmount } = getPromoParams();\n          let tagManagerValue =\n            totalPaidAmount || service.servicePrice || 0;\n          window.dataLayer = window.dataLayer || [];\n          window.dataLayer.push({\n            appointmentTotal: tagManagerValue,\n          });\n          console.log(\n            \"finished pushing total paid amount to the datalayer\" +\n              tagManagerValue\n          );\n          return isRemoteService && !isEditing ? (\n            <Card className=\"text-start mb-3\" style={{ padding: \"25px 35px\" }}>\n              <div\n                className=\"fs-3 book fw-bold\"\n                id=\"remoteAppointmentConfirmation\"\n              >\n                {bookResponse.partialFailure\n                  ? \"Rendez-vous r\u00e9serv\u00e9 - v\u00e9rification requise\"\n                  : \"Rendez-vous confirm\u00e9\"}\n              <\/div>\n              <div className=\"py-3\">\n                Votre kit de test virtuel a \u00e9t\u00e9 command\u00e9. Vous recevrez un\n                notification par e-mail une fois qu'il a \u00e9t\u00e9 exp\u00e9di\u00e9 \u00e0 l'adresse\n                au dessous de. Nous vous enverrons \u00e9galement un e-mail de\n                confirmation avec votre informations de rendez-vous, y compris\n                le lien de chat vid\u00e9o.\n              <\/div>\n              {bookResponse.partialFailure && (\n                <div className=\"alert alert-warning my-3\" role=\"alert\">\n                  <strong>V\u00e9rification requise par la clinique:<\/strong>{\" \"}\n                  {bookResponse.partialFailure.userMessage}\n                <\/div>\n              )}\n              <Row>\n                <Col md={3} className=\"py-3\">\n                  <div className=\"book fw-bold\">Coordonn\u00e9es<\/div>\n                  <div>\n                    {contacts[0].firstName} {contacts[0].lastName}\n                  <\/div>\n                  <div>{userAddress}<\/div>\n                  <div className=\"pt-3\">{contacts[0].email}<\/div>\n                <\/Col>\n                <Col md={3} className=\"py-3\">\n                  <div className=\"book fw-bold\">\n                    Informations sur la livraison\n                  <\/div>\n                  <div>\n                    {contacts[0].firstName} {contacts[0].lastName}\n                  <\/div>\n                  <div>{shippingAddress}<\/div>\n                  <div className=\"pt-3\">{contacts[0].email}<\/div>\n                <\/Col>\n                <Col md={3} className=\"py-3\">\n                  <div className=\"book fw-bold\">Rendez-Vous<\/div>\n                  <span>{`${moment(t, \"hh:mm A\").format(\"h:mm A\")}\n                                  ${moment(date).format(\"dddd, MMM D\")}`}<\/span>\n                  <div style={{ fontSize: \"13px\" }}>\n                    Les informations sur la visioconf\u00e9rence vous seront envoy\u00e9es\n                    par e-mail\n                  <\/div>\n                <\/Col>\n                <Col md={3} className=\"py-3\">\n                  <div className=\"book fw-bold\">Prix Service<\/div>\n                  <div>${service.servicePrice || 0}<\/div>\n                  {discountAmount > 0 && (\n                    <div className=\"pt-3\">\n                      <div className=\"book fw-bold\">montant de la remise <\/div>\n                      <div>${discountAmount}<\/div>\n                    <\/div>\n                  )}\n                  <div className=\"book fw-bold pt-3\">Prix Total<\/div>\n                  <div>${totalPaidAmount || service.servicePrice || 0}<\/div>\n                <\/Col>\n              <\/Row>\n            <\/Card>\n          ) : (\n            <div>\n              {bookResponse.Id && (\n                <Card className=\"text-start px-2 py-3 mb-3\">\n                  <div className=\"fw-bold tip mb-3\" id=\"appointmentSuccessDiv\">\n                    {bookResponse.partialFailure\n                      ? \"Votre rendez-vous a \u00e9t\u00e9 r\u00e9serv\u00e9 - v\u00e9rification requise\"\n                      : \"Votre rendez-vous a \u00e9t\u00e9 pris avec succ\u00e8s !\"}\n                  <\/div>\n                  {bookResponse.partialFailure && (\n                    <div className=\"alert alert-warning my-3\" role=\"alert\">\n                      <strong>V\u00e9rification requise par la clinique:<\/strong>{\" \"}\n                      {bookResponse.partialFailure.userMessage}\n                    <\/div>\n                  )}\n                  {\/**<div className=\"text-sm tip text-start\">\n                            Id: {bookResponse.Id}\n                          <\/div>\n                          **\/}\n                  <div className=\"text-sm tip text-start\">\n                    Numero Confirmation: {bookResponse.appointmentName}\n                  <\/div>\n                  <div className=\"text-sm tip text-start\">\n                    Courriel: {bookResponse.appointmentEmail}\n                  <\/div>\n                  <div className=\"my-2\">\n                    <Button\n                      className=\"button\"\n                      href={bookResponse.questionnaireURL}\n                      target=\"_self\"\n                    >\n                      Veuillez remplir votre questionnaire\n                    <\/Button>\n                  <\/div>\n                <\/Card>\n              )}\n              {params.aid && (\n                <div className=\"text-sm tip text-start mb-3\">\n                  Votre r\u00e9servation a \u00e9t\u00e9 mise \u00e0 jour.\n                <\/div>\n              )}\n            <\/div>\n          );\n        };\n        const showError = error || paymentError;\n\n        const renderPageHeader = () =>\n          isRemoteService && key !== \"appointment\" ? (\n            <HeaderUtil\n              title={\"Achetez Votre Test Virtuel\"}\n              renderSubtitle={renderRemoteHeaderSubtitle}\n            \/>\n          ) : (\n            <HeaderUtil\n              title={\"Prenez rendez-vous en ligne\"}\n              renderSubtitle={() => (\n                <div>\n                  <div\n                    className=\"mt-4 text-start border-1 pb-3\"\n                    style={{ borderBottom: \"1px solid #CCCCCC\" }}\n                  >\n                    {isEditing ? (\n                      <span className=\"text-sm-end tip\">\n                        Informations sur le rendez-vous:\n                      <\/span>\n                    ) : (\n                      renderDefaultHeaderSubtitle()\n                    )}\n                  <\/div>\n                <\/div>\n              )}\n            \/>\n          );\n        return (\n          <Container fluid className=\"container-sm text-center App\">\n            {isLoadingAppointment ? (\n              <LoadingView \/>\n            ) : (\n              <div>\n                {renderPageHeader()}\n                <div>\n                  {key !== \"appointment\" && !isEditing && (\n                    <StyledProgressBar\n                      labels={\n                        isRemoteService\n                          ? ONLINE_PROGRESS_LABELS\n                          : STANDARD_PROGRESS_LABELS\n                      }\n                      progress={progress}\n                    \/>\n                  )}\n                  <Tabs\n                    id=\"controlled-tab-example\"\n                    activeKey={key}\n                    onSelect={(k) => setKey(k)}\n                    className=\"mb-3 border-0\"\n                  >\n                    <Tab\n                      eventKey=\"appointment\"\n                      title=\"Appointment\"\n                      disabled\n                      tabClassName=\"d-none\"\n                    >\n                      {renderMainFormTabs()}\n                      {isLoadingLocations && <LoadingView \/>}\n                    <\/Tab>\n                    <Tab\n                      eventKey=\"location\"\n                      title=\"Location\"\n                      disabled\n                      tabClassName=\"d-none\"\n                    >\n                      {!!locations.length && renderLocationForm()}\n                    <\/Tab>\n                    {isEditing ? (\n                      <Tab\n                        eventKey={\"confirm\"}\n                        title=\"confirm\"\n                        disabled\n                        tabClassName=\"d-none\"\n                      >\n                        {renderConfirmScheduling()}\n                        {isSubmitting && <LoadingView \/>}\n                      <\/Tab>\n                    ) : (\n                      <Tab\n                        eventKey={\"contact\"}\n                        title=\"Contact\"\n                        disabled\n                        tabClassName=\"d-none\"\n                      >\n                        {renderUserTab()}\n                      <\/Tab>\n                    )}\n                    <Tab\n                      eventKey=\"complete\"\n                      title=\"Complete\"\n                      disabled\n                      tabClassName=\"d-none\"\n                    >\n                      {key === \"complete\" ? (\n                        <div>\n                          {renderAppointmentCompleteCard()}\n                          {!isEditing && (\n                            <div className=\"d-flex justify-content-end my-4\">\n                              <Button\n                                className=\"button\"\n                                onClick={() => window.location.reload()}\n                              >\n                                R\u00e9server un autre test\n                              <\/Button>\n                            <\/div>\n                          )}\n                        <\/div>\n                      ) : (\n                        <div \/>\n                      )}\n                    <\/Tab>\n                  <\/Tabs>\n                <\/div>\n              <\/div>\n            )}\n            {showError && (\n              <ErrorView\n                error={paymentError || error}\n                showErrorMessage={paymentError}\n              \/>\n            )}\n          <\/Container>\n        );\n      };\n      const App = () => <SummitHealth \/>;\n\n      ReactDOM.render(<App \/>, document.getElementById(\"book-form\"));\n    <\/script>\n  <\/body>\n<\/html>\n\n\n\n\n<script>\ndocument.addEventListener(\"DOMContentLoaded\", function () {\nconsole.log(\"page loaded, setting root var\");\n    \/\/ Access the root element\n    const root = document.documentElement;\n    \n    \/\/ Set the --base-3 variable to white (#FFFFFF)\n    root.style.setProperty(\"--base-3\", \"#FFFFFF\");\n});\n<\/script>\n","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-1654","page","type-page","status-publish"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Prendre rendez-vous - Summit Health<\/title>\n<meta name=\"description\" content=\"Prenez rendez-vous dans une clinique Summit Health au Qu\u00e9bec ou en Ontario pour des services de sant\u00e9 rapides et pratiques.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Prendre rendez-vous - Summit Health\" \/>\n<meta property=\"og:description\" content=\"Prenez rendez-vous dans une clinique Summit Health au Qu\u00e9bec ou en Ontario pour des services de sant\u00e9 rapides et pratiques.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/\" \/>\n<meta property=\"og:site_name\" content=\"Summit Health\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-27T11:25:55+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/prendre-rendez-vous\\\/\",\"url\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/prendre-rendez-vous\\\/\",\"name\":\"Prendre rendez-vous - Summit Health\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/#website\"},\"datePublished\":\"2024-08-01T15:19:18+00:00\",\"dateModified\":\"2026-06-27T11:25:55+00:00\",\"description\":\"Prenez rendez-vous dans une clinique Summit Health au Qu\u00e9bec ou en Ontario pour des services de sant\u00e9 rapides et pratiques.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/prendre-rendez-vous\\\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/summithealth.ca\\\/fr\\\/prendre-rendez-vous\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/prendre-rendez-vous\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Prendre rendez-vous\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/#website\",\"url\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/\",\"name\":\"Summit Health\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/#organization\",\"name\":\"Summit Health\",\"url\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/summithealth.ca\\\/wp-content\\\/uploads\\\/2024\\\/07\\\/logo_trimmed.png\",\"contentUrl\":\"https:\\\/\\\/summithealth.ca\\\/wp-content\\\/uploads\\\/2024\\\/07\\\/logo_trimmed.png\",\"width\":312,\"height\":312,\"caption\":\"Summit Health\"},\"image\":{\"@id\":\"https:\\\/\\\/summithealth.ca\\\/fr\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Prendre rendez-vous - Summit Health","description":"Prenez rendez-vous dans une clinique Summit Health au Qu\u00e9bec ou en Ontario pour des services de sant\u00e9 rapides et pratiques.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/","og_locale":"fr_FR","og_type":"article","og_title":"Prendre rendez-vous - Summit Health","og_description":"Prenez rendez-vous dans une clinique Summit Health au Qu\u00e9bec ou en Ontario pour des services de sant\u00e9 rapides et pratiques.","og_url":"https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/","og_site_name":"Summit Health","article_modified_time":"2026-06-27T11:25:55+00:00","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/","url":"https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/","name":"Prendre rendez-vous - Summit Health","isPartOf":{"@id":"https:\/\/summithealth.ca\/fr\/#website"},"datePublished":"2024-08-01T15:19:18+00:00","dateModified":"2026-06-27T11:25:55+00:00","description":"Prenez rendez-vous dans une clinique Summit Health au Qu\u00e9bec ou en Ontario pour des services de sant\u00e9 rapides et pratiques.","breadcrumb":{"@id":"https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/summithealth.ca\/fr\/prendre-rendez-vous\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/summithealth.ca\/fr\/"},{"@type":"ListItem","position":2,"name":"Prendre rendez-vous"}]},{"@type":"WebSite","@id":"https:\/\/summithealth.ca\/fr\/#website","url":"https:\/\/summithealth.ca\/fr\/","name":"Summit Health","description":"","publisher":{"@id":"https:\/\/summithealth.ca\/fr\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/summithealth.ca\/fr\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/summithealth.ca\/fr\/#organization","name":"Summit Health","url":"https:\/\/summithealth.ca\/fr\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/summithealth.ca\/fr\/#\/schema\/logo\/image\/","url":"https:\/\/summithealth.ca\/wp-content\/uploads\/2024\/07\/logo_trimmed.png","contentUrl":"https:\/\/summithealth.ca\/wp-content\/uploads\/2024\/07\/logo_trimmed.png","width":312,"height":312,"caption":"Summit Health"},"image":{"@id":"https:\/\/summithealth.ca\/fr\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/summithealth.ca\/fr\/wp-json\/wp\/v2\/pages\/1654","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/summithealth.ca\/fr\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/summithealth.ca\/fr\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/summithealth.ca\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/summithealth.ca\/fr\/wp-json\/wp\/v2\/comments?post=1654"}],"version-history":[{"count":9,"href":"https:\/\/summithealth.ca\/fr\/wp-json\/wp\/v2\/pages\/1654\/revisions"}],"predecessor-version":[{"id":3081,"href":"https:\/\/summithealth.ca\/fr\/wp-json\/wp\/v2\/pages\/1654\/revisions\/3081"}],"wp:attachment":[{"href":"https:\/\/summithealth.ca\/fr\/wp-json\/wp\/v2\/media?parent=1654"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}