var/cache/dev/twig/30/30deb9757f5704ec3ef8107fc211f896.php line 49

Open in your IDE?
  1. <?php
  2. use Twig\Environment;
  3. use Twig\Error\LoaderError;
  4. use Twig\Error\RuntimeError;
  5. use Twig\Extension\CoreExtension;
  6. use Twig\Extension\SandboxExtension;
  7. use Twig\Markup;
  8. use Twig\Sandbox\SecurityError;
  9. use Twig\Sandbox\SecurityNotAllowedTagError;
  10. use Twig\Sandbox\SecurityNotAllowedFilterError;
  11. use Twig\Sandbox\SecurityNotAllowedFunctionError;
  12. use Twig\Sandbox\SecurityNotAllowedTestError;
  13. use Twig\Source;
  14. use Twig\Template;
  15. use Twig\TemplateWrapper;
  16. /* application/whileresume/application/jobs/dashboard.html.twig */
  17. class __TwigTemplate_e306429a77277e7ea85b497d31a6c7ce extends Template
  18. {
  19. private Source $source;
  20. /**
  21. * @var array<string, Template>
  22. */
  23. private array $macros = [];
  24. public function __construct(Environment $env)
  25. {
  26. parent::__construct($env);
  27. $this->source = $this->getSourceContext();
  28. $this->blocks = [
  29. 'title' => [$this, 'block_title'],
  30. 'description' => [$this, 'block_description'],
  31. 'robots' => [$this, 'block_robots'],
  32. 'css' => [$this, 'block_css'],
  33. 'body' => [$this, 'block_body'],
  34. 'footerjs' => [$this, 'block_footerjs'],
  35. ];
  36. }
  37. protected function doGetParent(array $context): bool|string|Template|TemplateWrapper
  38. {
  39. // line 1
  40. return "application/whileresume/application/jobs/layout-social.html.twig";
  41. }
  42. protected function doDisplay(array $context, array $blocks = []): iterable
  43. {
  44. $macros = $this->macros;
  45. $__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  46. $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template", "application/whileresume/application/jobs/dashboard.html.twig"));
  47. $__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  48. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template", "application/whileresume/application/jobs/dashboard.html.twig"));
  49. // line 5
  50. $macros["promo"] = $this->macros["promo"] = $this->load("application/whileresume/_macros/promo_card.html.twig", 5)->unwrap();
  51. // line 1
  52. $this->parent = $this->load("application/whileresume/application/jobs/layout-social.html.twig", 1);
  53. yield from $this->parent->unwrap()->yield($context, array_merge($this->blocks, $blocks));
  54. $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  55. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  56. }
  57. // line 7
  58. /**
  59. * @return iterable<null|scalar|\Stringable>
  60. */
  61. public function block_title(array $context, array $blocks = []): iterable
  62. {
  63. $macros = $this->macros;
  64. $__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  65. $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "title"));
  66. $__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  67. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "title"));
  68. if ((((array_key_exists("currentSubKeyword", $context) && !(null === (isset($context["currentSubKeyword"]) || array_key_exists("currentSubKeyword", $context) ? $context["currentSubKeyword"] : (function () { throw new RuntimeError('Variable "currentSubKeyword" does not exist.', 7, $this->source); })()))) && array_key_exists("currentCityFilter", $context)) && !(null === (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 7, $this->source); })())))) {
  69. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["currentSubKeyword"]) || array_key_exists("currentSubKeyword", $context) ? $context["currentSubKeyword"] : (function () { throw new RuntimeError('Variable "currentSubKeyword" does not exist.', 7, $this->source); })()), "html", null, true);
  70. yield " ";
  71. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 7, $this->source); })()), "label", [], "any", false, false, false, 7), "html", null, true);
  72. } elseif ((array_key_exists("currentFilter", $context) && !(null === (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 7, $this->source); })())))) {
  73. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(((CoreExtension::getAttribute($this->env, $this->source, ($context["currentFilter"] ?? null), "shortTitle", [], "any", true, true, false, 7)) ? (Twig\Extension\CoreExtension::default(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 7, $this->source); })()), "shortTitle", [], "any", false, false, false, 7), CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 7, $this->source); })()), "label", [], "any", false, false, false, 7))) : (CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 7, $this->source); })()), "label", [], "any", false, false, false, 7))), "html", null, true);
  74. yield " — WhileResume";
  75. } else {
  76. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.title", [], "whr-public"), "html", null, true);
  77. }
  78. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  79. $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  80. yield from [];
  81. }
  82. // line 8
  83. /**
  84. * @return iterable<null|scalar|\Stringable>
  85. */
  86. public function block_description(array $context, array $blocks = []): iterable
  87. {
  88. $macros = $this->macros;
  89. $__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  90. $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "description"));
  91. $__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  92. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "description"));
  93. if ((((array_key_exists("currentSubKeyword", $context) && !(null === (isset($context["currentSubKeyword"]) || array_key_exists("currentSubKeyword", $context) ? $context["currentSubKeyword"] : (function () { throw new RuntimeError('Variable "currentSubKeyword" does not exist.', 8, $this->source); })()))) && array_key_exists("currentCityFilter", $context)) && !(null === (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 8, $this->source); })())))) {
  94. yield "Découvrez les offres \"";
  95. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["currentSubKeyword"]) || array_key_exists("currentSubKeyword", $context) ? $context["currentSubKeyword"] : (function () { throw new RuntimeError('Variable "currentSubKeyword" does not exist.', 8, $this->source); })()), "html", null, true);
  96. yield "\" à ";
  97. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 8, $this->source); })()), "label", [], "any", false, false, false, 8), "html", null, true);
  98. yield ".";
  99. } elseif (((array_key_exists("currentFilter", $context) && !(null === (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 8, $this->source); })()))) && !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 8, $this->source); })()), "shortDescription", [], "any", false, false, false, 8)))) {
  100. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 8, $this->source); })()), "shortDescription", [], "any", false, false, false, 8), "html", null, true);
  101. } else {
  102. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.description", [], "whr-public"), "html", null, true);
  103. }
  104. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  105. $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  106. yield from [];
  107. }
  108. // line 9
  109. /**
  110. * @return iterable<null|scalar|\Stringable>
  111. */
  112. public function block_robots(array $context, array $blocks = []): iterable
  113. {
  114. $macros = $this->macros;
  115. $__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  116. $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "robots"));
  117. $__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  118. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "robots"));
  119. yield "index,follow";
  120. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  121. $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  122. yield from [];
  123. }
  124. // line 11
  125. /**
  126. * @return iterable<null|scalar|\Stringable>
  127. */
  128. public function block_css(array $context, array $blocks = []): iterable
  129. {
  130. $macros = $this->macros;
  131. $__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  132. $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "css"));
  133. $__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  134. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "css"));
  135. // line 12
  136. yield " <style>
  137. /* ─── Page dashboard /jobs ─── */
  138. .jobs-dash{max-width:880px;margin:0 auto}
  139. .jobs-dash-header{margin-bottom:18px}
  140. .jobs-dash-title{font-size:24px;font-weight:800;color:#1E1B2E;line-height:1.2;letter-spacing:-0.02em;margin:0 0 6px}
  141. @media(min-width:768px){.jobs-dash-title{font-size:28px}}
  142. .jobs-dash-subtitle{font-size:14px;color:#6B7280;margin:0 0 18px}
  143. /* ─── Bandeau contexte ville (badge + bouton ALL) ─── */
  144. .city-context-bar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:0 0 16px;padding:10px 14px;background:#F5F3FF;border:1px solid rgba(108,58,237,.15);border-radius:12px}
  145. .city-context-label{font-size:11px;font-weight:600;color:#6B7280;text-transform:uppercase;letter-spacing:.06em}
  146. .city-context-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:100px;background:#fff;color:var(--theme-color,#6C3AED);font-size:13px;font-weight:600;border:1px solid rgba(108,58,237,.2)}
  147. .city-context-badge-icon{font-size:14px;line-height:1}
  148. .city-context-clear-btn{margin-left:auto;display:inline-flex;align-items:center;gap:4px;padding:5px 12px;border-radius:100px;background:transparent;color:var(--theme-color,#6C3AED);font-size:12px;font-weight:600;border:1px dashed rgba(108,58,237,.4);cursor:pointer;text-decoration:none;font-family:inherit;transition:background .15s,border-style .15s,border-color .15s}
  149. .city-context-clear-btn:hover{background:#fff;border-style:solid;border-color:var(--theme-color,#6C3AED);color:var(--theme-color,#6C3AED)}
  150. .city-context-clear-btn svg{width:11px;height:11px}
  151. /* Widget recherche */
  152. .wr-search-widget{background:#fff;border-radius:14px;box-shadow:0 0 20px 0 rgba(0,0,0,0.05);padding:10px;margin-bottom:14px;position:relative;z-index:50}
  153. .wr-search-input-wrap{display:flex;align-items:center;gap:10px;padding:8px 14px;border-radius:10px;background:#F9FAFB;border:1px solid #E5E7EB;transition:border-color .2s,background .2s}
  154. .wr-search-input-wrap:focus-within{border-color:rgba(108,58,237,.3);background:#fff}
  155. .wr-search-icon{color:#9CA3AF;display:flex;flex-shrink:0}
  156. .wr-search-input{flex:1;border:none;outline:none;background:transparent;font-size:14px;color:#1E1B2E;padding:4px 0;font-family:inherit;min-width:0}
  157. .wr-search-input::placeholder{color:#9CA3AF}
  158. .wr-search-clear{background:none;border:none;color:#9CA3AF;cursor:pointer;padding:4px;display:none;font-family:inherit}
  159. .wr-search-clear.visible{display:flex;align-items:center}
  160. .wr-search-clear:hover{color:#6B7280}
  161. .wr-search-clear svg{width:14px;height:14px}
  162. .wr-search-kbd-hint{display:none;align-items:center;padding:3px 8px;background:#fff;border:1px solid #E5E7EB;border-radius:6px;font-size:10.5px;font-weight:600;color:#6B7280;font-family:inherit;letter-spacing:.02em;white-space:nowrap;flex-shrink:0}
  163. .wr-search-input-wrap:focus-within .wr-search-kbd-hint{display:inline-flex}
  164. /* Chips keywords */
  165. .search-keywords{display:flex;flex-wrap:wrap;gap:6px;margin:0 4px 18px}
  166. .search-keyword-chip{display:inline-flex;align-items:center;gap:5px;padding:6px 12px;border-radius:100px;background:#F5F3FF;color:var(--theme-color,#6C3AED);font-size:12.5px;font-weight:500;cursor:pointer;border:1px solid transparent;transition:background .15s,border-color .15s,transform .15s;font-family:inherit}
  167. .search-keyword-chip:hover{background:#EDE9FE;border-color:rgba(108,58,237,.2);transform:translateY(-1px);color:var(--theme-color,#6C3AED)}
  168. .search-keyword-chip.active{background:var(--theme-color,#6C3AED);color:#fff;border-color:var(--theme-color,#6C3AED)}
  169. .search-keyword-chip-icon{font-size:13px;line-height:1}
  170. .search-keywords-label{font-size:11px;font-weight:600;color:#9CA3AF;text-transform:uppercase;letter-spacing:.06em;margin-right:4px;align-self:center}
  171. /* ─── Sections de filtres SEO (chips → liens directs vers /jobs/{slug}) ─── */
  172. .filter-section{margin:0 0 14px}
  173. .filter-section-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;padding:0 4px;flex-wrap:wrap}
  174. @media(max-width:540px){
  175. .dash-filter-search{max-width:100%;width:100%}
  176. }
  177. .filter-section-title{display:flex;align-items:center;gap:7px;font-size:11.5px;font-weight:700;color:#6B7280;text-transform:uppercase;letter-spacing:.06em;margin:0}
  178. .filter-section-title-icon{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:6px;background:#F5F3FF;color:var(--theme-color,#6C3AED)}
  179. .filter-section-title-icon svg{width:11px;height:11px}
  180. .filter-section-toggle{background:none;border:none;font-size:11px;color:var(--theme-color,#6C3AED);font-weight:600;cursor:pointer;font-family:inherit;padding:4px 8px;border-radius:6px;transition:background .15s}
  181. .filter-section-toggle:hover{background:#F5F3FF}
  182. .filter-chips{display:flex;flex-wrap:wrap;gap:6px;padding:0 4px}
  183. .filter-chip{display:inline-flex;align-items:center;gap:5px;padding:7px 13px;border-radius:100px;background:#fff;color:#374151;font-size:12.5px;font-weight:500;cursor:pointer;border:1px solid #E5E7EB;text-decoration:none;transition:background .15s,border-color .15s,transform .15s,color .15s;font-family:inherit;white-space:nowrap}
  184. .filter-chip:hover{background:#F5F3FF;border-color:rgba(108,58,237,.3);color:var(--theme-color,#6C3AED);transform:translateY(-1px)}
  185. .filter-chip.active{background:var(--theme-color,#6C3AED);border-color:var(--theme-color,#6C3AED);color:#fff}
  186. .filter-chip.active:hover{background:#5B21B6;color:#fff}
  187. .filter-chip-icon{font-size:14px;line-height:1}
  188. .filter-chip-arrow{display:inline-flex;color:currentColor;opacity:.5;margin-left:2px}
  189. .filter-chip-arrow svg{width:10px;height:10px}
  190. /* Mode \"compact\" : sections initialement collapsées si > 6 items */
  191. .filter-chips.collapsed{max-height:80px;overflow:hidden;position:relative}
  192. .filter-chips.collapsed::after{content:\"\";position:absolute;left:0;right:0;bottom:0;height:30px;background:linear-gradient(to bottom,transparent,#FAFAFA);pointer-events:none}
  193. /* ─── Bouton \"Voir plus\" sous les chips villes/métiers ─── */
  194. .dash-filter-more-wrap{display:flex;justify-content:center;margin-top:10px;padding:0 4px}
  195. .dash-filter-more-btn{display:inline-flex;align-items:center;gap:6px;padding:7px 16px;background:transparent;color:var(--theme-color,#6C3AED);border:1px dashed rgba(108,58,237,.35);border-radius:100px;font-size:12px;font-weight:600;font-family:inherit;cursor:pointer;transition:background .15s,border-style .15s,border-color .15s}
  196. .dash-filter-more-btn:hover{background:#F5F3FF;border-style:solid;border-color:var(--theme-color,#6C3AED)}
  197. .dash-filter-more-icon{display:inline-flex;align-items:center;color:currentColor;transition:transform .2s}
  198. .dash-filter-more-icon svg{width:12px;height:12px}
  199. .dash-filter-more-btn.is-expanded .dash-filter-more-icon{transform:rotate(180deg)}
  200. /* ─── Input de recherche dans le header (toujours visible) ─── */
  201. .dash-filter-search{display:block;position:relative;flex:1;max-width:240px}
  202. @keyframes dashFilterFadeIn{from{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:translateY(0)}}
  203. .dash-filter-search-input{width:100%;padding:5px 10px 5px 28px;border:1px solid #E5E7EB;border-radius:100px;font-size:12px;color:#1E1B2E;background:#FAFAFA;font-family:inherit;outline:none;box-sizing:border-box;transition:border-color .15s,background .15s}
  204. .dash-filter-search-input:focus{border-color:rgba(108,58,237,.4);background:#fff}
  205. .dash-filter-search-input::placeholder{color:#9CA3AF}
  206. .dash-filter-search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:#9CA3AF;pointer-events:none;display:flex}
  207. .dash-filter-search-icon svg{width:11px;height:11px}
  208. /* État \"aucun résultat\" sous le filtre live */
  209. .dash-filter-empty{display:none;padding:14px 12px;font-size:12.5px;color:#9CA3AF;font-style:italic;text-align:center;width:100%}
  210. .filter-chips.is-no-results .dash-filter-empty{display:block}
  211. /* Bandeau résultats */
  212. .jobs-results-info{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 4px 14px;font-size:12px;color:#6B7280}
  213. .jobs-results-info strong{color:#1E1B2E;font-weight:700}
  214. /* Liste cards (style \"similar-card\" comme dans show.html.twig) */
  215. .jobs-list{display:flex;flex-direction:column;gap:10px}
  216. .jobs-card{background:#fff;border-radius:14px;padding:14px;box-shadow:0 0 16px 0 rgba(0,0,0,0.04);display:flex;align-items:center;gap:14px;text-decoration:none;color:inherit;transition:transform .15s,box-shadow .2s}
  217. .jobs-card:hover{transform:translateY(-1px);box-shadow:0 4px 20px rgba(108,58,237,.1);color:inherit}
  218. .jobs-card-logo{width:50px;height:50px;border-radius:12px;background:linear-gradient(135deg,#EDE9FE,#DDD6FE);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:20px;color:var(--theme-color,#6C3AED);flex-shrink:0;overflow:hidden}
  219. .jobs-card-logo img{width:100%;height:100%;object-fit:cover;border-radius:8px}
  220. .jobs-card-logo svg{width:24px;height:24px;opacity:.85}
  221. .jobs-card-info{flex:1;min-width:0}
  222. .jobs-card-title{font-size:14px;font-weight:700;color:#1E1B2E;line-height:1.3;margin:0 0 4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  223. .jobs-card-meta{font-size:12px;color:#6B7280;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  224. .jobs-card-salary{font-size:11px;font-weight:600;color:var(--theme-color,#6C3AED);background:#F5F3FF;padding:3px 9px;border-radius:100px;white-space:nowrap;flex-shrink:0}
  225. .jobs-card-arrow{flex-shrink:0;color:#9CA3AF;transition:color .15s,transform .15s}
  226. .jobs-card:hover .jobs-card-arrow{color:var(--theme-color,#6C3AED);transform:translateX(2px)}
  227. .jobs-card-arrow svg{width:18px;height:18px}
  228. /* Skeleton (pendant chargement) */
  229. .jobs-skeleton{display:flex;flex-direction:column;gap:10px}
  230. .jobs-skel-card{background:#fff;border-radius:14px;padding:14px;display:flex;align-items:center;gap:14px;box-shadow:0 0 16px 0 rgba(0,0,0,0.04)}
  231. .jobs-skel-logo{width:50px;height:50px;border-radius:12px;background:linear-gradient(90deg,#F3F4F6 0%,#E5E7EB 50%,#F3F4F6 100%);background-size:200% 100%;animation:jobsSkelShim 1.4s ease-in-out infinite;flex-shrink:0}
  232. .jobs-skel-info{flex:1}
  233. .jobs-skel-line{height:14px;background:linear-gradient(90deg,#F3F4F6 0%,#E5E7EB 50%,#F3F4F6 100%);background-size:200% 100%;animation:jobsSkelShim 1.4s ease-in-out infinite;border-radius:4px;margin-bottom:6px}
  234. .jobs-skel-line.s1{width:60%}
  235. .jobs-skel-line.s2{width:40%;height:12px;margin-bottom:0}
  236. @keyframes jobsSkelShim{0%{background-position:200% 0}100%{background-position:-200% 0}}
  237. /* Bouton charger plus */
  238. .jobs-load-more{display:flex;width:100%;padding:14px;margin-top:16px;background:#fff;color:var(--theme-color,#6C3AED);border:1.5px dashed rgba(108,58,237,.35);border-radius:12px;font-size:13.5px;font-weight:700;cursor:pointer;font-family:inherit;align-items:center;justify-content:center;gap:8px;transition:background .15s,border-color .15s}
  239. .jobs-load-more:hover{background:#F5F3FF;border-color:var(--theme-color,#6C3AED);border-style:solid;color:var(--theme-color,#6C3AED)}
  240. .jobs-load-more svg{width:14px;height:14px}
  241. .jobs-load-more:disabled{opacity:.6;cursor:not-allowed}
  242. .jobs-load-more.loading{cursor:wait}
  243. .jobs-load-spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(108,58,237,.2);border-top-color:var(--theme-color,#6C3AED);border-radius:50%;animation:jobsLoadSpin .7s linear infinite}
  244. @keyframes jobsLoadSpin{to{transform:rotate(360deg)}}
  245. /* État vide */
  246. .jobs-empty{text-align:center;padding:40px 20px;background:#fff;border-radius:14px;box-shadow:0 0 16px 0 rgba(0,0,0,0.04)}
  247. .jobs-empty-icon{display:inline-flex;align-items:center;justify-content:center;width:56px;height:56px;border-radius:16px;background:#F5F3FF;color:var(--theme-color,#6C3AED);margin-bottom:14px}
  248. .jobs-empty-icon svg{width:24px;height:24px}
  249. .jobs-empty-title{font-size:16px;font-weight:700;color:#1E1B2E;margin:0 0 6px}
  250. .jobs-empty-text{font-size:13px;color:#6B7280;margin:0}
  251. </style>
  252. ";
  253. // line 143
  254. yield " ";
  255. yield (string) $macros["promo"]->getTemplateForMacro("macro_styles", $context, 143, $this->getSourceContext())->macro_styles(...[]);
  256. yield "
  257. ";
  258. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  259. $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  260. yield from [];
  261. }
  262. // line 146
  263. /**
  264. * @return iterable<null|scalar|\Stringable>
  265. */
  266. public function block_body(array $context, array $blocks = []): iterable
  267. {
  268. $macros = $this->macros;
  269. $__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  270. $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "body"));
  271. $__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  272. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "body"));
  273. // line 147
  274. yield " <div class=\"jobs-dash\">
  275. <div class=\"jobs-dash-header\">
  276. ";
  277. // line 150
  278. if ((((array_key_exists("currentSubKeyword", $context) && !(null === (isset($context["currentSubKeyword"]) || array_key_exists("currentSubKeyword", $context) ? $context["currentSubKeyword"] : (function () { throw new RuntimeError('Variable "currentSubKeyword" does not exist.', 150, $this->source); })()))) && array_key_exists("currentCityFilter", $context)) && !(null === (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 150, $this->source); })())))) {
  279. // line 151
  280. yield " ";
  281. // line 152
  282. yield " <h1 class=\"jobs-dash-title\">
  283. ";
  284. // line 153
  285. if ( !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 153, $this->source); })()), "icon", [], "any", false, false, false, 153))) {
  286. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 153, $this->source); })()), "icon", [], "any", false, false, false, 153), "html", null, true);
  287. yield " ";
  288. }
  289. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["currentSubKeyword"]) || array_key_exists("currentSubKeyword", $context) ? $context["currentSubKeyword"] : (function () { throw new RuntimeError('Variable "currentSubKeyword" does not exist.', 153, $this->source); })()), "html", null, true);
  290. yield " ";
  291. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.cityHeadingIn", [], "whr-public"), "html", null, true);
  292. yield " ";
  293. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 153, $this->source); })()), "label", [], "any", false, false, false, 153), "html", null, true);
  294. yield "
  295. </h1>
  296. <p class=\"jobs-dash-subtitle\">";
  297. // line 155
  298. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.cityKeywordSubtitle", ["%keyword%" => (isset($context["currentSubKeyword"]) || array_key_exists("currentSubKeyword", $context) ? $context["currentSubKeyword"] : (function () { throw new RuntimeError('Variable "currentSubKeyword" does not exist.', 155, $this->source); })()), "%city%" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 155, $this->source); })()), "label", [], "any", false, false, false, 155)], "whr-public"), "html", null, true);
  299. yield "</p>
  300. ";
  301. } elseif (( // line 156
  302. array_key_exists("currentFilter", $context) && !(null === (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 156, $this->source); })())))) {
  303. // line 157
  304. yield " <h1 class=\"jobs-dash-title\">
  305. ";
  306. // line 158
  307. if ( !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 158, $this->source); })()), "icon", [], "any", false, false, false, 158))) {
  308. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 158, $this->source); })()), "icon", [], "any", false, false, false, 158), "html", null, true);
  309. yield " ";
  310. }
  311. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(((CoreExtension::getAttribute($this->env, $this->source, ($context["currentFilter"] ?? null), "shortTitle", [], "any", true, true, false, 158)) ? (Twig\Extension\CoreExtension::default(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 158, $this->source); })()), "shortTitle", [], "any", false, false, false, 158), CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 158, $this->source); })()), "label", [], "any", false, false, false, 158))) : (CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 158, $this->source); })()), "label", [], "any", false, false, false, 158))), "html", null, true);
  312. yield "
  313. </h1>
  314. ";
  315. // line 160
  316. if ( !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 160, $this->source); })()), "shortDescription", [], "any", false, false, false, 160))) {
  317. // line 161
  318. yield " <p class=\"jobs-dash-subtitle\">";
  319. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 161, $this->source); })()), "shortDescription", [], "any", false, false, false, 161), "html", null, true);
  320. yield "</p>
  321. ";
  322. } else {
  323. // line 163
  324. yield " <p class=\"jobs-dash-subtitle\">";
  325. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.subtitle", [], "whr-public"), "html", null, true);
  326. yield "</p>
  327. ";
  328. }
  329. // line 165
  330. yield " ";
  331. } else {
  332. // line 166
  333. yield " <h1 class=\"jobs-dash-title\">";
  334. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.heading", [], "whr-public"), "html", null, true);
  335. yield "</h1>
  336. <p class=\"jobs-dash-subtitle\">";
  337. // line 167
  338. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.subtitle", [], "whr-public"), "html", null, true);
  339. yield "</p>
  340. ";
  341. }
  342. // line 169
  343. yield "
  344. ";
  345. // line 171
  346. yield " ";
  347. if ((array_key_exists("currentCityFilter", $context) && !(null === (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 171, $this->source); })())))) {
  348. // line 172
  349. yield " <div class=\"city-context-bar\">
  350. <span class=\"city-context-label\">";
  351. // line 173
  352. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.contextLabel", [], "whr-public"), "html", null, true);
  353. yield "</span>
  354. <span class=\"city-context-badge\">
  355. ";
  356. // line 175
  357. if ( !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 175, $this->source); })()), "icon", [], "any", false, false, false, 175))) {
  358. yield "<span class=\"city-context-badge-icon\">";
  359. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 175, $this->source); })()), "icon", [], "any", false, false, false, 175), "html", null, true);
  360. yield "</span>";
  361. }
  362. // line 176
  363. yield " <span>";
  364. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 176, $this->source); })()), "label", [], "any", false, false, false, 176), "html", null, true);
  365. yield "</span>
  366. </span>
  367. <a href=\"";
  368. // line 178
  369. if ((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 178, $this->source); })()), "request", [], "any", false, false, false, 178), "locale", [], "any", false, false, false, 178) == "en")) {
  370. yield (string) $this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("whileresume_jobs_list");
  371. } else {
  372. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_whileresume_jobs_list", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 178, $this->source); })()), "request", [], "any", false, false, false, 178), "locale", [], "any", false, false, false, 178)]), "html", null, true);
  373. }
  374. yield "\"
  375. class=\"city-context-clear-btn\"
  376. title=\"";
  377. // line 180
  378. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.clearCity", [], "whr-public"), "html", null, true);
  379. yield "\">
  380. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"9 18 15 12 9 6\" transform=\"rotate(180 12 12)\"/></svg>
  381. ";
  382. // line 182
  383. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.allCities", [], "whr-public"), "html", null, true);
  384. yield "
  385. </a>
  386. </div>
  387. ";
  388. }
  389. // line 186
  390. yield " </div>
  391. ";
  392. // line 189
  393. yield " <div class=\"wr-search-widget\">
  394. <div class=\"wr-search-input-wrap\">
  395. <span class=\"wr-search-icon\">
  396. <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>
  397. </span>
  398. <input type=\"text\" id=\"dashSearchInput\" class=\"wr-search-input\"
  399. placeholder=\"";
  400. // line 195
  401. if ((array_key_exists("currentCityFilter", $context) && !(null === (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 195, $this->source); })())))) {
  402. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.searchInCity", ["%city%" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 195, $this->source); })()), "label", [], "any", false, false, false, 195)], "whr-public"), "html", null, true);
  403. } else {
  404. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.show.searchPlaceholder", [], "whr-public"), "html", null, true);
  405. }
  406. yield "\"
  407. value=\"";
  408. // line 196
  409. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(((array_key_exists("initialQuery", $context)) ? (Twig\Extension\CoreExtension::default((isset($context["initialQuery"]) || array_key_exists("initialQuery", $context) ? $context["initialQuery"] : (function () { throw new RuntimeError('Variable "initialQuery" does not exist.', 196, $this->source); })()), "")) : ("")), "html", null, true);
  410. yield "\"
  411. autocomplete=\"off\">
  412. <span class=\"wr-search-kbd-hint\" id=\"dashSearchKbdHint\">";
  413. // line 198
  414. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.pressEnter", [], "whr-public"), "html", null, true);
  415. yield "</span>
  416. <button type=\"button\" id=\"dashSearchClear\" class=\"wr-search-clear\" aria-label=\"Clear\">
  417. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>
  418. </button>
  419. </div>
  420. </div>
  421. ";
  422. // line 206
  423. yield " ";
  424. if ((array_key_exists("searchKeywords", $context) && (Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["searchKeywords"]) || array_key_exists("searchKeywords", $context) ? $context["searchKeywords"] : (function () { throw new RuntimeError('Variable "searchKeywords" does not exist.', 206, $this->source); })())) > 0))) {
  425. // line 207
  426. yield " <div class=\"search-keywords\">
  427. <span class=\"search-keywords-label\">";
  428. // line 208
  429. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.show.keywordsLabel", [], "whr-public"), "html", null, true);
  430. yield "</span>
  431. ";
  432. // line 209
  433. $context['_parent'] = $context;
  434. $context['_seq'] = CoreExtension::ensureTraversable((isset($context["searchKeywords"]) || array_key_exists("searchKeywords", $context) ? $context["searchKeywords"] : (function () { throw new RuntimeError('Variable "searchKeywords" does not exist.', 209, $this->source); })()));
  435. foreach ($context['_seq'] as $context["_key"] => $context["kw"]) {
  436. // line 210
  437. yield " ";
  438. // line 211
  439. yield " ";
  440. $context["kwSlug"] = Twig\Extension\CoreExtension::trim(Twig\Extension\CoreExtension::replace(Twig\Extension\CoreExtension::lower($this->env->getCharset(), Twig\Extension\CoreExtension::replace(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "label", [], "any", false, false, false, 211), ["à" => "a", "â" => "a", "ä" => "a", "á" => "a", "ã" => "a", "å" => "a", "À" => "a", "Â" => "a", "Ä" => "a", "Á" => "a", "Ã" => "a", "Å" => "a", "é" => "e", "è" => "e", "ê" => "e", "ë" => "e", "ẽ" => "e", "É" => "e", "È" => "e", "Ê" => "e", "Ë" => "e", "î" => "i", "ï" => "i", "ì" => "i", "í" => "i", "Î" => "i", "Ï" => "i", "Ì" => "i", "Í" => "i", "ô" => "o", "ö" => "o", "ò" => "o", "ó" => "o", "õ" => "o", "ø" => "o", "Ô" => "o", "Ö" => "o", "Ò" => "o", "Ó" => "o", "Õ" => "o", "Ø" => "o", "û" => "u", "ü" => "u", "ù" => "u", "ú" => "u", "Û" => "u", "Ü" => "u", "Ù" => "u", "Ú" => "u", "ÿ" => "y", "ý" => "y", "Ÿ" => "y", "Ý" => "y", "ç" => "c", "Ç" => "c", "ñ" => "n", "Ñ" => "n", "œ" => "oe", "Œ" => "oe", "æ" => "ae", "Æ" => "ae", "\x27" => "-", " " => "-", "/" => "-", "_" => "-", "." => "-", "," => "-", "(" => "", "*" => "", ")" => "", "&" => "-and-", "+" => "-plus-", "#" => "", "?" => "", "!" => "", "\"" => "", "’" => "-", "`" => "-"])), ["--" => "-", "---" => "-"]), "-");
  441. // line 234
  442. yield "
  443. ";
  444. // line 235
  445. if ((array_key_exists("currentCityFilter", $context) && !(null === (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 235, $this->source); })())))) {
  446. // line 236
  447. yield " ";
  448. // line 237
  449. yield " <a href=\"";
  450. if ((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 237, $this->source); })()), "request", [], "any", false, false, false, 237), "locale", [], "any", false, false, false, 237) == "en")) {
  451. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cvs_application_jobs_filter_keyword", ["slug" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 237, $this->source); })()), "slug", [], "any", false, false, false, 237), "keyword" => (isset($context["kwSlug"]) || array_key_exists("kwSlug", $context) ? $context["kwSlug"] : (function () { throw new RuntimeError('Variable "kwSlug" does not exist.', 237, $this->source); })())]), "html", null, true);
  452. } else {
  453. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_cvs_application_jobs_filter_keyword", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 237, $this->source); })()), "request", [], "any", false, false, false, 237), "locale", [], "any", false, false, false, 237), "slug" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 237, $this->source); })()), "slug", [], "any", false, false, false, 237), "keyword" => (isset($context["kwSlug"]) || array_key_exists("kwSlug", $context) ? $context["kwSlug"] : (function () { throw new RuntimeError('Variable "kwSlug" does not exist.', 237, $this->source); })())]), "html", null, true);
  454. }
  455. yield "\"
  456. class=\"search-keyword-chip";
  457. // line 238
  458. if ((array_key_exists("currentSubKeywordSlug", $context) && ((isset($context["currentSubKeywordSlug"]) || array_key_exists("currentSubKeywordSlug", $context) ? $context["currentSubKeywordSlug"] : (function () { throw new RuntimeError('Variable "currentSubKeywordSlug" does not exist.', 238, $this->source); })()) == (isset($context["kwSlug"]) || array_key_exists("kwSlug", $context) ? $context["kwSlug"] : (function () { throw new RuntimeError('Variable "kwSlug" does not exist.', 238, $this->source); })())))) {
  459. yield " active";
  460. }
  461. yield "\">
  462. ";
  463. // line 239
  464. if ( !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "icon", [], "any", false, false, false, 239))) {
  465. yield "<span class=\"search-keyword-chip-icon\">";
  466. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "icon", [], "any", false, false, false, 239), "html", null, true);
  467. yield "</span>";
  468. }
  469. // line 240
  470. yield " <span>";
  471. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "label", [], "any", false, false, false, 240), "html", null, true);
  472. yield "</span>
  473. </a>
  474. ";
  475. } else {
  476. // line 243
  477. yield " ";
  478. // line 244
  479. yield " <button type=\"button\"
  480. class=\"search-keyword-chip\"
  481. data-keyword=\"";
  482. // line 246
  483. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(((CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "searchKeyword", [], "any", true, true, false, 246)) ? (Twig\Extension\CoreExtension::default(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "searchKeyword", [], "any", false, false, false, 246), CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "label", [], "any", false, false, false, 246))) : (CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "label", [], "any", false, false, false, 246))), "html", null, true);
  484. yield "\">
  485. ";
  486. // line 247
  487. if ( !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "icon", [], "any", false, false, false, 247))) {
  488. yield "<span class=\"search-keyword-chip-icon\">";
  489. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "icon", [], "any", false, false, false, 247), "html", null, true);
  490. yield "</span>";
  491. }
  492. // line 248
  493. yield " <span>";
  494. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "label", [], "any", false, false, false, 248), "html", null, true);
  495. yield "</span>
  496. </button>
  497. ";
  498. }
  499. // line 251
  500. yield " ";
  501. }
  502. $_parent = $context['_parent'];
  503. unset($context['_seq'], $context['_key'], $context['kw'], $context['_parent']);
  504. $context = array_intersect_key($context, $_parent);
  505. $context += $_parent;
  506. // line 252
  507. yield " </div>
  508. ";
  509. }
  510. // line 254
  511. yield "
  512. ";
  513. // line 256
  514. yield " ";
  515. if ((array_key_exists("sidebarFiltersCities", $context) && (Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCities"]) || array_key_exists("sidebarFiltersCities", $context) ? $context["sidebarFiltersCities"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCities" does not exist.', 256, $this->source); })())) > 0))) {
  516. // line 257
  517. yield " ";
  518. $context["citiesInitial"] = 14;
  519. // line 258
  520. yield " <div class=\"filter-section\" id=\"filterSectionCities\">
  521. <div class=\"filter-section-head\">
  522. <h3 class=\"filter-section-title\">
  523. <span class=\"filter-section-title-icon\">
  524. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z\"/><circle cx=\"12\" cy=\"10\" r=\"3\"/></svg>
  525. </span>
  526. ";
  527. // line 264
  528. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.popularCities", [], "whr-public"), "html", null, true);
  529. yield "
  530. </h3>
  531. ";
  532. // line 267
  533. yield " ";
  534. if ((Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCities"]) || array_key_exists("sidebarFiltersCities", $context) ? $context["sidebarFiltersCities"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCities" does not exist.', 267, $this->source); })())) > (isset($context["citiesInitial"]) || array_key_exists("citiesInitial", $context) ? $context["citiesInitial"] : (function () { throw new RuntimeError('Variable "citiesInitial" does not exist.', 267, $this->source); })()))) {
  535. // line 268
  536. yield " <div class=\"dash-filter-search\">
  537. <span class=\"dash-filter-search-icon\">
  538. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>
  539. </span>
  540. <input type=\"text\"
  541. class=\"dash-filter-search-input\"
  542. data-search-target=\"filterChipsCities\"
  543. placeholder=\"";
  544. // line 275
  545. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.filterSearchCity", [], "whr-public"), "html", null, true);
  546. yield "\"
  547. autocomplete=\"off\">
  548. </div>
  549. ";
  550. }
  551. // line 279
  552. yield " </div>
  553. <div class=\"filter-chips dash-filter-chips\" id=\"filterChipsCities\" data-initial=\"";
  554. // line 280
  555. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["citiesInitial"]) || array_key_exists("citiesInitial", $context) ? $context["citiesInitial"] : (function () { throw new RuntimeError('Variable "citiesInitial" does not exist.', 280, $this->source); })()), "html", null, true);
  556. yield "\">
  557. ";
  558. // line 281
  559. $context['_parent'] = $context;
  560. $context['_seq'] = CoreExtension::ensureTraversable((isset($context["sidebarFiltersCities"]) || array_key_exists("sidebarFiltersCities", $context) ? $context["sidebarFiltersCities"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCities" does not exist.', 281, $this->source); })()));
  561. $context['loop'] = [
  562. 'parent' => $context['_parent'],
  563. 'index0' => 0,
  564. 'index' => 1,
  565. 'first' => true,
  566. ];
  567. if (is_array($context['_seq']) || (is_object($context['_seq']) && $context['_seq'] instanceof \Countable)) {
  568. $length = count($context['_seq']);
  569. $context['loop']['revindex0'] = $length - 1;
  570. $context['loop']['revindex'] = $length;
  571. $context['loop']['length'] = $length;
  572. $context['loop']['last'] = 1 === $length;
  573. }
  574. foreach ($context['_seq'] as $context["_key"] => $context["f"]) {
  575. // line 282
  576. yield " <a href=\"";
  577. if ((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 282, $this->source); })()), "request", [], "any", false, false, false, 282), "locale", [], "any", false, false, false, 282) == "en")) {
  578. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cvs_application_jobs_filter", ["slug" => CoreExtension::getAttribute($this->env, $this->source, $context["f"], "slug", [], "any", false, false, false, 282)]), "html", null, true);
  579. } else {
  580. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_cvs_application_jobs_filter", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 282, $this->source); })()), "request", [], "any", false, false, false, 282), "locale", [], "any", false, false, false, 282), "slug" => CoreExtension::getAttribute($this->env, $this->source, $context["f"], "slug", [], "any", false, false, false, 282)]), "html", null, true);
  581. }
  582. yield "\"
  583. class=\"filter-chip dash-filter-item";
  584. // line 283
  585. if (((array_key_exists("currentFilter", $context) && !(null === (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 283, $this->source); })()))) && (CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 283, $this->source); })()), "slug", [], "any", false, false, false, 283) == CoreExtension::getAttribute($this->env, $this->source, $context["f"], "slug", [], "any", false, false, false, 283)))) {
  586. yield " active";
  587. }
  588. yield "\"
  589. data-filter-slug=\"";
  590. // line 284
  591. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "slug", [], "any", false, false, false, 284), "html", null, true);
  592. yield "\"
  593. data-filter-label=\"";
  594. // line 285
  595. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "label", [], "any", false, false, false, 285), "html", null, true);
  596. yield "\"
  597. data-index=\"";
  598. // line 286
  599. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["loop"], "index0", [], "any", false, false, false, 286), "html", null, true);
  600. yield "\"";
  601. if ((CoreExtension::getAttribute($this->env, $this->source, $context["loop"], "index0", [], "any", false, false, false, 286) >= (isset($context["citiesInitial"]) || array_key_exists("citiesInitial", $context) ? $context["citiesInitial"] : (function () { throw new RuntimeError('Variable "citiesInitial" does not exist.', 286, $this->source); })()))) {
  602. yield " style=\"display:none\"";
  603. }
  604. yield ">
  605. ";
  606. // line 287
  607. if ( !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "icon", [], "any", false, false, false, 287))) {
  608. yield "<span class=\"filter-chip-icon\">";
  609. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "icon", [], "any", false, false, false, 287), "html", null, true);
  610. yield "</span>";
  611. }
  612. // line 288
  613. yield " <span>";
  614. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "label", [], "any", false, false, false, 288), "html", null, true);
  615. yield "</span>
  616. <span class=\"filter-chip-arrow\">
  617. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"9 18 15 12 9 6\"/></svg>
  618. </span>
  619. </a>
  620. ";
  621. ++$context['loop']['index0'];
  622. ++$context['loop']['index'];
  623. $context['loop']['first'] = false;
  624. if (isset($context['loop']['revindex0'], $context['loop']['revindex'])) {
  625. --$context['loop']['revindex0'];
  626. --$context['loop']['revindex'];
  627. $context['loop']['last'] = 0 === $context['loop']['revindex0'];
  628. }
  629. }
  630. $_parent = $context['_parent'];
  631. unset($context['_seq'], $context['_key'], $context['f'], $context['_parent'], $context['loop']);
  632. $context = array_intersect_key($context, $_parent);
  633. $context += $_parent;
  634. // line 294
  635. yield " <div class=\"dash-filter-empty\">";
  636. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.filterNoResults", [], "whr-public"), "html", null, true);
  637. yield "</div>
  638. </div>
  639. ";
  640. // line 296
  641. if ((Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCities"]) || array_key_exists("sidebarFiltersCities", $context) ? $context["sidebarFiltersCities"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCities" does not exist.', 296, $this->source); })())) > (isset($context["citiesInitial"]) || array_key_exists("citiesInitial", $context) ? $context["citiesInitial"] : (function () { throw new RuntimeError('Variable "citiesInitial" does not exist.', 296, $this->source); })()))) {
  642. // line 297
  643. yield " <div class=\"dash-filter-more-wrap\">
  644. <button type=\"button\" class=\"dash-filter-more-btn\" data-target=\"filterChipsCities\" data-section=\"filterSectionCities\">
  645. <span class=\"dash-filter-more-icon\">
  646. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"6 9 12 15 18 9\"/></svg>
  647. </span>
  648. <span class=\"dash-filter-more-label\" data-more=\"";
  649. // line 302
  650. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.showMoreCities", ["%n%" => (Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCities"]) || array_key_exists("sidebarFiltersCities", $context) ? $context["sidebarFiltersCities"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCities" does not exist.', 302, $this->source); })())) - (isset($context["citiesInitial"]) || array_key_exists("citiesInitial", $context) ? $context["citiesInitial"] : (function () { throw new RuntimeError('Variable "citiesInitial" does not exist.', 302, $this->source); })()))], "whr-public"), "html", null, true);
  651. yield "\" data-less=\"";
  652. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.showLessCities", [], "whr-public"), "html", null, true);
  653. yield "\">";
  654. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.showMoreCities", ["%n%" => (Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCities"]) || array_key_exists("sidebarFiltersCities", $context) ? $context["sidebarFiltersCities"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCities" does not exist.', 302, $this->source); })())) - (isset($context["citiesInitial"]) || array_key_exists("citiesInitial", $context) ? $context["citiesInitial"] : (function () { throw new RuntimeError('Variable "citiesInitial" does not exist.', 302, $this->source); })()))], "whr-public"), "html", null, true);
  655. yield "</span>
  656. </button>
  657. </div>
  658. ";
  659. }
  660. // line 306
  661. yield " </div>
  662. ";
  663. }
  664. // line 308
  665. yield "
  666. ";
  667. // line 310
  668. yield " ";
  669. if ((array_key_exists("sidebarFiltersCategories", $context) && (Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCategories"]) || array_key_exists("sidebarFiltersCategories", $context) ? $context["sidebarFiltersCategories"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCategories" does not exist.', 310, $this->source); })())) > 0))) {
  670. // line 311
  671. yield " ";
  672. $context["categoriesInitial"] = 8;
  673. // line 312
  674. yield " <div class=\"filter-section\" id=\"filterSectionCategories\">
  675. <div class=\"filter-section-head\">
  676. <h3 class=\"filter-section-title\">
  677. <span class=\"filter-section-title-icon\">
  678. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"2\" y=\"7\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M16 7V5a4 4 0 00-8 0v2\"/></svg>
  679. </span>
  680. ";
  681. // line 318
  682. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.popularCategories", [], "whr-public"), "html", null, true);
  683. yield "
  684. </h3>
  685. ";
  686. // line 321
  687. yield " ";
  688. if ((Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCategories"]) || array_key_exists("sidebarFiltersCategories", $context) ? $context["sidebarFiltersCategories"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCategories" does not exist.', 321, $this->source); })())) > (isset($context["categoriesInitial"]) || array_key_exists("categoriesInitial", $context) ? $context["categoriesInitial"] : (function () { throw new RuntimeError('Variable "categoriesInitial" does not exist.', 321, $this->source); })()))) {
  689. // line 322
  690. yield " <div class=\"dash-filter-search\">
  691. <span class=\"dash-filter-search-icon\">
  692. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>
  693. </span>
  694. <input type=\"text\"
  695. class=\"dash-filter-search-input\"
  696. data-search-target=\"filterChipsCategories\"
  697. placeholder=\"";
  698. // line 329
  699. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.filterSearchCategory", [], "whr-public"), "html", null, true);
  700. yield "\"
  701. autocomplete=\"off\">
  702. </div>
  703. ";
  704. }
  705. // line 333
  706. yield " </div>
  707. <div class=\"filter-chips dash-filter-chips\" id=\"filterChipsCategories\" data-initial=\"";
  708. // line 334
  709. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["categoriesInitial"]) || array_key_exists("categoriesInitial", $context) ? $context["categoriesInitial"] : (function () { throw new RuntimeError('Variable "categoriesInitial" does not exist.', 334, $this->source); })()), "html", null, true);
  710. yield "\">
  711. ";
  712. // line 335
  713. $context['_parent'] = $context;
  714. $context['_seq'] = CoreExtension::ensureTraversable((isset($context["sidebarFiltersCategories"]) || array_key_exists("sidebarFiltersCategories", $context) ? $context["sidebarFiltersCategories"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCategories" does not exist.', 335, $this->source); })()));
  715. $context['loop'] = [
  716. 'parent' => $context['_parent'],
  717. 'index0' => 0,
  718. 'index' => 1,
  719. 'first' => true,
  720. ];
  721. if (is_array($context['_seq']) || (is_object($context['_seq']) && $context['_seq'] instanceof \Countable)) {
  722. $length = count($context['_seq']);
  723. $context['loop']['revindex0'] = $length - 1;
  724. $context['loop']['revindex'] = $length;
  725. $context['loop']['length'] = $length;
  726. $context['loop']['last'] = 1 === $length;
  727. }
  728. foreach ($context['_seq'] as $context["_key"] => $context["f"]) {
  729. // line 336
  730. yield " <a href=\"";
  731. if ((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 336, $this->source); })()), "request", [], "any", false, false, false, 336), "locale", [], "any", false, false, false, 336) == "en")) {
  732. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cvs_application_jobs_filter", ["slug" => CoreExtension::getAttribute($this->env, $this->source, $context["f"], "slug", [], "any", false, false, false, 336)]), "html", null, true);
  733. } else {
  734. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_cvs_application_jobs_filter", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 336, $this->source); })()), "request", [], "any", false, false, false, 336), "locale", [], "any", false, false, false, 336), "slug" => CoreExtension::getAttribute($this->env, $this->source, $context["f"], "slug", [], "any", false, false, false, 336)]), "html", null, true);
  735. }
  736. yield "\"
  737. class=\"filter-chip dash-filter-item";
  738. // line 337
  739. if (((array_key_exists("currentFilter", $context) && !(null === (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 337, $this->source); })()))) && (CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentFilter"]) || array_key_exists("currentFilter", $context) ? $context["currentFilter"] : (function () { throw new RuntimeError('Variable "currentFilter" does not exist.', 337, $this->source); })()), "slug", [], "any", false, false, false, 337) == CoreExtension::getAttribute($this->env, $this->source, $context["f"], "slug", [], "any", false, false, false, 337)))) {
  740. yield " active";
  741. }
  742. yield "\"
  743. data-filter-slug=\"";
  744. // line 338
  745. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "slug", [], "any", false, false, false, 338), "html", null, true);
  746. yield "\"
  747. data-filter-label=\"";
  748. // line 339
  749. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "label", [], "any", false, false, false, 339), "html", null, true);
  750. yield "\"
  751. data-index=\"";
  752. // line 340
  753. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["loop"], "index0", [], "any", false, false, false, 340), "html", null, true);
  754. yield "\"";
  755. if ((CoreExtension::getAttribute($this->env, $this->source, $context["loop"], "index0", [], "any", false, false, false, 340) >= (isset($context["categoriesInitial"]) || array_key_exists("categoriesInitial", $context) ? $context["categoriesInitial"] : (function () { throw new RuntimeError('Variable "categoriesInitial" does not exist.', 340, $this->source); })()))) {
  756. yield " style=\"display:none\"";
  757. }
  758. yield ">
  759. ";
  760. // line 341
  761. if ( !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "icon", [], "any", false, false, false, 341))) {
  762. yield "<span class=\"filter-chip-icon\">";
  763. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "icon", [], "any", false, false, false, 341), "html", null, true);
  764. yield "</span>";
  765. }
  766. // line 342
  767. yield " <span>";
  768. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["f"], "label", [], "any", false, false, false, 342), "html", null, true);
  769. yield "</span>
  770. <span class=\"filter-chip-arrow\">
  771. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"9 18 15 12 9 6\"/></svg>
  772. </span>
  773. </a>
  774. ";
  775. ++$context['loop']['index0'];
  776. ++$context['loop']['index'];
  777. $context['loop']['first'] = false;
  778. if (isset($context['loop']['revindex0'], $context['loop']['revindex'])) {
  779. --$context['loop']['revindex0'];
  780. --$context['loop']['revindex'];
  781. $context['loop']['last'] = 0 === $context['loop']['revindex0'];
  782. }
  783. }
  784. $_parent = $context['_parent'];
  785. unset($context['_seq'], $context['_key'], $context['f'], $context['_parent'], $context['loop']);
  786. $context = array_intersect_key($context, $_parent);
  787. $context += $_parent;
  788. // line 348
  789. yield " <div class=\"dash-filter-empty\">";
  790. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.filterNoResults", [], "whr-public"), "html", null, true);
  791. yield "</div>
  792. </div>
  793. ";
  794. // line 350
  795. if ((Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCategories"]) || array_key_exists("sidebarFiltersCategories", $context) ? $context["sidebarFiltersCategories"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCategories" does not exist.', 350, $this->source); })())) > (isset($context["categoriesInitial"]) || array_key_exists("categoriesInitial", $context) ? $context["categoriesInitial"] : (function () { throw new RuntimeError('Variable "categoriesInitial" does not exist.', 350, $this->source); })()))) {
  796. // line 351
  797. yield " <div class=\"dash-filter-more-wrap\">
  798. <button type=\"button\" class=\"dash-filter-more-btn\" data-target=\"filterChipsCategories\" data-section=\"filterSectionCategories\">
  799. <span class=\"dash-filter-more-icon\">
  800. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"6 9 12 15 18 9\"/></svg>
  801. </span>
  802. <span class=\"dash-filter-more-label\" data-more=\"";
  803. // line 356
  804. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.showMoreCategories", ["%n%" => (Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCategories"]) || array_key_exists("sidebarFiltersCategories", $context) ? $context["sidebarFiltersCategories"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCategories" does not exist.', 356, $this->source); })())) - (isset($context["categoriesInitial"]) || array_key_exists("categoriesInitial", $context) ? $context["categoriesInitial"] : (function () { throw new RuntimeError('Variable "categoriesInitial" does not exist.', 356, $this->source); })()))], "whr-public"), "html", null, true);
  805. yield "\" data-less=\"";
  806. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.showLessCategories", [], "whr-public"), "html", null, true);
  807. yield "\">";
  808. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.showMoreCategories", ["%n%" => (Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["sidebarFiltersCategories"]) || array_key_exists("sidebarFiltersCategories", $context) ? $context["sidebarFiltersCategories"] : (function () { throw new RuntimeError('Variable "sidebarFiltersCategories" does not exist.', 356, $this->source); })())) - (isset($context["categoriesInitial"]) || array_key_exists("categoriesInitial", $context) ? $context["categoriesInitial"] : (function () { throw new RuntimeError('Variable "categoriesInitial" does not exist.', 356, $this->source); })()))], "whr-public"), "html", null, true);
  809. yield "</span>
  810. </button>
  811. </div>
  812. ";
  813. }
  814. // line 360
  815. yield " </div>
  816. ";
  817. }
  818. // line 362
  819. yield "
  820. ";
  821. // line 364
  822. yield " <div class=\"jobs-results-info\" id=\"jobsResultsInfo\">
  823. <span id=\"jobsResultsCount\">";
  824. // line 365
  825. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.loading", [], "whr-public"), "html", null, true);
  826. yield "</span>
  827. </div>
  828. ";
  829. // line 369
  830. yield " <div class=\"jobs-list\" id=\"jobsList\">
  831. <div class=\"jobs-skeleton\">
  832. ";
  833. // line 371
  834. $context['_parent'] = $context;
  835. $context['_seq'] = CoreExtension::ensureTraversable(range(1, 5));
  836. foreach ($context['_seq'] as $context["_key"] => $context["i"]) {
  837. // line 372
  838. yield " <div class=\"jobs-skel-card\">
  839. <div class=\"jobs-skel-logo\"></div>
  840. <div class=\"jobs-skel-info\">
  841. <div class=\"jobs-skel-line s1\"></div>
  842. <div class=\"jobs-skel-line s2\"></div>
  843. </div>
  844. </div>
  845. ";
  846. }
  847. $_parent = $context['_parent'];
  848. unset($context['_seq'], $context['_key'], $context['i'], $context['_parent']);
  849. $context = array_intersect_key($context, $_parent);
  850. $context += $_parent;
  851. // line 380
  852. yield " </div>
  853. </div>
  854. ";
  855. // line 384
  856. yield " <button type=\"button\" class=\"jobs-load-more\" id=\"jobsLoadMore\" style=\"display:none;\">
  857. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"6 9 12 15 18 9\"/></svg>
  858. <span id=\"jobsLoadMoreLabel\">";
  859. // line 386
  860. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.loadMore", [], "whr-public"), "html", null, true);
  861. yield "</span>
  862. </button>
  863. ";
  864. // line 392
  865. yield " ";
  866. if ((null === CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 392, $this->source); })()), "user", [], "any", false, false, false, 392))) {
  867. // line 393
  868. yield " ";
  869. yield (string) $macros["promo"]->getTemplateForMacro("macro_card_template", $context, 393, $this->getSourceContext())->macro_card_template(...[["template_id" => "jobs-promo-card-template", "login_url" => (((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, // line 395
  870. (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 395, $this->source); })()), "request", [], "any", false, false, false, 395), "locale", [], "any", false, false, false, 395) == "en")) ? ($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cvs_gestion_candidates_dashboard")) : ($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_cvs_gestion_candidates_dashboard", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, // line 397
  871. (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 397, $this->source); })()), "request", [], "any", false, false, false, 397), "locale", [], "any", false, false, false, 397)])))]]);
  872. // line 398
  873. yield "
  874. ";
  875. }
  876. // line 400
  877. yield "
  878. </div>
  879. ";
  880. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  881. $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  882. yield from [];
  883. }
  884. // line 404
  885. /**
  886. * @return iterable<null|scalar|\Stringable>
  887. */
  888. public function block_footerjs(array $context, array $blocks = []): iterable
  889. {
  890. $macros = $this->macros;
  891. $__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  892. $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "footerjs"));
  893. $__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  894. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "footerjs"));
  895. // line 405
  896. yield " <script>
  897. (function(){
  898. \x27use strict\x27;
  899. try {
  900. var LOCALE = \x27";
  901. // line 410
  902. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 410, $this->source); })()), "request", [], "any", false, false, false, 410), "locale", [], "any", false, false, false, 410), "html", null, true);
  903. yield "\x27;
  904. var IS_AUTHENTICATED = ";
  905. // line 411
  906. yield (string) (((($tmp = CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 411, $this->source); })()), "user", [], "any", false, false, false, 411)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) ? ("true") : ("false"));
  907. yield ";
  908. var SEARCH_API_URL = \x27";
  909. // line 413
  910. yield (string) (((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 413, $this->source); })()), "request", [], "any", false, false, false, 413), "locale", [], "any", false, false, false, 413) == "en")) ? ($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cvs_application_jobs_api_search")) : ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_cvs_application_jobs_api_search", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 413, $this->source); })()), "request", [], "any", false, false, false, 413), "locale", [], "any", false, false, false, 413)]), "html", null, true)));
  911. yield "\x27;
  912. var JOB_SHOW_URL_TEMPLATE = \x27";
  913. // line 414
  914. yield (string) (((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 414, $this->source); })()), "request", [], "any", false, false, false, 414), "locale", [], "any", false, false, false, 414) == "en")) ? ($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cvs_application_jobs_api_search", ["slug" => "__SLUG__"])) : ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_cvs_application_job_show", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 414, $this->source); })()), "request", [], "any", false, false, false, 414), "locale", [], "any", false, false, false, 414), "slug" => "__SLUG__"]), "html", null, true)));
  915. yield "\x27;
  916. var PAGE_SIZE = 10;
  917. var currentQuery = ";
  918. // line 417
  919. yield (string) json_encode(((array_key_exists("initialQuery", $context)) ? (Twig\Extension\CoreExtension::default((isset($context["initialQuery"]) || array_key_exists("initialQuery", $context) ? $context["initialQuery"] : (function () { throw new RuntimeError('Variable "initialQuery" does not exist.', 417, $this->source); })()), "")) : ("")));
  920. yield ";
  921. var currentOffset = 0;
  922. var currentTotal = 0;
  923. var isFetching = false;
  924. // Contexte ville (vient du serveur si on est sur /jobs/paris/... sinon vide)
  925. var CITY_KEYWORD = ";
  926. // line 423
  927. yield (string) json_encode(((array_key_exists("currentCityKeyword", $context)) ? (Twig\Extension\CoreExtension::default((isset($context["currentCityKeyword"]) || array_key_exists("currentCityKeyword", $context) ? $context["currentCityKeyword"] : (function () { throw new RuntimeError('Variable "currentCityKeyword" does not exist.', 423, $this->source); })()), "")) : ("")));
  928. yield ";
  929. // Slug ville pour générer les URLs de redirection au submit
  930. var CITY_SLUG = ";
  931. // line 426
  932. yield (string) (((array_key_exists("currentCityFilter", $context) && !(null === (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 426, $this->source); })())))) ? (json_encode(CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 426, $this->source); })()), "slug", [], "any", false, false, false, 426))) : ("null"));
  933. yield ";
  934. // URL pour le dashboard global (sans ville)
  935. var DASHBOARD_URL = \x27";
  936. // line 429
  937. yield (string) (((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 429, $this->source); })()), "request", [], "any", false, false, false, 429), "locale", [], "any", false, false, false, 429) == "en")) ? ($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("whileresume_jobs_list")) : ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_whileresume_jobs_list", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 429, $this->source); })()), "request", [], "any", false, false, false, 429), "locale", [], "any", false, false, false, 429)]), "html", null, true)));
  938. yield "\x27;
  939. // Templates URL pour /jobs/{citySlug} et /jobs/{citySlug}/{keyword}
  940. var CITY_LANDING_URL_TEMPLATE = ";
  941. // line 432
  942. yield (string) (((array_key_exists("currentCityFilter", $context) && !(null === (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 432, $this->source); })())))) ? (json_encode((((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, // line 433
  943. (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 433, $this->source); })()), "request", [], "any", false, false, false, 433), "locale", [], "any", false, false, false, 433) == "en")) ? ($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cvs_application_jobs_filter", ["slug" => CoreExtension::getAttribute($this->env, $this->source, // line 434
  944. (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 434, $this->source); })()), "slug", [], "any", false, false, false, 434)])) : ($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_cvs_application_jobs_filter", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, // line 435
  945. (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 435, $this->source); })()), "request", [], "any", false, false, false, 435), "locale", [], "any", false, false, false, 435), "slug" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 435, $this->source); })()), "slug", [], "any", false, false, false, 435)]))))) : ("null"));
  946. // line 436
  947. yield ";
  948. var CITY_KEYWORD_URL_TEMPLATE = ";
  949. // line 437
  950. yield (string) (((array_key_exists("currentCityFilter", $context) && !(null === (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 437, $this->source); })())))) ? (json_encode((((CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, // line 438
  951. (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 438, $this->source); })()), "request", [], "any", false, false, false, 438), "locale", [], "any", false, false, false, 438) == "en")) ? ($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cvs_application_jobs_filter_keyword", ["slug" => CoreExtension::getAttribute($this->env, $this->source, // line 439
  952. (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 439, $this->source); })()), "slug", [], "any", false, false, false, 439), "keyword" => "kwplaceholder"])) : ($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("locale_cvs_application_jobs_filter_keyword", ["_locale" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, // line 440
  953. (isset($context["app"]) || array_key_exists("app", $context) ? $context["app"] : (function () { throw new RuntimeError('Variable "app" does not exist.', 440, $this->source); })()), "request", [], "any", false, false, false, 440), "locale", [], "any", false, false, false, 440), "slug" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["currentCityFilter"]) || array_key_exists("currentCityFilter", $context) ? $context["currentCityFilter"] : (function () { throw new RuntimeError('Variable "currentCityFilter" does not exist.', 440, $this->source); })()), "slug", [], "any", false, false, false, 440), "keyword" => "kwplaceholder"]))))) : ("null"));
  954. // line 441
  955. yield ";
  956. // Liste des keywords BDD avec leur slug (pour le matching côté client au submit)
  957. var KEYWORDS_DATA = [
  958. ";
  959. // line 445
  960. $context['_parent'] = $context;
  961. $context['_seq'] = CoreExtension::ensureTraversable(((array_key_exists("searchKeywords", $context)) ? (Twig\Extension\CoreExtension::default((isset($context["searchKeywords"]) || array_key_exists("searchKeywords", $context) ? $context["searchKeywords"] : (function () { throw new RuntimeError('Variable "searchKeywords" does not exist.', 445, $this->source); })()), [])) : ([])));
  962. $context['loop'] = [
  963. 'parent' => $context['_parent'],
  964. 'index0' => 0,
  965. 'index' => 1,
  966. 'first' => true,
  967. ];
  968. if (is_array($context['_seq']) || (is_object($context['_seq']) && $context['_seq'] instanceof \Countable)) {
  969. $length = count($context['_seq']);
  970. $context['loop']['revindex0'] = $length - 1;
  971. $context['loop']['revindex'] = $length;
  972. $context['loop']['length'] = $length;
  973. $context['loop']['last'] = 1 === $length;
  974. }
  975. foreach ($context['_seq'] as $context["_key"] => $context["kw"]) {
  976. // line 446
  977. yield " ";
  978. $context["kwSlugForJs"] = Twig\Extension\CoreExtension::trim(Twig\Extension\CoreExtension::replace(Twig\Extension\CoreExtension::lower($this->env->getCharset(), Twig\Extension\CoreExtension::replace(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "label", [], "any", false, false, false, 446), ["à" => "a", "â" => "a", "ä" => "a", "á" => "a", "ã" => "a", "å" => "a", "À" => "a", "Â" => "a", "Ä" => "a", "Á" => "a", "Ã" => "a", "Å" => "a", "é" => "e", "è" => "e", "ê" => "e", "ë" => "e", "ẽ" => "e", "É" => "e", "È" => "e", "Ê" => "e", "Ë" => "e", "î" => "i", "ï" => "i", "ì" => "i", "í" => "i", "Î" => "i", "Ï" => "i", "Ì" => "i", "Í" => "i", "ô" => "o", "ö" => "o", "ò" => "o", "ó" => "o", "õ" => "o", "ø" => "o", "Ô" => "o", "Ö" => "o", "Ò" => "o", "Ó" => "o", "Õ" => "o", "Ø" => "o", "û" => "u", "ü" => "u", "ù" => "u", "ú" => "u", "Û" => "u", "Ü" => "u", "Ù" => "u", "Ú" => "u", "ÿ" => "y", "ý" => "y", "Ÿ" => "y", "Ý" => "y", "ç" => "c", "Ç" => "c", "ñ" => "n", "Ñ" => "n", "œ" => "oe", "Œ" => "oe", "æ" => "ae", "Æ" => "ae", "\x27" => "-", " " => "-", "/" => "-", "_" => "-", "." => "-", "," => "-", "(" => "", "*" => "", ")" => "", "&" => "-and-", "+" => "-plus-", "#" => "", "?" => "", "!" => "", "\"" => "", "’" => "-", "`" => "-"])), ["--" => "-", "---" => "-"]), "-");
  979. // line 468
  980. yield " { label: ";
  981. yield (string) json_encode(CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "label", [], "any", false, false, false, 468));
  982. yield ", searchKeyword: ";
  983. yield (string) json_encode((((($tmp = CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "searchKeyword", [], "any", false, false, false, 468)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) ? (CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "searchKeyword", [], "any", false, false, false, 468)) : (CoreExtension::getAttribute($this->env, $this->source, $context["kw"], "label", [], "any", false, false, false, 468))));
  984. yield ", slug: ";
  985. yield (string) json_encode((isset($context["kwSlugForJs"]) || array_key_exists("kwSlugForJs", $context) ? $context["kwSlugForJs"] : (function () { throw new RuntimeError('Variable "kwSlugForJs" does not exist.', 468, $this->source); })()));
  986. yield " }";
  987. if ( !(($tmp = CoreExtension::getAttribute($this->env, $this->source, $context["loop"], "last", [], "any", false, false, false, 468)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
  988. yield ",";
  989. }
  990. // line 469
  991. yield " ";
  992. ++$context['loop']['index0'];
  993. ++$context['loop']['index'];
  994. $context['loop']['first'] = false;
  995. if (isset($context['loop']['revindex0'], $context['loop']['revindex'])) {
  996. --$context['loop']['revindex0'];
  997. --$context['loop']['revindex'];
  998. $context['loop']['last'] = 0 === $context['loop']['revindex0'];
  999. }
  1000. }
  1001. $_parent = $context['_parent'];
  1002. unset($context['_seq'], $context['_key'], $context['kw'], $context['_parent'], $context['loop']);
  1003. $context = array_intersect_key($context, $_parent);
  1004. $context += $_parent;
  1005. // line 470
  1006. yield " ];
  1007. var input = document.getElementById(\x27dashSearchInput\x27);
  1008. var clearBtn = document.getElementById(\x27dashSearchClear\x27);
  1009. var listEl = document.getElementById(\x27jobsList\x27);
  1010. var infoEl = document.getElementById(\x27jobsResultsCount\x27);
  1011. var loadMoreBtn = document.getElementById(\x27jobsLoadMore\x27);
  1012. var loadMoreLabel = document.getElementById(\x27jobsLoadMoreLabel\x27);
  1013. function escapeHtml(s){
  1014. if(s == null) return \x27\x27;
  1015. return String(s).replace(/[&<>\"\x27]/g, function(c){
  1016. return {\x27&\x27:\x27&amp;\x27,\x27<\x27:\x27&lt;\x27,\x27>\x27:\x27&gt;\x27,\x27\"\x27:\x27&quot;\x27,\"\x27\":\x27&#39;\x27}[c];
  1017. });
  1018. }
  1019. function buildJobCard(item){
  1020. // Fallback logo : icône \"immeuble\" stylisée si pas de logo entreprise
  1021. var fallbackLogo =
  1022. \x27<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\x27 +
  1023. \x27<path d=\"M3 21h18\"/>\x27 +
  1024. \x27<path d=\"M5 21V7l8-4v18\"/>\x27 +
  1025. \x27<path d=\"M19 21V11l-6-4\"/>\x27 +
  1026. \x27<path d=\"M9 9h.01\"/>\x27 +
  1027. \x27<path d=\"M9 13h.01\"/>\x27 +
  1028. \x27<path d=\"M9 17h.01\"/>\x27 +
  1029. \x27</svg>\x27;
  1030. var logoHtml = item.logo
  1031. ? \x27<img src=\"\x27 + escapeHtml(item.logo) + \x27\" alt=\"\" onerror=\"this.parentNode.innerHTML=\\\x27\x27 + fallbackLogo.replace(/\x27/g, \"\\\\\x27\") + \x27\\\x27;\">\x27
  1032. : fallbackLogo;
  1033. var meta = [];
  1034. if(item.companyName) meta.push(escapeHtml(item.companyName));
  1035. if(item.city) meta.push(escapeHtml(item.city));
  1036. if(item.employmentType) meta.push(escapeHtml(item.employmentType));
  1037. var salaryHtml = (item.salaryMin && item.salaryMax)
  1038. ? \x27<span class=\"jobs-card-salary\">\x27 + item.salaryMin + \x27–\x27 + item.salaryMax + \x27 \x27 + escapeHtml(item.devise || \x27\x27) + \x27</span>\x27
  1039. : \x27\x27;
  1040. var url = item.url || JOB_SHOW_URL_TEMPLATE.replace(\x27__SLUG__\x27, encodeURIComponent(item.slug));
  1041. var a = document.createElement(\x27a\x27);
  1042. a.href = url;
  1043. a.className = \x27jobs-card\x27;
  1044. a.innerHTML =
  1045. \x27<div class=\"jobs-card-logo\">\x27 + logoHtml + \x27</div>\x27 +
  1046. \x27<div class=\"jobs-card-info\">\x27 +
  1047. \x27<div class=\"jobs-card-title\">\x27 + escapeHtml(item.title || \x27\x27) + \x27</div>\x27 +
  1048. \x27<div class=\"jobs-card-meta\">\x27 + meta.join(\x27 · \x27) + \x27</div>\x27 +
  1049. \x27</div>\x27 +
  1050. salaryHtml +
  1051. \x27<span class=\"jobs-card-arrow\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"9 18 15 12 9 6\"/></svg></span>\x27;
  1052. return a;
  1053. }
  1054. /**
  1055. * Clone la promo card depuis le <template> rendu par la macro Twig.
  1056. * Le HTML est défini une seule fois dans _macros/promo_card.html.twig.
  1057. * Retourne null si le template n\x27est pas présent (ex: user connecté).
  1058. */
  1059. function buildPromoCard(){
  1060. var tpl = document.getElementById(\x27jobs-promo-card-template\x27);
  1061. if(!tpl || !tpl.content) return null;
  1062. var node = tpl.content.firstElementChild;
  1063. return node ? node.cloneNode(true) : null;
  1064. }
  1065. function renderEmpty(){
  1066. listEl.innerHTML =
  1067. \x27<div class=\"jobs-empty\">\x27 +
  1068. \x27<div class=\"jobs-empty-icon\">\x27 +
  1069. \x27<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>\x27 +
  1070. \x27</div>\x27 +
  1071. \x27<div class=\"jobs-empty-title\">";
  1072. // line 543
  1073. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.emptyTitle", [], "whr-public"), "js"), "html", null, true);
  1074. yield "</div>\x27 +
  1075. \x27<div class=\"jobs-empty-text\">";
  1076. // line 544
  1077. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.emptyText", [], "whr-public"), "js"), "html", null, true);
  1078. yield "</div>\x27 +
  1079. \x27</div>\x27;
  1080. }
  1081. function setLoadMoreState(state){
  1082. if(!loadMoreBtn) return;
  1083. if(state === \x27loading\x27){
  1084. loadMoreBtn.classList.add(\x27loading\x27);
  1085. loadMoreBtn.disabled = true;
  1086. loadMoreLabel.innerHTML = \x27<span class=\"jobs-load-spinner\"></span> ";
  1087. // line 553
  1088. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.loadingMore", [], "whr-public"), "js"), "html", null, true);
  1089. yield "\x27;
  1090. } else {
  1091. loadMoreBtn.classList.remove(\x27loading\x27);
  1092. loadMoreBtn.disabled = false;
  1093. loadMoreLabel.textContent = \x27";
  1094. // line 557
  1095. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.loadMore", [], "whr-public"), "js"), "html", null, true);
  1096. yield "\x27;
  1097. }
  1098. }
  1099. function fetchJobs(query, offset, append){
  1100. if(isFetching) return;
  1101. isFetching = true;
  1102. if(!append){
  1103. // Nouvelle recherche : on remet à zéro et affiche skeleton
  1104. listEl.innerHTML =
  1105. \x27<div class=\"jobs-skeleton\">\x27 +
  1106. Array(5).fill(0).map(function(){
  1107. return \x27<div class=\"jobs-skel-card\"><div class=\"jobs-skel-logo\"></div><div class=\"jobs-skel-info\"><div class=\"jobs-skel-line s1\"></div><div class=\"jobs-skel-line s2\"></div></div></div>\x27;
  1108. }).join(\x27\x27) +
  1109. \x27</div>\x27;
  1110. if(infoEl) infoEl.textContent = \x27";
  1111. // line 573
  1112. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.loading", [], "whr-public"), "js"), "html", null, true);
  1113. yield "\x27;
  1114. if(loadMoreBtn) loadMoreBtn.style.display = \x27none\x27;
  1115. } else {
  1116. setLoadMoreState(\x27loading\x27);
  1117. }
  1118. var url = SEARCH_API_URL + \x27?q=\x27 + encodeURIComponent(query || \x27\x27) + \x27&locale=\x27 + LOCALE + \x27&limit=\x27 + PAGE_SIZE + \x27&offset=\x27 + offset;
  1119. if(CITY_KEYWORD){ url += \x27&city=\x27 + encodeURIComponent(CITY_KEYWORD); }
  1120. // Dashboard sans filtre : on demande explicitement les dernières offres
  1121. if(!query && !CITY_KEYWORD){ url += \x27&all=1\x27; }
  1122. fetch(url, {headers:{\x27Accept\x27:\x27application/json\x27}})
  1123. .then(function(r){ return r.ok ? r.json() : null; })
  1124. .then(function(data){
  1125. isFetching = false;
  1126. if(!data){
  1127. if(!append) renderEmpty();
  1128. setLoadMoreState(\x27idle\x27);
  1129. return;
  1130. }
  1131. var items = data.items || [];
  1132. currentTotal = data.total || 0;
  1133. if(!append){
  1134. listEl.innerHTML = \x27\x27;
  1135. if(items.length === 0){
  1136. renderEmpty();
  1137. if(infoEl) infoEl.innerHTML = \x27<strong>0</strong> ";
  1138. // line 600
  1139. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.resultsCount", [], "whr-public"), "js"), "html", null, true);
  1140. yield "\x27;
  1141. if(loadMoreBtn) loadMoreBtn.style.display = \x27none\x27;
  1142. return;
  1143. }
  1144. }
  1145. // Insérer la promo card après la 5ème offre (uniquement au 1er chargement,
  1146. // si user non connecté et qu\x27on a au moins 5 offres)
  1147. var insertPromoAt = (!append && !IS_AUTHENTICATED && items.length >= 5) ? 5 : -1;
  1148. items.forEach(function(item, idx){
  1149. listEl.appendChild(buildJobCard(item));
  1150. if(idx === insertPromoAt - 1){
  1151. var promoNode = buildPromoCard();
  1152. if(promoNode) listEl.appendChild(promoNode);
  1153. }
  1154. });
  1155. currentOffset = offset + items.length;
  1156. if(infoEl){
  1157. infoEl.innerHTML = \x27<strong>\x27 + currentTotal + \x27</strong> ";
  1158. // line 621
  1159. yield (string) $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\TranslationExtension']->trans("job.dashboard.resultsCount", [], "whr-public"), "js"), "html", null, true);
  1160. yield "\x27;
  1161. }
  1162. // Afficher / cacher \"Charger plus\"
  1163. if(loadMoreBtn){
  1164. if(currentOffset < currentTotal){
  1165. loadMoreBtn.style.display = \x27flex\x27;
  1166. } else {
  1167. loadMoreBtn.style.display = \x27none\x27;
  1168. }
  1169. setLoadMoreState(\x27idle\x27);
  1170. }
  1171. })
  1172. .catch(function(err){
  1173. isFetching = false;
  1174. console.error(\x27[whr-dash] fetch error\x27, err);
  1175. setLoadMoreState(\x27idle\x27);
  1176. });
  1177. }
  1178. /**
  1179. * Slugifie un texte côté client. DOIT produire EXACTEMENT le même slug
  1180. * que la méthode PHP slugify() du JobsController et que le filtre Twig
  1181. * utilisé pour rendre les chips.
  1182. */
  1183. function slugify(text){
  1184. if(!text) return \x27\x27;
  1185. var t = String(text);
  1186. // Pré-remplacements sémantiques
  1187. t = t.replace(/&/g, \x27-and-\x27).replace(/\\+/g, \x27-plus-\x27)
  1188. .replace(/[œŒ]/g, \x27oe\x27).replace(/[æÆ]/g, \x27ae\x27);
  1189. // Translitération accents → ASCII via NFD + strip diacritics
  1190. t = t.normalize ? t.normalize(\x27NFD\x27).replace(/[\\u0300-\\u036f]/g, \x27\x27) : t;
  1191. t = t.toLowerCase();
  1192. // Tout ce qui n\x27est pas a-z 0-9 → tiret
  1193. t = t.replace(/[^a-z0-9]+/g, \x27-\x27);
  1194. return t.replace(/^-+|-+\$/g, \x27\x27);
  1195. }
  1196. /**
  1197. * Tente de matcher un texte tapé avec un keyword en BDD (par slug ou label).
  1198. * Retourne l\x27objet keyword si trouvé, null sinon.
  1199. */
  1200. function findMatchingKeyword(text){
  1201. if(!text || !KEYWORDS_DATA || !KEYWORDS_DATA.length) return null;
  1202. var slug = slugify(text);
  1203. if(!slug) return null;
  1204. for(var i = 0; i < KEYWORDS_DATA.length; i++){
  1205. var kw = KEYWORDS_DATA[i];
  1206. // Match sur slug exact OU label/searchKeyword exact (case insensitive)
  1207. if(kw.slug === slug) return kw;
  1208. if(slugify(kw.searchKeyword) === slug) return kw;
  1209. }
  1210. return null;
  1211. }
  1212. /**
  1213. * Au submit : décide où rediriger l\x27utilisateur en fonction du contexte.
  1214. * - Si on est sur une ville et le texte matche un keyword BDD → /jobs/{ville}/{slug-kw}
  1215. * - Sinon si on est sur une ville → /jobs/{ville}?search={texte}
  1216. * - Sinon (pas de ville) → /jobs?q={texte}
  1217. * - Si texte vide ET ville définie → /jobs/{ville} (reset)
  1218. * - Si texte vide ET pas de ville → /jobs (reset)
  1219. */
  1220. function submitSearch(query){
  1221. query = (query || \x27\x27).trim();
  1222. // Reset (recherche vide)
  1223. if(!query){
  1224. if(CITY_SLUG && CITY_LANDING_URL_TEMPLATE){
  1225. window.location.href = CITY_LANDING_URL_TEMPLATE;
  1226. } else {
  1227. window.location.href = DASHBOARD_URL;
  1228. }
  1229. return;
  1230. }
  1231. // Cas 1 : on est sur une ville
  1232. if(CITY_SLUG && CITY_KEYWORD_URL_TEMPLATE){
  1233. var matched = findMatchingKeyword(query);
  1234. if(matched && matched.slug){
  1235. // Match BDD → URL slug propre /jobs/paris/developpeur
  1236. window.location.href = CITY_KEYWORD_URL_TEMPLATE.replace(\x27kwplaceholder\x27, encodeURIComponent(matched.slug));
  1237. return;
  1238. }
  1239. // Pas de match → fallback ?search=texte
  1240. window.location.href = CITY_LANDING_URL_TEMPLATE + \x27?search=\x27 + encodeURIComponent(query);
  1241. return;
  1242. }
  1243. // Cas 2 : pas de ville → dashboard global avec ?q=
  1244. window.location.href = DASHBOARD_URL + \x27?q=\x27 + encodeURIComponent(query);
  1245. }
  1246. /**
  1247. * Lance la search côté UI (sans redirection — utilisée pour les chips et
  1248. * pour la \"Charger plus\").
  1249. */
  1250. function startSearch(query){
  1251. currentQuery = query;
  1252. currentOffset = 0;
  1253. currentTotal = 0;
  1254. fetchJobs(query, 0, false);
  1255. // Update clear button
  1256. if(clearBtn){
  1257. if(query) clearBtn.classList.add(\x27visible\x27);
  1258. else clearBtn.classList.remove(\x27visible\x27);
  1259. }
  1260. // Update active chip (uniquement les chips qui sont des <button>, pas les <a>)
  1261. document.querySelectorAll(\x27button.search-keyword-chip\x27).forEach(function(c){
  1262. if(c.getAttribute(\x27data-keyword\x27) === query) c.classList.add(\x27active\x27);
  1263. else c.classList.remove(\x27active\x27);
  1264. });
  1265. }
  1266. // ═══ Submit-only : Entrée déclenche la search ET change l\x27URL ═══
  1267. if(input){
  1268. input.addEventListener(\x27keydown\x27, function(e){
  1269. if(e.key === \x27Enter\x27 || e.keyCode === 13){
  1270. e.preventDefault();
  1271. submitSearch(input.value);
  1272. }
  1273. });
  1274. // Affiche bouton clear si query initiale
  1275. if(input.value.trim()){
  1276. if(clearBtn) clearBtn.classList.add(\x27visible\x27);
  1277. }
  1278. }
  1279. // Bouton clear : reset complet → redirection vers /jobs ou /jobs/{ville}
  1280. if(clearBtn){
  1281. clearBtn.addEventListener(\x27click\x27, function(){
  1282. submitSearch(\x27\x27);
  1283. });
  1284. }
  1285. // Chips keywords (cas dashboard global, sans contexte ville → comportement local)
  1286. // En contexte ville, les chips sont des <a href> directs gérés par le navigateur.
  1287. document.querySelectorAll(\x27button.search-keyword-chip\x27).forEach(function(chip){
  1288. chip.addEventListener(\x27click\x27, function(){
  1289. var kw = chip.getAttribute(\x27data-keyword\x27);
  1290. if(!kw) return;
  1291. // Sur dashboard global : on submit (redirige vers /jobs?q=kw)
  1292. submitSearch(kw);
  1293. });
  1294. });
  1295. // Bouton \"Charger plus\"
  1296. if(loadMoreBtn){
  1297. loadMoreBtn.addEventListener(\x27click\x27, function(){
  1298. if(isFetching) return;
  1299. fetchJobs(currentQuery, currentOffset, true);
  1300. });
  1301. }
  1302. // ═══ Bouton \"Voir plus\" + Recherche live des chips ═══
  1303. /**
  1304. * Slugify simple côté UI pour matching tolérant aux accents
  1305. * (ex: \"developpeur\" matche \"Développeur\")
  1306. */
  1307. function dashSlug(str){
  1308. if(!str) return \x27\x27;
  1309. var t = String(str);
  1310. t = t.normalize ? t.normalize(\x27NFD\x27).replace(/[\\u0300-\\u036f]/g, \x27\x27) : t;
  1311. return t.toLowerCase().trim();
  1312. }
  1313. document.querySelectorAll(\x27.dash-filter-more-btn\x27).forEach(function(btn){
  1314. var targetId = btn.getAttribute(\x27data-target\x27);
  1315. var sectionId = btn.getAttribute(\x27data-section\x27);
  1316. var container = document.getElementById(targetId);
  1317. var section = sectionId ? document.getElementById(sectionId) : null;
  1318. if(!container) return;
  1319. var label = btn.querySelector(\x27.dash-filter-more-label\x27);
  1320. if(!label) return;
  1321. var initial = parseInt(container.getAttribute(\x27data-initial\x27), 10) || 14;
  1322. var items = container.querySelectorAll(\x27.dash-filter-item\x27);
  1323. var searchInput = section ? section.querySelector(\x27.dash-filter-search-input\x27) : null;
  1324. // 2 états indépendants :
  1325. // - userExpanded : l\x27utilisateur a cliqué sur \"Voir plus\"
  1326. // - searchActive : l\x27input contient du texte (auto-expand temporaire)
  1327. // Mode développé = userExpanded || searchActive
  1328. var userExpanded = false;
  1329. function isEffectivelyExpanded(){
  1330. var hasSearch = searchInput && searchInput.value.trim().length > 0;
  1331. return userExpanded || hasSearch;
  1332. }
  1333. function applyDisplay(){
  1334. var filterText = searchInput ? searchInput.value : \x27\x27;
  1335. var query = dashSlug(filterText || \x27\x27);
  1336. var expanded = isEffectivelyExpanded();
  1337. var visibleCount = 0;
  1338. items.forEach(function(it, i){
  1339. var label = it.getAttribute(\x27data-filter-label\x27) || \x27\x27;
  1340. var matches = !query || dashSlug(label).indexOf(query) !== -1;
  1341. if(expanded){
  1342. // Mode développé : on affiche tous les items qui matchent la recherche
  1343. it.style.display = matches ? \x27\x27 : \x27none\x27;
  1344. if(matches) visibleCount++;
  1345. } else {
  1346. // Mode collapsé : on affiche les N premiers (pas de filtrage)
  1347. it.style.display = (i < initial) ? \x27\x27 : \x27none\x27;
  1348. if(i < initial) visibleCount++;
  1349. }
  1350. });
  1351. // Toggle \"aucun résultat\" (uniquement quand recherche active)
  1352. if(query && visibleCount === 0){
  1353. container.classList.add(\x27is-no-results\x27);
  1354. } else {
  1355. container.classList.remove(\x27is-no-results\x27);
  1356. }
  1357. // Synchroniser le bouton \"Voir plus/moins\" avec l\x27état effectif
  1358. if(expanded){
  1359. btn.classList.add(\x27is-expanded\x27);
  1360. label.textContent = label.getAttribute(\x27data-less\x27);
  1361. } else {
  1362. btn.classList.remove(\x27is-expanded\x27);
  1363. label.textContent = label.getAttribute(\x27data-more\x27);
  1364. }
  1365. }
  1366. btn.addEventListener(\x27click\x27, function(){
  1367. userExpanded = !userExpanded;
  1368. if(!userExpanded){
  1369. // Reset input quand l\x27utilisateur collapse manuellement
  1370. if(searchInput){ searchInput.value = \x27\x27; }
  1371. // Scroll vers le bouton
  1372. btn.scrollIntoView({behavior:\x27smooth\x27, block:\x27nearest\x27});
  1373. }
  1374. applyDisplay();
  1375. });
  1376. // Recherche live dans les chips quand on tape dans l\x27input
  1377. // → auto-expand pendant la recherche, retour à l\x27état userExpanded quand on vide
  1378. if(searchInput){
  1379. searchInput.addEventListener(\x27input\x27, function(){
  1380. applyDisplay();
  1381. });
  1382. }
  1383. });
  1384. // Premier chargement (vide ou avec query initiale)
  1385. fetchJobs(currentQuery, 0, false);
  1386. } catch(err){
  1387. console.error(\x27[whr-dash] init error\x27, err);
  1388. }
  1389. })();
  1390. </script>
  1391. ";
  1392. $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  1393. $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  1394. yield from [];
  1395. }
  1396. /**
  1397. * @codeCoverageIgnore
  1398. */
  1399. public function getTemplateName(): string
  1400. {
  1401. return "application/whileresume/application/jobs/dashboard.html.twig";
  1402. }
  1403. /**
  1404. * @codeCoverageIgnore
  1405. */
  1406. public function isTraitable(): bool
  1407. {
  1408. return false;
  1409. }
  1410. /**
  1411. * @codeCoverageIgnore
  1412. */
  1413. public function getDebugInfo(): array
  1414. {
  1415. return array ( 1256 => 621, 1232 => 600, 1202 => 573, 1183 => 557, 1176 => 553, 1164 => 544, 1160 => 543, 1085 => 470, 1070 => 469, 1059 => 468, 1056 => 446, 1039 => 445, 1033 => 441, 1031 => 440, 1030 => 439, 1029 => 438, 1028 => 437, 1025 => 436, 1023 => 435, 1022 => 434, 1021 => 433, 1020 => 432, 1014 => 429, 1008 => 426, 1002 => 423, 993 => 417, 987 => 414, 983 => 413, 978 => 411, 974 => 410, 967 => 405, 954 => 404, 941 => 400, 937 => 398, 935 => 397, 934 => 395, 932 => 393, 929 => 392, 923 => 386, 919 => 384, 914 => 380, 900 => 372, 896 => 371, 892 => 369, 886 => 365, 883 => 364, 880 => 362, 876 => 360, 865 => 356, 858 => 351, 856 => 350, 850 => 348, 828 => 342, 822 => 341, 814 => 340, 810 => 339, 806 => 338, 800 => 337, 791 => 336, 774 => 335, 770 => 334, 767 => 333, 760 => 329, 751 => 322, 748 => 321, 743 => 318, 735 => 312, 732 => 311, 729 => 310, 726 => 308, 722 => 306, 711 => 302, 704 => 297, 702 => 296, 696 => 294, 674 => 288, 668 => 287, 660 => 286, 656 => 285, 652 => 284, 646 => 283, 637 => 282, 620 => 281, 616 => 280, 613 => 279, 606 => 275, 597 => 268, 594 => 267, 589 => 264, 581 => 258, 578 => 257, 575 => 256, 572 => 254, 568 => 252, 561 => 251, 554 => 248, 548 => 247, 544 => 246, 540 => 244, 538 => 243, 531 => 240, 525 => 239, 519 => 238, 510 => 237, 508 => 236, 506 => 235, 503 => 234, 500 => 211, 498 => 210, 494 => 209, 490 => 208, 487 => 207, 484 => 206, 474 => 198, 469 => 196, 461 => 195, 453 => 189, 449 => 186, 442 => 182, 437 => 180, 428 => 178, 422 => 176, 416 => 175, 411 => 173, 408 => 172, 405 => 171, 402 => 169, 397 => 167, 392 => 166, 389 => 165, 383 => 163, 377 => 161, 375 => 160, 366 => 158, 363 => 157, 361 => 156, 357 => 155, 344 => 153, 341 => 152, 339 => 151, 337 => 150, 332 => 147, 319 => 146, 305 => 143, 173 => 12, 160 => 11, 137 => 9, 104 => 8, 72 => 7, 61 => 1, 59 => 5, 46 => 1,);
  1416. }
  1417. public function getSourceContext(): Source
  1418. {
  1419. return new Source("{% extends \x27application/whileresume/application/jobs/layout-social.html.twig\x27 %}
  1420. {% trans_default_domain \x27whr-public\x27 %}
  1421. {% import \x27application/whileresume/_macros/promo_card.html.twig\x27 as promo %}
  1422. {% block title %}{% if currentSubKeyword is defined and currentSubKeyword is not null and currentCityFilter is defined and currentCityFilter is not null %}{{ currentSubKeyword }} {{ currentCityFilter.label }}{% elseif currentFilter is defined and currentFilter is not null %}{{ currentFilter.shortTitle|default(currentFilter.label) }} — WhileResume{% else %}{{ \x27job.dashboard.title\x27|trans }}{% endif %}{% endblock title %}
  1423. {% block description %}{% if currentSubKeyword is defined and currentSubKeyword is not null and currentCityFilter is defined and currentCityFilter is not null %}Découvrez les offres \"{{ currentSubKeyword }}\" à {{ currentCityFilter.label }}.{% elseif currentFilter is defined and currentFilter is not null and currentFilter.shortDescription is not empty %}{{ currentFilter.shortDescription }}{% else %}{{ \x27job.dashboard.description\x27|trans }}{% endif %}{% endblock description %}
  1424. {% block robots %}index,follow{% endblock robots %}
  1425. {% block css %}
  1426. <style>
  1427. /* ─── Page dashboard /jobs ─── */
  1428. .jobs-dash{max-width:880px;margin:0 auto}
  1429. .jobs-dash-header{margin-bottom:18px}
  1430. .jobs-dash-title{font-size:24px;font-weight:800;color:#1E1B2E;line-height:1.2;letter-spacing:-0.02em;margin:0 0 6px}
  1431. @media(min-width:768px){.jobs-dash-title{font-size:28px}}
  1432. .jobs-dash-subtitle{font-size:14px;color:#6B7280;margin:0 0 18px}
  1433. /* ─── Bandeau contexte ville (badge + bouton ALL) ─── */
  1434. .city-context-bar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:0 0 16px;padding:10px 14px;background:#F5F3FF;border:1px solid rgba(108,58,237,.15);border-radius:12px}
  1435. .city-context-label{font-size:11px;font-weight:600;color:#6B7280;text-transform:uppercase;letter-spacing:.06em}
  1436. .city-context-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:100px;background:#fff;color:var(--theme-color,#6C3AED);font-size:13px;font-weight:600;border:1px solid rgba(108,58,237,.2)}
  1437. .city-context-badge-icon{font-size:14px;line-height:1}
  1438. .city-context-clear-btn{margin-left:auto;display:inline-flex;align-items:center;gap:4px;padding:5px 12px;border-radius:100px;background:transparent;color:var(--theme-color,#6C3AED);font-size:12px;font-weight:600;border:1px dashed rgba(108,58,237,.4);cursor:pointer;text-decoration:none;font-family:inherit;transition:background .15s,border-style .15s,border-color .15s}
  1439. .city-context-clear-btn:hover{background:#fff;border-style:solid;border-color:var(--theme-color,#6C3AED);color:var(--theme-color,#6C3AED)}
  1440. .city-context-clear-btn svg{width:11px;height:11px}
  1441. /* Widget recherche */
  1442. .wr-search-widget{background:#fff;border-radius:14px;box-shadow:0 0 20px 0 rgba(0,0,0,0.05);padding:10px;margin-bottom:14px;position:relative;z-index:50}
  1443. .wr-search-input-wrap{display:flex;align-items:center;gap:10px;padding:8px 14px;border-radius:10px;background:#F9FAFB;border:1px solid #E5E7EB;transition:border-color .2s,background .2s}
  1444. .wr-search-input-wrap:focus-within{border-color:rgba(108,58,237,.3);background:#fff}
  1445. .wr-search-icon{color:#9CA3AF;display:flex;flex-shrink:0}
  1446. .wr-search-input{flex:1;border:none;outline:none;background:transparent;font-size:14px;color:#1E1B2E;padding:4px 0;font-family:inherit;min-width:0}
  1447. .wr-search-input::placeholder{color:#9CA3AF}
  1448. .wr-search-clear{background:none;border:none;color:#9CA3AF;cursor:pointer;padding:4px;display:none;font-family:inherit}
  1449. .wr-search-clear.visible{display:flex;align-items:center}
  1450. .wr-search-clear:hover{color:#6B7280}
  1451. .wr-search-clear svg{width:14px;height:14px}
  1452. .wr-search-kbd-hint{display:none;align-items:center;padding:3px 8px;background:#fff;border:1px solid #E5E7EB;border-radius:6px;font-size:10.5px;font-weight:600;color:#6B7280;font-family:inherit;letter-spacing:.02em;white-space:nowrap;flex-shrink:0}
  1453. .wr-search-input-wrap:focus-within .wr-search-kbd-hint{display:inline-flex}
  1454. /* Chips keywords */
  1455. .search-keywords{display:flex;flex-wrap:wrap;gap:6px;margin:0 4px 18px}
  1456. .search-keyword-chip{display:inline-flex;align-items:center;gap:5px;padding:6px 12px;border-radius:100px;background:#F5F3FF;color:var(--theme-color,#6C3AED);font-size:12.5px;font-weight:500;cursor:pointer;border:1px solid transparent;transition:background .15s,border-color .15s,transform .15s;font-family:inherit}
  1457. .search-keyword-chip:hover{background:#EDE9FE;border-color:rgba(108,58,237,.2);transform:translateY(-1px);color:var(--theme-color,#6C3AED)}
  1458. .search-keyword-chip.active{background:var(--theme-color,#6C3AED);color:#fff;border-color:var(--theme-color,#6C3AED)}
  1459. .search-keyword-chip-icon{font-size:13px;line-height:1}
  1460. .search-keywords-label{font-size:11px;font-weight:600;color:#9CA3AF;text-transform:uppercase;letter-spacing:.06em;margin-right:4px;align-self:center}
  1461. /* ─── Sections de filtres SEO (chips → liens directs vers /jobs/{slug}) ─── */
  1462. .filter-section{margin:0 0 14px}
  1463. .filter-section-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;padding:0 4px;flex-wrap:wrap}
  1464. @media(max-width:540px){
  1465. .dash-filter-search{max-width:100%;width:100%}
  1466. }
  1467. .filter-section-title{display:flex;align-items:center;gap:7px;font-size:11.5px;font-weight:700;color:#6B7280;text-transform:uppercase;letter-spacing:.06em;margin:0}
  1468. .filter-section-title-icon{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:6px;background:#F5F3FF;color:var(--theme-color,#6C3AED)}
  1469. .filter-section-title-icon svg{width:11px;height:11px}
  1470. .filter-section-toggle{background:none;border:none;font-size:11px;color:var(--theme-color,#6C3AED);font-weight:600;cursor:pointer;font-family:inherit;padding:4px 8px;border-radius:6px;transition:background .15s}
  1471. .filter-section-toggle:hover{background:#F5F3FF}
  1472. .filter-chips{display:flex;flex-wrap:wrap;gap:6px;padding:0 4px}
  1473. .filter-chip{display:inline-flex;align-items:center;gap:5px;padding:7px 13px;border-radius:100px;background:#fff;color:#374151;font-size:12.5px;font-weight:500;cursor:pointer;border:1px solid #E5E7EB;text-decoration:none;transition:background .15s,border-color .15s,transform .15s,color .15s;font-family:inherit;white-space:nowrap}
  1474. .filter-chip:hover{background:#F5F3FF;border-color:rgba(108,58,237,.3);color:var(--theme-color,#6C3AED);transform:translateY(-1px)}
  1475. .filter-chip.active{background:var(--theme-color,#6C3AED);border-color:var(--theme-color,#6C3AED);color:#fff}
  1476. .filter-chip.active:hover{background:#5B21B6;color:#fff}
  1477. .filter-chip-icon{font-size:14px;line-height:1}
  1478. .filter-chip-arrow{display:inline-flex;color:currentColor;opacity:.5;margin-left:2px}
  1479. .filter-chip-arrow svg{width:10px;height:10px}
  1480. /* Mode \"compact\" : sections initialement collapsées si > 6 items */
  1481. .filter-chips.collapsed{max-height:80px;overflow:hidden;position:relative}
  1482. .filter-chips.collapsed::after{content:\"\";position:absolute;left:0;right:0;bottom:0;height:30px;background:linear-gradient(to bottom,transparent,#FAFAFA);pointer-events:none}
  1483. /* ─── Bouton \"Voir plus\" sous les chips villes/métiers ─── */
  1484. .dash-filter-more-wrap{display:flex;justify-content:center;margin-top:10px;padding:0 4px}
  1485. .dash-filter-more-btn{display:inline-flex;align-items:center;gap:6px;padding:7px 16px;background:transparent;color:var(--theme-color,#6C3AED);border:1px dashed rgba(108,58,237,.35);border-radius:100px;font-size:12px;font-weight:600;font-family:inherit;cursor:pointer;transition:background .15s,border-style .15s,border-color .15s}
  1486. .dash-filter-more-btn:hover{background:#F5F3FF;border-style:solid;border-color:var(--theme-color,#6C3AED)}
  1487. .dash-filter-more-icon{display:inline-flex;align-items:center;color:currentColor;transition:transform .2s}
  1488. .dash-filter-more-icon svg{width:12px;height:12px}
  1489. .dash-filter-more-btn.is-expanded .dash-filter-more-icon{transform:rotate(180deg)}
  1490. /* ─── Input de recherche dans le header (toujours visible) ─── */
  1491. .dash-filter-search{display:block;position:relative;flex:1;max-width:240px}
  1492. @keyframes dashFilterFadeIn{from{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:translateY(0)}}
  1493. .dash-filter-search-input{width:100%;padding:5px 10px 5px 28px;border:1px solid #E5E7EB;border-radius:100px;font-size:12px;color:#1E1B2E;background:#FAFAFA;font-family:inherit;outline:none;box-sizing:border-box;transition:border-color .15s,background .15s}
  1494. .dash-filter-search-input:focus{border-color:rgba(108,58,237,.4);background:#fff}
  1495. .dash-filter-search-input::placeholder{color:#9CA3AF}
  1496. .dash-filter-search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:#9CA3AF;pointer-events:none;display:flex}
  1497. .dash-filter-search-icon svg{width:11px;height:11px}
  1498. /* État \"aucun résultat\" sous le filtre live */
  1499. .dash-filter-empty{display:none;padding:14px 12px;font-size:12.5px;color:#9CA3AF;font-style:italic;text-align:center;width:100%}
  1500. .filter-chips.is-no-results .dash-filter-empty{display:block}
  1501. /* Bandeau résultats */
  1502. .jobs-results-info{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 4px 14px;font-size:12px;color:#6B7280}
  1503. .jobs-results-info strong{color:#1E1B2E;font-weight:700}
  1504. /* Liste cards (style \"similar-card\" comme dans show.html.twig) */
  1505. .jobs-list{display:flex;flex-direction:column;gap:10px}
  1506. .jobs-card{background:#fff;border-radius:14px;padding:14px;box-shadow:0 0 16px 0 rgba(0,0,0,0.04);display:flex;align-items:center;gap:14px;text-decoration:none;color:inherit;transition:transform .15s,box-shadow .2s}
  1507. .jobs-card:hover{transform:translateY(-1px);box-shadow:0 4px 20px rgba(108,58,237,.1);color:inherit}
  1508. .jobs-card-logo{width:50px;height:50px;border-radius:12px;background:linear-gradient(135deg,#EDE9FE,#DDD6FE);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:20px;color:var(--theme-color,#6C3AED);flex-shrink:0;overflow:hidden}
  1509. .jobs-card-logo img{width:100%;height:100%;object-fit:cover;border-radius:8px}
  1510. .jobs-card-logo svg{width:24px;height:24px;opacity:.85}
  1511. .jobs-card-info{flex:1;min-width:0}
  1512. .jobs-card-title{font-size:14px;font-weight:700;color:#1E1B2E;line-height:1.3;margin:0 0 4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  1513. .jobs-card-meta{font-size:12px;color:#6B7280;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  1514. .jobs-card-salary{font-size:11px;font-weight:600;color:var(--theme-color,#6C3AED);background:#F5F3FF;padding:3px 9px;border-radius:100px;white-space:nowrap;flex-shrink:0}
  1515. .jobs-card-arrow{flex-shrink:0;color:#9CA3AF;transition:color .15s,transform .15s}
  1516. .jobs-card:hover .jobs-card-arrow{color:var(--theme-color,#6C3AED);transform:translateX(2px)}
  1517. .jobs-card-arrow svg{width:18px;height:18px}
  1518. /* Skeleton (pendant chargement) */
  1519. .jobs-skeleton{display:flex;flex-direction:column;gap:10px}
  1520. .jobs-skel-card{background:#fff;border-radius:14px;padding:14px;display:flex;align-items:center;gap:14px;box-shadow:0 0 16px 0 rgba(0,0,0,0.04)}
  1521. .jobs-skel-logo{width:50px;height:50px;border-radius:12px;background:linear-gradient(90deg,#F3F4F6 0%,#E5E7EB 50%,#F3F4F6 100%);background-size:200% 100%;animation:jobsSkelShim 1.4s ease-in-out infinite;flex-shrink:0}
  1522. .jobs-skel-info{flex:1}
  1523. .jobs-skel-line{height:14px;background:linear-gradient(90deg,#F3F4F6 0%,#E5E7EB 50%,#F3F4F6 100%);background-size:200% 100%;animation:jobsSkelShim 1.4s ease-in-out infinite;border-radius:4px;margin-bottom:6px}
  1524. .jobs-skel-line.s1{width:60%}
  1525. .jobs-skel-line.s2{width:40%;height:12px;margin-bottom:0}
  1526. @keyframes jobsSkelShim{0%{background-position:200% 0}100%{background-position:-200% 0}}
  1527. /* Bouton charger plus */
  1528. .jobs-load-more{display:flex;width:100%;padding:14px;margin-top:16px;background:#fff;color:var(--theme-color,#6C3AED);border:1.5px dashed rgba(108,58,237,.35);border-radius:12px;font-size:13.5px;font-weight:700;cursor:pointer;font-family:inherit;align-items:center;justify-content:center;gap:8px;transition:background .15s,border-color .15s}
  1529. .jobs-load-more:hover{background:#F5F3FF;border-color:var(--theme-color,#6C3AED);border-style:solid;color:var(--theme-color,#6C3AED)}
  1530. .jobs-load-more svg{width:14px;height:14px}
  1531. .jobs-load-more:disabled{opacity:.6;cursor:not-allowed}
  1532. .jobs-load-more.loading{cursor:wait}
  1533. .jobs-load-spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(108,58,237,.2);border-top-color:var(--theme-color,#6C3AED);border-radius:50%;animation:jobsLoadSpin .7s linear infinite}
  1534. @keyframes jobsLoadSpin{to{transform:rotate(360deg)}}
  1535. /* État vide */
  1536. .jobs-empty{text-align:center;padding:40px 20px;background:#fff;border-radius:14px;box-shadow:0 0 16px 0 rgba(0,0,0,0.04)}
  1537. .jobs-empty-icon{display:inline-flex;align-items:center;justify-content:center;width:56px;height:56px;border-radius:16px;background:#F5F3FF;color:var(--theme-color,#6C3AED);margin-bottom:14px}
  1538. .jobs-empty-icon svg{width:24px;height:24px}
  1539. .jobs-empty-title{font-size:16px;font-weight:700;color:#1E1B2E;margin:0 0 6px}
  1540. .jobs-empty-text{font-size:13px;color:#6B7280;margin:0}
  1541. </style>
  1542. {# Styles partagés de la promo card (définis dans la macro) #}
  1543. {{ promo.styles() }}
  1544. {% endblock css %}
  1545. {% block body %}
  1546. <div class=\"jobs-dash\">
  1547. <div class=\"jobs-dash-header\">
  1548. {% if currentSubKeyword is defined and currentSubKeyword is not null and currentCityFilter is defined and currentCityFilter is not null %}
  1549. {# Cas combiné : ville + keyword #}
  1550. <h1 class=\"jobs-dash-title\">
  1551. {% if currentCityFilter.icon is not empty %}{{ currentCityFilter.icon }} {% endif %}{{ currentSubKeyword }} {{ \x27job.dashboard.cityHeadingIn\x27|trans }} {{ currentCityFilter.label }}
  1552. </h1>
  1553. <p class=\"jobs-dash-subtitle\">{{ \x27job.dashboard.cityKeywordSubtitle\x27|trans({\x27%keyword%\x27: currentSubKeyword, \x27%city%\x27: currentCityFilter.label}) }}</p>
  1554. {% elseif currentFilter is defined and currentFilter is not null %}
  1555. <h1 class=\"jobs-dash-title\">
  1556. {% if currentFilter.icon is not empty %}{{ currentFilter.icon }} {% endif %}{{ currentFilter.shortTitle|default(currentFilter.label) }}
  1557. </h1>
  1558. {% if currentFilter.shortDescription is not empty %}
  1559. <p class=\"jobs-dash-subtitle\">{{ currentFilter.shortDescription }}</p>
  1560. {% else %}
  1561. <p class=\"jobs-dash-subtitle\">{{ \x27job.dashboard.subtitle\x27|trans }}</p>
  1562. {% endif %}
  1563. {% else %}
  1564. <h1 class=\"jobs-dash-title\">{{ \x27job.dashboard.heading\x27|trans }}</h1>
  1565. <p class=\"jobs-dash-subtitle\">{{ \x27job.dashboard.subtitle\x27|trans }}</p>
  1566. {% endif %}
  1567. {# Badge \"Ville active\" + bouton \"Tout voir\" / ALL pour reset #}
  1568. {% if currentCityFilter is defined and currentCityFilter is not null %}
  1569. <div class=\"city-context-bar\">
  1570. <span class=\"city-context-label\">{{ \x27job.dashboard.contextLabel\x27|trans }}</span>
  1571. <span class=\"city-context-badge\">
  1572. {% if currentCityFilter.icon is not empty %}<span class=\"city-context-badge-icon\">{{ currentCityFilter.icon }}</span>{% endif %}
  1573. <span>{{ currentCityFilter.label }}</span>
  1574. </span>
  1575. <a href=\"{% if app.request.locale == \x27en\x27 %}{{ path(\x27whileresume_jobs_list\x27) }}{% else %}{{ path(\x27locale_whileresume_jobs_list\x27,{\x27_locale\x27:app.request.locale}) }}{% endif %}\"
  1576. class=\"city-context-clear-btn\"
  1577. title=\"{{ \x27job.dashboard.clearCity\x27|trans }}\">
  1578. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"9 18 15 12 9 6\" transform=\"rotate(180 12 12)\"/></svg>
  1579. {{ \x27job.dashboard.allCities\x27|trans }}
  1580. </a>
  1581. </div>
  1582. {% endif %}
  1583. </div>
  1584. {# ═══ Widget recherche ═══ #}
  1585. <div class=\"wr-search-widget\">
  1586. <div class=\"wr-search-input-wrap\">
  1587. <span class=\"wr-search-icon\">
  1588. <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>
  1589. </span>
  1590. <input type=\"text\" id=\"dashSearchInput\" class=\"wr-search-input\"
  1591. placeholder=\"{% if currentCityFilter is defined and currentCityFilter is not null %}{{ \x27job.dashboard.searchInCity\x27|trans({\x27%city%\x27: currentCityFilter.label}) }}{% else %}{{ \x27job.show.searchPlaceholder\x27|trans }}{% endif %}\"
  1592. value=\"{{ initialQuery|default(\x27\x27) }}\"
  1593. autocomplete=\"off\">
  1594. <span class=\"wr-search-kbd-hint\" id=\"dashSearchKbdHint\">{{ \x27job.dashboard.pressEnter\x27|trans }}</span>
  1595. <button type=\"button\" id=\"dashSearchClear\" class=\"wr-search-clear\" aria-label=\"Clear\">
  1596. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>
  1597. </button>
  1598. </div>
  1599. </div>
  1600. {# Chips keywords cliquables #}
  1601. {% if searchKeywords is defined and searchKeywords|length > 0 %}
  1602. <div class=\"search-keywords\">
  1603. <span class=\"search-keywords-label\">{{ \x27job.show.keywordsLabel\x27|trans }}</span>
  1604. {% for kw in searchKeywords %}
  1605. {# Slug pour l\x27URL : translitération des accents FR + lowercase + remplacement caractères spéciaux #}
  1606. {% set kwSlug = kw.label
  1607. |replace({
  1608. \x27à\x27:\x27a\x27,\x27â\x27:\x27a\x27,\x27ä\x27:\x27a\x27,\x27á\x27:\x27a\x27,\x27ã\x27:\x27a\x27,\x27å\x27:\x27a\x27,
  1609. \x27À\x27:\x27a\x27,\x27Â\x27:\x27a\x27,\x27Ä\x27:\x27a\x27,\x27Á\x27:\x27a\x27,\x27Ã\x27:\x27a\x27,\x27Å\x27:\x27a\x27,
  1610. \x27é\x27:\x27e\x27,\x27è\x27:\x27e\x27,\x27ê\x27:\x27e\x27,\x27ë\x27:\x27e\x27,\x27ẽ\x27:\x27e\x27,
  1611. \x27É\x27:\x27e\x27,\x27È\x27:\x27e\x27,\x27Ê\x27:\x27e\x27,\x27Ë\x27:\x27e\x27,
  1612. \x27î\x27:\x27i\x27,\x27ï\x27:\x27i\x27,\x27ì\x27:\x27i\x27,\x27í\x27:\x27i\x27,
  1613. \x27Î\x27:\x27i\x27,\x27Ï\x27:\x27i\x27,\x27Ì\x27:\x27i\x27,\x27Í\x27:\x27i\x27,
  1614. \x27ô\x27:\x27o\x27,\x27ö\x27:\x27o\x27,\x27ò\x27:\x27o\x27,\x27ó\x27:\x27o\x27,\x27õ\x27:\x27o\x27,\x27ø\x27:\x27o\x27,
  1615. \x27Ô\x27:\x27o\x27,\x27Ö\x27:\x27o\x27,\x27Ò\x27:\x27o\x27,\x27Ó\x27:\x27o\x27,\x27Õ\x27:\x27o\x27,\x27Ø\x27:\x27o\x27,
  1616. \x27û\x27:\x27u\x27,\x27ü\x27:\x27u\x27,\x27ù\x27:\x27u\x27,\x27ú\x27:\x27u\x27,
  1617. \x27Û\x27:\x27u\x27,\x27Ü\x27:\x27u\x27,\x27Ù\x27:\x27u\x27,\x27Ú\x27:\x27u\x27,
  1618. \x27ÿ\x27:\x27y\x27,\x27ý\x27:\x27y\x27,\x27Ÿ\x27:\x27y\x27,\x27Ý\x27:\x27y\x27,
  1619. \x27ç\x27:\x27c\x27,\x27Ç\x27:\x27c\x27,
  1620. \x27ñ\x27:\x27n\x27,\x27Ñ\x27:\x27n\x27,
  1621. \x27œ\x27:\x27oe\x27,\x27Œ\x27:\x27oe\x27,\x27æ\x27:\x27ae\x27,\x27Æ\x27:\x27ae\x27,
  1622. \"\x27\":\x27-\x27,\x27 \x27:\x27-\x27,\x27/\x27:\x27-\x27,\x27_\x27:\x27-\x27,\x27.\x27:\x27-\x27,\x27,\x27:\x27-\x27,
  1623. \x27(\x27:\x27\x27,\x27*\x27:\x27\x27,\x27)\x27:\x27\x27,\x27&\x27:\x27-and-\x27,\x27+\x27:\x27-plus-\x27,
  1624. \x27#\x27:\x27\x27,\x27?\x27:\x27\x27,\x27!\x27:\x27\x27,\x27\"\x27:\x27\x27,\x27’\x27:\x27-\x27,\x27`\x27:\x27-\x27
  1625. })
  1626. |lower
  1627. |replace({\x27--\x27:\x27-\x27,\x27---\x27:\x27-\x27})
  1628. |trim(\x27-\x27) %}
  1629. {% if currentCityFilter is defined and currentCityFilter is not null %}
  1630. {# Contexte ville : chip = lien vers /jobs/ville/keyword #}
  1631. <a href=\"{% if app.request.locale == \x27en\x27 %}{{ path(\x27cvs_application_jobs_filter_keyword\x27,{\x27slug\x27:currentCityFilter.slug,\x27keyword\x27:kwSlug}) }}{% else %}{{ path(\x27locale_cvs_application_jobs_filter_keyword\x27,{\x27_locale\x27:app.request.locale,\x27slug\x27:currentCityFilter.slug,\x27keyword\x27:kwSlug}) }}{% endif %}\"
  1632. class=\"search-keyword-chip{% if currentSubKeywordSlug is defined and currentSubKeywordSlug == kwSlug %} active{% endif %}\">
  1633. {% if kw.icon is not empty %}<span class=\"search-keyword-chip-icon\">{{ kw.icon }}</span>{% endif %}
  1634. <span>{{ kw.label }}</span>
  1635. </a>
  1636. {% else %}
  1637. {# Pas de contexte ville : chip pré-remplit la search (comportement existant) #}
  1638. <button type=\"button\"
  1639. class=\"search-keyword-chip\"
  1640. data-keyword=\"{{ kw.searchKeyword|default(kw.label) }}\">
  1641. {% if kw.icon is not empty %}<span class=\"search-keyword-chip-icon\">{{ kw.icon }}</span>{% endif %}
  1642. <span>{{ kw.label }}</span>
  1643. </button>
  1644. {% endif %}
  1645. {% endfor %}
  1646. </div>
  1647. {% endif %}
  1648. {# ═══ Filtres SEO : Villes (chips → /jobs/{slug}) — 14 visibles + reste cliqué pour révéler ═══ #}
  1649. {% if sidebarFiltersCities is defined and sidebarFiltersCities|length > 0 %}
  1650. {% set citiesInitial = 14 %}
  1651. <div class=\"filter-section\" id=\"filterSectionCities\">
  1652. <div class=\"filter-section-head\">
  1653. <h3 class=\"filter-section-title\">
  1654. <span class=\"filter-section-title-icon\">
  1655. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z\"/><circle cx=\"12\" cy=\"10\" r=\"3\"/></svg>
  1656. </span>
  1657. {{ \x27job.dashboard.popularCities\x27|trans }}
  1658. </h3>
  1659. {# Input de recherche (visible uniquement quand expanded) #}
  1660. {% if sidebarFiltersCities|length > citiesInitial %}
  1661. <div class=\"dash-filter-search\">
  1662. <span class=\"dash-filter-search-icon\">
  1663. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>
  1664. </span>
  1665. <input type=\"text\"
  1666. class=\"dash-filter-search-input\"
  1667. data-search-target=\"filterChipsCities\"
  1668. placeholder=\"{{ \x27job.dashboard.filterSearchCity\x27|trans }}\"
  1669. autocomplete=\"off\">
  1670. </div>
  1671. {% endif %}
  1672. </div>
  1673. <div class=\"filter-chips dash-filter-chips\" id=\"filterChipsCities\" data-initial=\"{{ citiesInitial }}\">
  1674. {% for f in sidebarFiltersCities %}
  1675. <a href=\"{% if app.request.locale == \x27en\x27 %}{{ path(\x27cvs_application_jobs_filter\x27,{\x27slug\x27:f.slug}) }}{% else %}{{ path(\x27locale_cvs_application_jobs_filter\x27,{\x27_locale\x27:app.request.locale,\x27slug\x27:f.slug}) }}{% endif %}\"
  1676. class=\"filter-chip dash-filter-item{% if currentFilter is defined and currentFilter is not null and currentFilter.slug == f.slug %} active{% endif %}\"
  1677. data-filter-slug=\"{{ f.slug }}\"
  1678. data-filter-label=\"{{ f.label }}\"
  1679. data-index=\"{{ loop.index0 }}\"{% if loop.index0 >= citiesInitial %} style=\"display:none\"{% endif %}>
  1680. {% if f.icon is not empty %}<span class=\"filter-chip-icon\">{{ f.icon }}</span>{% endif %}
  1681. <span>{{ f.label }}</span>
  1682. <span class=\"filter-chip-arrow\">
  1683. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"9 18 15 12 9 6\"/></svg>
  1684. </span>
  1685. </a>
  1686. {% endfor %}
  1687. <div class=\"dash-filter-empty\">{{ \x27job.dashboard.filterNoResults\x27|trans }}</div>
  1688. </div>
  1689. {% if sidebarFiltersCities|length > citiesInitial %}
  1690. <div class=\"dash-filter-more-wrap\">
  1691. <button type=\"button\" class=\"dash-filter-more-btn\" data-target=\"filterChipsCities\" data-section=\"filterSectionCities\">
  1692. <span class=\"dash-filter-more-icon\">
  1693. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"6 9 12 15 18 9\"/></svg>
  1694. </span>
  1695. <span class=\"dash-filter-more-label\" data-more=\"{{ \x27job.dashboard.showMoreCities\x27|trans({\x27%n%\x27: sidebarFiltersCities|length - citiesInitial}) }}\" data-less=\"{{ \x27job.dashboard.showLessCities\x27|trans }}\">{{ \x27job.dashboard.showMoreCities\x27|trans({\x27%n%\x27: sidebarFiltersCities|length - citiesInitial}) }}</span>
  1696. </button>
  1697. </div>
  1698. {% endif %}
  1699. </div>
  1700. {% endif %}
  1701. {# ═══ Filtres SEO : Métiers (chips → /jobs/{slug}) — 8 visibles + reste cliqué pour révéler ═══ #}
  1702. {% if sidebarFiltersCategories is defined and sidebarFiltersCategories|length > 0 %}
  1703. {% set categoriesInitial = 8 %}
  1704. <div class=\"filter-section\" id=\"filterSectionCategories\">
  1705. <div class=\"filter-section-head\">
  1706. <h3 class=\"filter-section-title\">
  1707. <span class=\"filter-section-title-icon\">
  1708. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"2\" y=\"7\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M16 7V5a4 4 0 00-8 0v2\"/></svg>
  1709. </span>
  1710. {{ \x27job.dashboard.popularCategories\x27|trans }}
  1711. </h3>
  1712. {# Input de recherche (visible uniquement quand expanded) #}
  1713. {% if sidebarFiltersCategories|length > categoriesInitial %}
  1714. <div class=\"dash-filter-search\">
  1715. <span class=\"dash-filter-search-icon\">
  1716. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>
  1717. </span>
  1718. <input type=\"text\"
  1719. class=\"dash-filter-search-input\"
  1720. data-search-target=\"filterChipsCategories\"
  1721. placeholder=\"{{ \x27job.dashboard.filterSearchCategory\x27|trans }}\"
  1722. autocomplete=\"off\">
  1723. </div>
  1724. {% endif %}
  1725. </div>
  1726. <div class=\"filter-chips dash-filter-chips\" id=\"filterChipsCategories\" data-initial=\"{{ categoriesInitial }}\">
  1727. {% for f in sidebarFiltersCategories %}
  1728. <a href=\"{% if app.request.locale == \x27en\x27 %}{{ path(\x27cvs_application_jobs_filter\x27,{\x27slug\x27:f.slug}) }}{% else %}{{ path(\x27locale_cvs_application_jobs_filter\x27,{\x27_locale\x27:app.request.locale,\x27slug\x27:f.slug}) }}{% endif %}\"
  1729. class=\"filter-chip dash-filter-item{% if currentFilter is defined and currentFilter is not null and currentFilter.slug == f.slug %} active{% endif %}\"
  1730. data-filter-slug=\"{{ f.slug }}\"
  1731. data-filter-label=\"{{ f.label }}\"
  1732. data-index=\"{{ loop.index0 }}\"{% if loop.index0 >= categoriesInitial %} style=\"display:none\"{% endif %}>
  1733. {% if f.icon is not empty %}<span class=\"filter-chip-icon\">{{ f.icon }}</span>{% endif %}
  1734. <span>{{ f.label }}</span>
  1735. <span class=\"filter-chip-arrow\">
  1736. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"9 18 15 12 9 6\"/></svg>
  1737. </span>
  1738. </a>
  1739. {% endfor %}
  1740. <div class=\"dash-filter-empty\">{{ \x27job.dashboard.filterNoResults\x27|trans }}</div>
  1741. </div>
  1742. {% if sidebarFiltersCategories|length > categoriesInitial %}
  1743. <div class=\"dash-filter-more-wrap\">
  1744. <button type=\"button\" class=\"dash-filter-more-btn\" data-target=\"filterChipsCategories\" data-section=\"filterSectionCategories\">
  1745. <span class=\"dash-filter-more-icon\">
  1746. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"6 9 12 15 18 9\"/></svg>
  1747. </span>
  1748. <span class=\"dash-filter-more-label\" data-more=\"{{ \x27job.dashboard.showMoreCategories\x27|trans({\x27%n%\x27: sidebarFiltersCategories|length - categoriesInitial}) }}\" data-less=\"{{ \x27job.dashboard.showLessCategories\x27|trans }}\">{{ \x27job.dashboard.showMoreCategories\x27|trans({\x27%n%\x27: sidebarFiltersCategories|length - categoriesInitial}) }}</span>
  1749. </button>
  1750. </div>
  1751. {% endif %}
  1752. </div>
  1753. {% endif %}
  1754. {# Bandeau d\x27info (compteur de résultats) #}
  1755. <div class=\"jobs-results-info\" id=\"jobsResultsInfo\">
  1756. <span id=\"jobsResultsCount\">{{ \x27job.dashboard.loading\x27|trans }}</span>
  1757. </div>
  1758. {# Liste des offres (peuplée en JS) #}
  1759. <div class=\"jobs-list\" id=\"jobsList\">
  1760. <div class=\"jobs-skeleton\">
  1761. {% for i in 1..5 %}
  1762. <div class=\"jobs-skel-card\">
  1763. <div class=\"jobs-skel-logo\"></div>
  1764. <div class=\"jobs-skel-info\">
  1765. <div class=\"jobs-skel-line s1\"></div>
  1766. <div class=\"jobs-skel-line s2\"></div>
  1767. </div>
  1768. </div>
  1769. {% endfor %}
  1770. </div>
  1771. </div>
  1772. {# Bouton \"Charger plus\" #}
  1773. <button type=\"button\" class=\"jobs-load-more\" id=\"jobsLoadMore\" style=\"display:none;\">
  1774. <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"6 9 12 15 18 9\"/></svg>
  1775. <span id=\"jobsLoadMoreLabel\">{{ \x27job.dashboard.loadMore\x27|trans }}</span>
  1776. </button>
  1777. {# Template HTML invisible cloné par le JS pour intercaler la promo card.
  1778. Affiché uniquement aux utilisateurs non connectés via la logique JS.
  1779. Les textes/bullets viennent automatiquement des clés job.show.promo* du YAML. #}
  1780. {% if app.user is null %}
  1781. {{ promo.card_template({
  1782. template_id: \x27jobs-promo-card-template\x27,
  1783. login_url: app.request.locale == \x27en\x27
  1784. ? path(\x27cvs_gestion_candidates_dashboard\x27)
  1785. : path(\x27locale_cvs_gestion_candidates_dashboard\x27,{\x27_locale\x27:app.request.locale})
  1786. }) }}
  1787. {% endif %}
  1788. </div>
  1789. {% endblock body %}
  1790. {% block footerjs %}
  1791. <script>
  1792. (function(){
  1793. \x27use strict\x27;
  1794. try {
  1795. var LOCALE = \x27{{ app.request.locale }}\x27;
  1796. var IS_AUTHENTICATED = {{ app.user ? \x27true\x27 : \x27false\x27 }};
  1797. var SEARCH_API_URL = \x27{{ app.request.locale == \"en\" ? path(\"cvs_application_jobs_api_search\") : path(\"locale_cvs_application_jobs_api_search\",{\"_locale\":app.request.locale}) }}\x27;
  1798. var JOB_SHOW_URL_TEMPLATE = \x27{{ app.request.locale == \"en\" ? path(\"cvs_application_jobs_api_search\",{\"slug\":\"__SLUG__\"}) : path(\"locale_cvs_application_job_show\",{\"_locale\":app.request.locale,\"slug\":\"__SLUG__\"}) }}\x27;
  1799. var PAGE_SIZE = 10;
  1800. var currentQuery = {{ initialQuery|default(\x27\x27)|json_encode|raw }};
  1801. var currentOffset = 0;
  1802. var currentTotal = 0;
  1803. var isFetching = false;
  1804. // Contexte ville (vient du serveur si on est sur /jobs/paris/... sinon vide)
  1805. var CITY_KEYWORD = {{ currentCityKeyword|default(\x27\x27)|json_encode|raw }};
  1806. // Slug ville pour générer les URLs de redirection au submit
  1807. var CITY_SLUG = {{ currentCityFilter is defined and currentCityFilter is not null ? currentCityFilter.slug|json_encode|raw : \x27null\x27 }};
  1808. // URL pour le dashboard global (sans ville)
  1809. var DASHBOARD_URL = \x27{{ app.request.locale == \"en\" ? path(\"whileresume_jobs_list\") : path(\"locale_whileresume_jobs_list\",{\"_locale\":app.request.locale}) }}\x27;
  1810. // Templates URL pour /jobs/{citySlug} et /jobs/{citySlug}/{keyword}
  1811. var CITY_LANDING_URL_TEMPLATE = {{ currentCityFilter is defined and currentCityFilter is not null
  1812. ? (app.request.locale == \x27en\x27
  1813. ? path(\x27cvs_application_jobs_filter\x27,{\x27slug\x27:currentCityFilter.slug})
  1814. : path(\x27locale_cvs_application_jobs_filter\x27,{\x27_locale\x27:app.request.locale,\x27slug\x27:currentCityFilter.slug}))|json_encode|raw
  1815. : \x27null\x27 }};
  1816. var CITY_KEYWORD_URL_TEMPLATE = {{ currentCityFilter is defined and currentCityFilter is not null
  1817. ? (app.request.locale == \x27en\x27
  1818. ? path(\x27cvs_application_jobs_filter_keyword\x27,{\x27slug\x27:currentCityFilter.slug,\x27keyword\x27:\x27kwplaceholder\x27})
  1819. : path(\x27locale_cvs_application_jobs_filter_keyword\x27,{\x27_locale\x27:app.request.locale,\x27slug\x27:currentCityFilter.slug,\x27keyword\x27:\x27kwplaceholder\x27}))|json_encode|raw
  1820. : \x27null\x27 }};
  1821. // Liste des keywords BDD avec leur slug (pour le matching côté client au submit)
  1822. var KEYWORDS_DATA = [
  1823. {% for kw in searchKeywords|default([]) %}
  1824. {% set kwSlugForJs = kw.label
  1825. |replace({
  1826. \x27à\x27:\x27a\x27,\x27â\x27:\x27a\x27,\x27ä\x27:\x27a\x27,\x27á\x27:\x27a\x27,\x27ã\x27:\x27a\x27,\x27å\x27:\x27a\x27,
  1827. \x27À\x27:\x27a\x27,\x27Â\x27:\x27a\x27,\x27Ä\x27:\x27a\x27,\x27Á\x27:\x27a\x27,\x27Ã\x27:\x27a\x27,\x27Å\x27:\x27a\x27,
  1828. \x27é\x27:\x27e\x27,\x27è\x27:\x27e\x27,\x27ê\x27:\x27e\x27,\x27ë\x27:\x27e\x27,\x27ẽ\x27:\x27e\x27,
  1829. \x27É\x27:\x27e\x27,\x27È\x27:\x27e\x27,\x27Ê\x27:\x27e\x27,\x27Ë\x27:\x27e\x27,
  1830. \x27î\x27:\x27i\x27,\x27ï\x27:\x27i\x27,\x27ì\x27:\x27i\x27,\x27í\x27:\x27i\x27,
  1831. \x27Î\x27:\x27i\x27,\x27Ï\x27:\x27i\x27,\x27Ì\x27:\x27i\x27,\x27Í\x27:\x27i\x27,
  1832. \x27ô\x27:\x27o\x27,\x27ö\x27:\x27o\x27,\x27ò\x27:\x27o\x27,\x27ó\x27:\x27o\x27,\x27õ\x27:\x27o\x27,\x27ø\x27:\x27o\x27,
  1833. \x27Ô\x27:\x27o\x27,\x27Ö\x27:\x27o\x27,\x27Ò\x27:\x27o\x27,\x27Ó\x27:\x27o\x27,\x27Õ\x27:\x27o\x27,\x27Ø\x27:\x27o\x27,
  1834. \x27û\x27:\x27u\x27,\x27ü\x27:\x27u\x27,\x27ù\x27:\x27u\x27,\x27ú\x27:\x27u\x27,
  1835. \x27Û\x27:\x27u\x27,\x27Ü\x27:\x27u\x27,\x27Ù\x27:\x27u\x27,\x27Ú\x27:\x27u\x27,
  1836. \x27ÿ\x27:\x27y\x27,\x27ý\x27:\x27y\x27,\x27Ÿ\x27:\x27y\x27,\x27Ý\x27:\x27y\x27,
  1837. \x27ç\x27:\x27c\x27,\x27Ç\x27:\x27c\x27,\x27ñ\x27:\x27n\x27,\x27Ñ\x27:\x27n\x27,
  1838. \x27œ\x27:\x27oe\x27,\x27Œ\x27:\x27oe\x27,\x27æ\x27:\x27ae\x27,\x27Æ\x27:\x27ae\x27,
  1839. \"\x27\":\x27-\x27,\x27 \x27:\x27-\x27,\x27/\x27:\x27-\x27,\x27_\x27:\x27-\x27,\x27.\x27:\x27-\x27,\x27,\x27:\x27-\x27,
  1840. \x27(\x27:\x27\x27,\x27*\x27:\x27\x27,\x27)\x27:\x27\x27,\x27&\x27:\x27-and-\x27,\x27+\x27:\x27-plus-\x27,
  1841. \x27#\x27:\x27\x27,\x27?\x27:\x27\x27,\x27!\x27:\x27\x27,\x27\"\x27:\x27\x27,\x27’\x27:\x27-\x27,\x27`\x27:\x27-\x27
  1842. })
  1843. |lower
  1844. |replace({\x27--\x27:\x27-\x27,\x27---\x27:\x27-\x27})
  1845. |trim(\x27-\x27) %}
  1846. { label: {{ kw.label|json_encode|raw }}, searchKeyword: {{ (kw.searchKeyword ?: kw.label)|json_encode|raw }}, slug: {{ kwSlugForJs|json_encode|raw }} }{% if not loop.last %},{% endif %}
  1847. {% endfor %}
  1848. ];
  1849. var input = document.getElementById(\x27dashSearchInput\x27);
  1850. var clearBtn = document.getElementById(\x27dashSearchClear\x27);
  1851. var listEl = document.getElementById(\x27jobsList\x27);
  1852. var infoEl = document.getElementById(\x27jobsResultsCount\x27);
  1853. var loadMoreBtn = document.getElementById(\x27jobsLoadMore\x27);
  1854. var loadMoreLabel = document.getElementById(\x27jobsLoadMoreLabel\x27);
  1855. function escapeHtml(s){
  1856. if(s == null) return \x27\x27;
  1857. return String(s).replace(/[&<>\"\x27]/g, function(c){
  1858. return {\x27&\x27:\x27&amp;\x27,\x27<\x27:\x27&lt;\x27,\x27>\x27:\x27&gt;\x27,\x27\"\x27:\x27&quot;\x27,\"\x27\":\x27&#39;\x27}[c];
  1859. });
  1860. }
  1861. function buildJobCard(item){
  1862. // Fallback logo : icône \"immeuble\" stylisée si pas de logo entreprise
  1863. var fallbackLogo =
  1864. \x27<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\x27 +
  1865. \x27<path d=\"M3 21h18\"/>\x27 +
  1866. \x27<path d=\"M5 21V7l8-4v18\"/>\x27 +
  1867. \x27<path d=\"M19 21V11l-6-4\"/>\x27 +
  1868. \x27<path d=\"M9 9h.01\"/>\x27 +
  1869. \x27<path d=\"M9 13h.01\"/>\x27 +
  1870. \x27<path d=\"M9 17h.01\"/>\x27 +
  1871. \x27</svg>\x27;
  1872. var logoHtml = item.logo
  1873. ? \x27<img src=\"\x27 + escapeHtml(item.logo) + \x27\" alt=\"\" onerror=\"this.parentNode.innerHTML=\\\x27\x27 + fallbackLogo.replace(/\x27/g, \"\\\\\x27\") + \x27\\\x27;\">\x27
  1874. : fallbackLogo;
  1875. var meta = [];
  1876. if(item.companyName) meta.push(escapeHtml(item.companyName));
  1877. if(item.city) meta.push(escapeHtml(item.city));
  1878. if(item.employmentType) meta.push(escapeHtml(item.employmentType));
  1879. var salaryHtml = (item.salaryMin && item.salaryMax)
  1880. ? \x27<span class=\"jobs-card-salary\">\x27 + item.salaryMin + \x27–\x27 + item.salaryMax + \x27 \x27 + escapeHtml(item.devise || \x27\x27) + \x27</span>\x27
  1881. : \x27\x27;
  1882. var url = item.url || JOB_SHOW_URL_TEMPLATE.replace(\x27__SLUG__\x27, encodeURIComponent(item.slug));
  1883. var a = document.createElement(\x27a\x27);
  1884. a.href = url;
  1885. a.className = \x27jobs-card\x27;
  1886. a.innerHTML =
  1887. \x27<div class=\"jobs-card-logo\">\x27 + logoHtml + \x27</div>\x27 +
  1888. \x27<div class=\"jobs-card-info\">\x27 +
  1889. \x27<div class=\"jobs-card-title\">\x27 + escapeHtml(item.title || \x27\x27) + \x27</div>\x27 +
  1890. \x27<div class=\"jobs-card-meta\">\x27 + meta.join(\x27 · \x27) + \x27</div>\x27 +
  1891. \x27</div>\x27 +
  1892. salaryHtml +
  1893. \x27<span class=\"jobs-card-arrow\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"9 18 15 12 9 6\"/></svg></span>\x27;
  1894. return a;
  1895. }
  1896. /**
  1897. * Clone la promo card depuis le <template> rendu par la macro Twig.
  1898. * Le HTML est défini une seule fois dans _macros/promo_card.html.twig.
  1899. * Retourne null si le template n\x27est pas présent (ex: user connecté).
  1900. */
  1901. function buildPromoCard(){
  1902. var tpl = document.getElementById(\x27jobs-promo-card-template\x27);
  1903. if(!tpl || !tpl.content) return null;
  1904. var node = tpl.content.firstElementChild;
  1905. return node ? node.cloneNode(true) : null;
  1906. }
  1907. function renderEmpty(){
  1908. listEl.innerHTML =
  1909. \x27<div class=\"jobs-empty\">\x27 +
  1910. \x27<div class=\"jobs-empty-icon\">\x27 +
  1911. \x27<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>\x27 +
  1912. \x27</div>\x27 +
  1913. \x27<div class=\"jobs-empty-title\">{{ \x27job.dashboard.emptyTitle\x27|trans|escape(\x27js\x27) }}</div>\x27 +
  1914. \x27<div class=\"jobs-empty-text\">{{ \x27job.dashboard.emptyText\x27|trans|escape(\x27js\x27) }}</div>\x27 +
  1915. \x27</div>\x27;
  1916. }
  1917. function setLoadMoreState(state){
  1918. if(!loadMoreBtn) return;
  1919. if(state === \x27loading\x27){
  1920. loadMoreBtn.classList.add(\x27loading\x27);
  1921. loadMoreBtn.disabled = true;
  1922. loadMoreLabel.innerHTML = \x27<span class=\"jobs-load-spinner\"></span> {{ \x27job.dashboard.loadingMore\x27|trans|escape(\x27js\x27) }}\x27;
  1923. } else {
  1924. loadMoreBtn.classList.remove(\x27loading\x27);
  1925. loadMoreBtn.disabled = false;
  1926. loadMoreLabel.textContent = \x27{{ \x27job.dashboard.loadMore\x27|trans|escape(\x27js\x27) }}\x27;
  1927. }
  1928. }
  1929. function fetchJobs(query, offset, append){
  1930. if(isFetching) return;
  1931. isFetching = true;
  1932. if(!append){
  1933. // Nouvelle recherche : on remet à zéro et affiche skeleton
  1934. listEl.innerHTML =
  1935. \x27<div class=\"jobs-skeleton\">\x27 +
  1936. Array(5).fill(0).map(function(){
  1937. return \x27<div class=\"jobs-skel-card\"><div class=\"jobs-skel-logo\"></div><div class=\"jobs-skel-info\"><div class=\"jobs-skel-line s1\"></div><div class=\"jobs-skel-line s2\"></div></div></div>\x27;
  1938. }).join(\x27\x27) +
  1939. \x27</div>\x27;
  1940. if(infoEl) infoEl.textContent = \x27{{ \x27job.dashboard.loading\x27|trans|escape(\x27js\x27) }}\x27;
  1941. if(loadMoreBtn) loadMoreBtn.style.display = \x27none\x27;
  1942. } else {
  1943. setLoadMoreState(\x27loading\x27);
  1944. }
  1945. var url = SEARCH_API_URL + \x27?q=\x27 + encodeURIComponent(query || \x27\x27) + \x27&locale=\x27 + LOCALE + \x27&limit=\x27 + PAGE_SIZE + \x27&offset=\x27 + offset;
  1946. if(CITY_KEYWORD){ url += \x27&city=\x27 + encodeURIComponent(CITY_KEYWORD); }
  1947. // Dashboard sans filtre : on demande explicitement les dernières offres
  1948. if(!query && !CITY_KEYWORD){ url += \x27&all=1\x27; }
  1949. fetch(url, {headers:{\x27Accept\x27:\x27application/json\x27}})
  1950. .then(function(r){ return r.ok ? r.json() : null; })
  1951. .then(function(data){
  1952. isFetching = false;
  1953. if(!data){
  1954. if(!append) renderEmpty();
  1955. setLoadMoreState(\x27idle\x27);
  1956. return;
  1957. }
  1958. var items = data.items || [];
  1959. currentTotal = data.total || 0;
  1960. if(!append){
  1961. listEl.innerHTML = \x27\x27;
  1962. if(items.length === 0){
  1963. renderEmpty();
  1964. if(infoEl) infoEl.innerHTML = \x27<strong>0</strong> {{ \x27job.dashboard.resultsCount\x27|trans|escape(\x27js\x27) }}\x27;
  1965. if(loadMoreBtn) loadMoreBtn.style.display = \x27none\x27;
  1966. return;
  1967. }
  1968. }
  1969. // Insérer la promo card après la 5ème offre (uniquement au 1er chargement,
  1970. // si user non connecté et qu\x27on a au moins 5 offres)
  1971. var insertPromoAt = (!append && !IS_AUTHENTICATED && items.length >= 5) ? 5 : -1;
  1972. items.forEach(function(item, idx){
  1973. listEl.appendChild(buildJobCard(item));
  1974. if(idx === insertPromoAt - 1){
  1975. var promoNode = buildPromoCard();
  1976. if(promoNode) listEl.appendChild(promoNode);
  1977. }
  1978. });
  1979. currentOffset = offset + items.length;
  1980. if(infoEl){
  1981. infoEl.innerHTML = \x27<strong>\x27 + currentTotal + \x27</strong> {{ \x27job.dashboard.resultsCount\x27|trans|escape(\x27js\x27) }}\x27;
  1982. }
  1983. // Afficher / cacher \"Charger plus\"
  1984. if(loadMoreBtn){
  1985. if(currentOffset < currentTotal){
  1986. loadMoreBtn.style.display = \x27flex\x27;
  1987. } else {
  1988. loadMoreBtn.style.display = \x27none\x27;
  1989. }
  1990. setLoadMoreState(\x27idle\x27);
  1991. }
  1992. })
  1993. .catch(function(err){
  1994. isFetching = false;
  1995. console.error(\x27[whr-dash] fetch error\x27, err);
  1996. setLoadMoreState(\x27idle\x27);
  1997. });
  1998. }
  1999. /**
  2000. * Slugifie un texte côté client. DOIT produire EXACTEMENT le même slug
  2001. * que la méthode PHP slugify() du JobsController et que le filtre Twig
  2002. * utilisé pour rendre les chips.
  2003. */
  2004. function slugify(text){
  2005. if(!text) return \x27\x27;
  2006. var t = String(text);
  2007. // Pré-remplacements sémantiques
  2008. t = t.replace(/&/g, \x27-and-\x27).replace(/\\+/g, \x27-plus-\x27)
  2009. .replace(/[œŒ]/g, \x27oe\x27).replace(/[æÆ]/g, \x27ae\x27);
  2010. // Translitération accents → ASCII via NFD + strip diacritics
  2011. t = t.normalize ? t.normalize(\x27NFD\x27).replace(/[\\u0300-\\u036f]/g, \x27\x27) : t;
  2012. t = t.toLowerCase();
  2013. // Tout ce qui n\x27est pas a-z 0-9 → tiret
  2014. t = t.replace(/[^a-z0-9]+/g, \x27-\x27);
  2015. return t.replace(/^-+|-+\$/g, \x27\x27);
  2016. }
  2017. /**
  2018. * Tente de matcher un texte tapé avec un keyword en BDD (par slug ou label).
  2019. * Retourne l\x27objet keyword si trouvé, null sinon.
  2020. */
  2021. function findMatchingKeyword(text){
  2022. if(!text || !KEYWORDS_DATA || !KEYWORDS_DATA.length) return null;
  2023. var slug = slugify(text);
  2024. if(!slug) return null;
  2025. for(var i = 0; i < KEYWORDS_DATA.length; i++){
  2026. var kw = KEYWORDS_DATA[i];
  2027. // Match sur slug exact OU label/searchKeyword exact (case insensitive)
  2028. if(kw.slug === slug) return kw;
  2029. if(slugify(kw.searchKeyword) === slug) return kw;
  2030. }
  2031. return null;
  2032. }
  2033. /**
  2034. * Au submit : décide où rediriger l\x27utilisateur en fonction du contexte.
  2035. * - Si on est sur une ville et le texte matche un keyword BDD → /jobs/{ville}/{slug-kw}
  2036. * - Sinon si on est sur une ville → /jobs/{ville}?search={texte}
  2037. * - Sinon (pas de ville) → /jobs?q={texte}
  2038. * - Si texte vide ET ville définie → /jobs/{ville} (reset)
  2039. * - Si texte vide ET pas de ville → /jobs (reset)
  2040. */
  2041. function submitSearch(query){
  2042. query = (query || \x27\x27).trim();
  2043. // Reset (recherche vide)
  2044. if(!query){
  2045. if(CITY_SLUG && CITY_LANDING_URL_TEMPLATE){
  2046. window.location.href = CITY_LANDING_URL_TEMPLATE;
  2047. } else {
  2048. window.location.href = DASHBOARD_URL;
  2049. }
  2050. return;
  2051. }
  2052. // Cas 1 : on est sur une ville
  2053. if(CITY_SLUG && CITY_KEYWORD_URL_TEMPLATE){
  2054. var matched = findMatchingKeyword(query);
  2055. if(matched && matched.slug){
  2056. // Match BDD → URL slug propre /jobs/paris/developpeur
  2057. window.location.href = CITY_KEYWORD_URL_TEMPLATE.replace(\x27kwplaceholder\x27, encodeURIComponent(matched.slug));
  2058. return;
  2059. }
  2060. // Pas de match → fallback ?search=texte
  2061. window.location.href = CITY_LANDING_URL_TEMPLATE + \x27?search=\x27 + encodeURIComponent(query);
  2062. return;
  2063. }
  2064. // Cas 2 : pas de ville → dashboard global avec ?q=
  2065. window.location.href = DASHBOARD_URL + \x27?q=\x27 + encodeURIComponent(query);
  2066. }
  2067. /**
  2068. * Lance la search côté UI (sans redirection — utilisée pour les chips et
  2069. * pour la \"Charger plus\").
  2070. */
  2071. function startSearch(query){
  2072. currentQuery = query;
  2073. currentOffset = 0;
  2074. currentTotal = 0;
  2075. fetchJobs(query, 0, false);
  2076. // Update clear button
  2077. if(clearBtn){
  2078. if(query) clearBtn.classList.add(\x27visible\x27);
  2079. else clearBtn.classList.remove(\x27visible\x27);
  2080. }
  2081. // Update active chip (uniquement les chips qui sont des <button>, pas les <a>)
  2082. document.querySelectorAll(\x27button.search-keyword-chip\x27).forEach(function(c){
  2083. if(c.getAttribute(\x27data-keyword\x27) === query) c.classList.add(\x27active\x27);
  2084. else c.classList.remove(\x27active\x27);
  2085. });
  2086. }
  2087. // ═══ Submit-only : Entrée déclenche la search ET change l\x27URL ═══
  2088. if(input){
  2089. input.addEventListener(\x27keydown\x27, function(e){
  2090. if(e.key === \x27Enter\x27 || e.keyCode === 13){
  2091. e.preventDefault();
  2092. submitSearch(input.value);
  2093. }
  2094. });
  2095. // Affiche bouton clear si query initiale
  2096. if(input.value.trim()){
  2097. if(clearBtn) clearBtn.classList.add(\x27visible\x27);
  2098. }
  2099. }
  2100. // Bouton clear : reset complet → redirection vers /jobs ou /jobs/{ville}
  2101. if(clearBtn){
  2102. clearBtn.addEventListener(\x27click\x27, function(){
  2103. submitSearch(\x27\x27);
  2104. });
  2105. }
  2106. // Chips keywords (cas dashboard global, sans contexte ville → comportement local)
  2107. // En contexte ville, les chips sont des <a href> directs gérés par le navigateur.
  2108. document.querySelectorAll(\x27button.search-keyword-chip\x27).forEach(function(chip){
  2109. chip.addEventListener(\x27click\x27, function(){
  2110. var kw = chip.getAttribute(\x27data-keyword\x27);
  2111. if(!kw) return;
  2112. // Sur dashboard global : on submit (redirige vers /jobs?q=kw)
  2113. submitSearch(kw);
  2114. });
  2115. });
  2116. // Bouton \"Charger plus\"
  2117. if(loadMoreBtn){
  2118. loadMoreBtn.addEventListener(\x27click\x27, function(){
  2119. if(isFetching) return;
  2120. fetchJobs(currentQuery, currentOffset, true);
  2121. });
  2122. }
  2123. // ═══ Bouton \"Voir plus\" + Recherche live des chips ═══
  2124. /**
  2125. * Slugify simple côté UI pour matching tolérant aux accents
  2126. * (ex: \"developpeur\" matche \"Développeur\")
  2127. */
  2128. function dashSlug(str){
  2129. if(!str) return \x27\x27;
  2130. var t = String(str);
  2131. t = t.normalize ? t.normalize(\x27NFD\x27).replace(/[\\u0300-\\u036f]/g, \x27\x27) : t;
  2132. return t.toLowerCase().trim();
  2133. }
  2134. document.querySelectorAll(\x27.dash-filter-more-btn\x27).forEach(function(btn){
  2135. var targetId = btn.getAttribute(\x27data-target\x27);
  2136. var sectionId = btn.getAttribute(\x27data-section\x27);
  2137. var container = document.getElementById(targetId);
  2138. var section = sectionId ? document.getElementById(sectionId) : null;
  2139. if(!container) return;
  2140. var label = btn.querySelector(\x27.dash-filter-more-label\x27);
  2141. if(!label) return;
  2142. var initial = parseInt(container.getAttribute(\x27data-initial\x27), 10) || 14;
  2143. var items = container.querySelectorAll(\x27.dash-filter-item\x27);
  2144. var searchInput = section ? section.querySelector(\x27.dash-filter-search-input\x27) : null;
  2145. // 2 états indépendants :
  2146. // - userExpanded : l\x27utilisateur a cliqué sur \"Voir plus\"
  2147. // - searchActive : l\x27input contient du texte (auto-expand temporaire)
  2148. // Mode développé = userExpanded || searchActive
  2149. var userExpanded = false;
  2150. function isEffectivelyExpanded(){
  2151. var hasSearch = searchInput && searchInput.value.trim().length > 0;
  2152. return userExpanded || hasSearch;
  2153. }
  2154. function applyDisplay(){
  2155. var filterText = searchInput ? searchInput.value : \x27\x27;
  2156. var query = dashSlug(filterText || \x27\x27);
  2157. var expanded = isEffectivelyExpanded();
  2158. var visibleCount = 0;
  2159. items.forEach(function(it, i){
  2160. var label = it.getAttribute(\x27data-filter-label\x27) || \x27\x27;
  2161. var matches = !query || dashSlug(label).indexOf(query) !== -1;
  2162. if(expanded){
  2163. // Mode développé : on affiche tous les items qui matchent la recherche
  2164. it.style.display = matches ? \x27\x27 : \x27none\x27;
  2165. if(matches) visibleCount++;
  2166. } else {
  2167. // Mode collapsé : on affiche les N premiers (pas de filtrage)
  2168. it.style.display = (i < initial) ? \x27\x27 : \x27none\x27;
  2169. if(i < initial) visibleCount++;
  2170. }
  2171. });
  2172. // Toggle \"aucun résultat\" (uniquement quand recherche active)
  2173. if(query && visibleCount === 0){
  2174. container.classList.add(\x27is-no-results\x27);
  2175. } else {
  2176. container.classList.remove(\x27is-no-results\x27);
  2177. }
  2178. // Synchroniser le bouton \"Voir plus/moins\" avec l\x27état effectif
  2179. if(expanded){
  2180. btn.classList.add(\x27is-expanded\x27);
  2181. label.textContent = label.getAttribute(\x27data-less\x27);
  2182. } else {
  2183. btn.classList.remove(\x27is-expanded\x27);
  2184. label.textContent = label.getAttribute(\x27data-more\x27);
  2185. }
  2186. }
  2187. btn.addEventListener(\x27click\x27, function(){
  2188. userExpanded = !userExpanded;
  2189. if(!userExpanded){
  2190. // Reset input quand l\x27utilisateur collapse manuellement
  2191. if(searchInput){ searchInput.value = \x27\x27; }
  2192. // Scroll vers le bouton
  2193. btn.scrollIntoView({behavior:\x27smooth\x27, block:\x27nearest\x27});
  2194. }
  2195. applyDisplay();
  2196. });
  2197. // Recherche live dans les chips quand on tape dans l\x27input
  2198. // → auto-expand pendant la recherche, retour à l\x27état userExpanded quand on vide
  2199. if(searchInput){
  2200. searchInput.addEventListener(\x27input\x27, function(){
  2201. applyDisplay();
  2202. });
  2203. }
  2204. });
  2205. // Premier chargement (vide ou avec query initiale)
  2206. fetchJobs(currentQuery, 0, false);
  2207. } catch(err){
  2208. console.error(\x27[whr-dash] init error\x27, err);
  2209. }
  2210. })();
  2211. </script>
  2212. {% endblock footerjs %}
  2213. ", "application/whileresume/application/jobs/dashboard.html.twig", "/var/www/vhosts/whileresume.com/b.whileresume.app/templates/application/whileresume/application/jobs/dashboard.html.twig");
  2214. }
  2215. }