templates/_partials/country_picker.html.twig line 1

Open in your IDE?
  1. {# =======================================================
  2.    JULICO Country Picker — Required modal + reusable opener
  3.    ✅ Place at: templates/_partials/country_picker.html.twig
  4.    Included via {% include '_partials/country_picker.html.twig' %}
  5.    in any page that should enforce country selection.
  6.    Required template context (passed by controller):
  7.    - needsCountryPicker (bool) — auto-shows modal when true
  8.    - allCountries (array of Country entities)
  9.    - selectedCountryId (int|null)
  10.    Exposes globally: window.openCountryModal()
  11.    ======================================================= #}
  12. <style>
  13. .cm-overlay {
  14.   position: fixed; inset: 0; background: rgba(15, 23, 42, 0.7);
  15.   display: none; align-items: center; justify-content: center;
  16.   z-index: 10000; padding: 20px; backdrop-filter: blur(4px);
  17. }
  18. .cm-overlay.show { display: flex; }
  19. .cm-box {
  20.   background: var(--white, #fff); border-radius: var(--rlg, 16px);
  21.   padding: 32px 30px; max-width: 480px; width: 100%;
  22.   box-shadow: 0 25px 60px rgba(0,0,0,0.35); text-align: center;
  23.   font-family: var(--font, 'Plus Jakarta Sans', sans-serif);
  24. }
  25. .cm-icon { font-size: 48px; margin-bottom: 8px; }
  26. .cm-box h2 {
  27.   font-size: 22px; font-weight: 800;
  28.   color: var(--gray-900, #0f172a);
  29.   margin: 0 0 8px; letter-spacing: -0.02em;
  30. }
  31. .cm-box p {
  32.   font-size: 14px; color: var(--gray-500, #64748b);
  33.   margin: 0 0 22px; line-height: 1.5;
  34. }
  35. .cm-label {
  36.   display: block; text-align: left; font-size: 11px; font-weight: 800;
  37.   color: var(--gray-600, #475569); margin-bottom: 6px;
  38.   text-transform: uppercase; letter-spacing: 0.07em;
  39. }
  40. .cm-select {
  41.   width: 100%; height: 50px;
  42.   border: 2px solid var(--gray-200, #e2e8f0);
  43.   border-radius: var(--rsm, 10px); padding: 0 14px;
  44.   font-family: inherit; font-size: 15px; font-weight: 600;
  45.   background: var(--gray-50, #f8fafc); color: var(--gray-800, #1e293b);
  46.   outline: none; margin-bottom: 18px; cursor: pointer;
  47. }
  48. .cm-select:focus {
  49.   border-color: var(--brand, #00a7b5);
  50.   background: var(--white, #fff);
  51. }
  52. .cm-btn {
  53.   width: 100%; height: 50px;
  54.   background: var(--brand, #00a7b5); color: #fff;
  55.   border: none; border-radius: var(--rsm, 10px);
  56.   font-family: inherit; font-size: 15px; font-weight: 800;
  57.   cursor: pointer; transition: background .2s;
  58. }
  59. .cm-btn:hover:not(:disabled) {
  60.   background: var(--brand-dark, #008c98);
  61. }
  62. .cm-btn:disabled {
  63.   background: var(--gray-300, #cbd5e1); cursor: not-allowed;
  64. }
  65. .cm-note {
  66.   font-size: 11px; color: var(--gray-400, #94a3b8);
  67.   margin-top: 12px;
  68. }
  69. /* Reusable pill class (in case any template wants a standalone pill — the
  70.    homepage uses the existing .location-btn already, so this is optional) */
  71. .country-pill {
  72.   display: inline-flex; align-items: center; gap: 6px;
  73.   padding: 8px 14px;
  74.   border: 1.5px solid var(--gray-200, #e2e8f0);
  75.   border-radius: var(--rsm, 10px); background: var(--white, #fff);
  76.   font-size: 13px; font-weight: 700; color: var(--gray-700, #334155);
  77.   text-decoration: none; cursor: pointer; transition: all .2s;
  78.   min-height: 40px; font-family: inherit;
  79. }
  80. .country-pill:hover {
  81.   border-color: var(--brand, #00a7b5); color: var(--brand, #00a7b5);
  82.   background: var(--brand-light, #e6f7f8);
  83. }
  84. .country-pill-flag { font-size: 16px; }
  85. </style>
  86. <div id="country-modal-overlay" class="cm-overlay {% if needsCountryPicker is defined and needsCountryPicker %}show{% endif %}">
  87.   <div class="cm-box">
  88.     <div class="cm-icon">📍</div>
  89.     <h2>Welcome to Julico</h2>
  90.     <p>Choose your country to see products available in your area.</p>
  91.     <label class="cm-label">🌍 Your country</label>
  92.     <select id="country-modal-select" class="cm-select">
  93.       <option value="">Select a country…</option>
  94.       {% if allCountries is defined and allCountries is iterable %}
  95.         {% for c in allCountries %}
  96.           <option value="{{ c.id }}"
  97.                   {% if selectedCountryId is defined and selectedCountryId == c.id %}selected{% endif %}>
  98.             {{ c.name }}{% if c.isoCode %} ({{ c.isoCode }}){% endif %}
  99.           </option>
  100.         {% endfor %}
  101.       {% endif %}
  102.     </select>
  103.     <button type="button" id="country-modal-continue" class="cm-btn" disabled>
  104.       Continue →
  105.     </button>
  106.     <div class="cm-note">You can change this anytime from the location pill.</div>
  107.   </div>
  108. </div>
  109. <script>
  110. (function() {
  111.   var overlay = document.getElementById('country-modal-overlay');
  112.   var select  = document.getElementById('country-modal-select');
  113.   var btn     = document.getElementById('country-modal-continue');
  114.   if (!overlay || !select || !btn) return;
  115.   // Enable Continue when a country is picked
  116.   select.addEventListener('change', function() {
  117.     btn.disabled = !this.value;
  118.   });
  119.   if (select.value) btn.disabled = false;
  120.   btn.addEventListener('click', function() {
  121.     if (!select.value) return;
  122.     btn.disabled = true;
  123.     var orig = btn.textContent;
  124.     btn.textContent = '⏳ Saving…';
  125.     var fd = new FormData();
  126.     fd.append('country_id', select.value);
  127.     fetch('/api/geo/set-country', {
  128.       method: 'POST',
  129.       body: fd,
  130.       headers: { 'X-Requested-With': 'XMLHttpRequest' },
  131.       credentials: 'same-origin'
  132.     })
  133.       .then(function(r) { return r.json(); })
  134.       .then(function(d) {
  135.         if (d.ok) {
  136.           window.location.reload();
  137.         } else {
  138.           alert('Failed: ' + (d.error || 'unknown error'));
  139.           btn.disabled = false;
  140.           btn.textContent = orig;
  141.         }
  142.       })
  143.       .catch(function() {
  144.         alert('Network error — please try again.');
  145.         btn.disabled = false;
  146.         btn.textContent = orig;
  147.       });
  148.   });
  149.   // Expose globally so the location pill (or any other element) can reopen the modal
  150.   window.openCountryModal = function() {
  151.     overlay.classList.add('show');
  152.   };
  153. })();
  154. </script>