var/cache/dev/twig/f2/f2ecf247f09ea21edc7b707efbc8f96b.php line 45

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\Source;
  13. use Twig\Template;
  14. use Twig\TemplateWrapper;
  15. /* home/single-product.html.twig */
  16. class __TwigTemplate_aae7211b9c474856cd179227db62caa6 extends Template
  17. {
  18.     private Source $source;
  19.     /**
  20.      * @var array<string, Template>
  21.      */
  22.     private array $macros = [];
  23.     public function __construct(Environment $env)
  24.     {
  25.         parent::__construct($env);
  26.         $this->source $this->getSourceContext();
  27.         $this->blocks = [
  28.             'title' => [$this'block_title'],
  29.             'stylesheets' => [$this'block_stylesheets'],
  30.             'body' => [$this'block_body'],
  31.         ];
  32.     }
  33.     protected function doGetParent(array $context): bool|string|Template|TemplateWrapper
  34.     {
  35.         // line 1
  36.         return "base_home.html.twig";
  37.     }
  38.     protected function doDisplay(array $context, array $blocks = []): iterable
  39.     {
  40.         $macros $this->macros;
  41.         $__internal_5a27a8ba21ca79b61932376b2fa922d2 $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  42.         $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template""home/single-product.html.twig"));
  43.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  44.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template""home/single-product.html.twig"));
  45.         $this->parent $this->load("base_home.html.twig"1);
  46.         yield from $this->parent->unwrap()->yield($contextarray_merge($this->blocks$blocks));
  47.         
  48.         $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  49.         
  50.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  51.     }
  52.     // line 3
  53.     /**
  54.      * @return iterable<null|scalar|\Stringable>
  55.      */
  56.     public function block_title(array $context, array $blocks = []): iterable
  57.     {
  58.         $macros $this->macros;
  59.         $__internal_5a27a8ba21ca79b61932376b2fa922d2 $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  60.         $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block""title"));
  61.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  62.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block""title"));
  63.         // line 4
  64.         yield "\t";
  65.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "name", [], "any"falsefalsefalse4), "html"nulltrue);
  66.         yield "
  67. \t| MaketOu
  68. ";
  69.         
  70.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  71.         
  72.         $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  73.         yield from [];
  74.     }
  75.     // line 8
  76.     /**
  77.      * @return iterable<null|scalar|\Stringable>
  78.      */
  79.     public function block_stylesheets(array $context, array $blocks = []): iterable
  80.     {
  81.         $macros $this->macros;
  82.         $__internal_5a27a8ba21ca79b61932376b2fa922d2 $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  83.         $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block""stylesheets"));
  84.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  85.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block""stylesheets"));
  86.         // line 9
  87.         yield "\t<style>
  88. \t\t/* Styles pour l'affichage de la boutique */
  89. \t\t.shop-info {
  90. \t\t\tpadding: 10px 0;
  91. \t\t\tborder-bottom: 1px solid #f0f0f0;
  92. \t\t\tmargin-bottom: 15px;
  93. \t\t}
  94. \t\t.shop-link {
  95. \t\t\tcolor: #ffa200;
  96. \t\t\ttext-decoration: none;
  97. \t\t\tfont-weight: 500;
  98. \t\t}
  99. \t\t.shop-link:hover {
  100. \t\t\tcolor: #e8910a;
  101. \t\t\ttext-decoration: underline;
  102. \t\t}
  103. \t\t/* Styles pour les statistiques du produit */
  104. \t\t.product-stats {
  105. \t\t\tbackground: #f8f9fa;
  106. \t\t\tpadding: 15px;
  107. \t\t\tborder-radius: 8px;
  108. \t\t\tborder: 1px solid #e9ecef;
  109. \t\t}
  110. \t\t.product-stats .stat-item {
  111. \t\t\ttext-align: center;
  112. \t\t}
  113. \t\t.product-stats .stat-number {
  114. \t\t\tdisplay: block;
  115. \t\t\tfont-size: 1.2rem;
  116. \t\t\tfont-weight: bold;
  117. \t\t\tcolor: #ffa200;
  118. \t\t}
  119. \t\t.product-stats .stat-label {
  120. \t\t\tcolor: #666;
  121. \t\t\tfont-size: 0.8rem;
  122. \t\t\ttext-transform: uppercase;
  123. \t\t\tletter-spacing: 0.5px;
  124. \t\t}
  125. \t\t/* Message de confirmation */
  126. \t\t#cart-message {
  127. \t\t\tborder: none;
  128. \t\t\tbackground: #d4edda;
  129. \t\t\tcolor: #155724;
  130. \t\t\tborder-radius: 6px;
  131. \t\t\tpadding: 12px 16px;
  132. \t\t\tmargin-top: 15px;
  133. \t\t}
  134. \t\t/* Amélioration des boutons d'actions */
  135. \t\t.card_area {
  136. \t\t\tmargin-top: 20px;
  137. \t\t\tgap: 12px;
  138. \t\t}
  139. \t\t.card_area .primary-btn {
  140. \t\t\tbackground: #ffa200;
  141. \t\t\tborder: none;
  142. \t\t\tpadding: 0 30px;
  143. \t\t\theight: 50px;
  144. \t\t\tdisplay: inline-flex;
  145. \t\t\talign-items: center;
  146. \t\t\tjustify-content: center;
  147. \t\t\tborder-radius: 8px;
  148. \t\t\tcolor: white;
  149. \t\t\tfont-weight: 600;
  150. \t\t\tfont-size: 16px;
  151. \t\t\ttransition: all 0.3s ease;
  152. \t\t\ttext-decoration: none;
  153. \t\t\tgap: 8px;
  154. \t\t\tflex: 1;
  155. \t\t\tmax-width: 250px;
  156. \t\t}
  157. \t\t.card_area .primary-btn i {
  158. \t\t\tfont-size: 20px;
  159. \t\t}
  160. \t\t.card_area .primary-btn:hover {
  161. \t\t\tbackground: #e8910a;
  162. \t\t\ttransform: translateY(-2px);
  163. \t\t\tbox-shadow: 0 4px 15px rgba(255, 162, 0, 0.3);
  164. \t\t\tcolor: white;
  165. \t\t}
  166. \t\t.card_area .icon_btn {
  167. \t\t\tdisplay: inline-flex;
  168. \t\t\talign-items: center;
  169. \t\t\tjustify-content: center;
  170. \t\t\twidth: 50px;
  171. \t\t\theight: 50px;
  172. \t\t\tborder-radius: 8px;
  173. \t\t\tbackground: #fff;
  174. \t\t\tcolor: #333;
  175. \t\t\tborder: 1px solid #ddd;
  176. \t\t\ttransition: all 0.3s ease;
  177. \t\t\ttext-decoration: none;
  178. \t\t}
  179. \t\t.card_area .icon_btn i {
  180. \t\t\tfont-size: 22px;
  181. \t\t}
  182. \t\t.card_area .icon_btn:hover {
  183. \t\t\tbackground: #ffa200;
  184. \t\t\tcolor: white;
  185. \t\t\tborder-color: #ffa200;
  186. \t\t\ttransform: translateY(-2px);
  187. \t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.2);
  188. \t\t}
  189. \t\t/* Styles pour la galerie moderne de produits */
  190. \t\t.product-gallery-modern {
  191. \t\t\tdisplay: flex;
  192. \t\t\tgap: 15px;
  193. \t\t\tposition: relative;
  194. \t\t\talign-items: stretch;
  195. \t\t}
  196. \t\t.thumbnails-container {
  197. \t\t\tposition: relative;
  198. \t\t\tdisplay: flex;
  199. \t\t\tflex-direction: column;
  200. \t\t\talign-items: center;
  201. \t\t\twidth: 80px;
  202. \t\t\tflex-shrink: 0;
  203. \t\t\theight: auto;
  204. \t\t}
  205. \t\t/* Les boutons sont toujours visibles (meilleur pour le mobile et l'UX) */
  206. \t\t.thumbnails-container .thumbnail-nav-btn {
  207. \t\t\ttransition: all 0.2s ease;
  208. \t\t}
  209. \t\t.product-thumbnails {
  210. \t\t\tdisplay: flex;
  211. \t\t\tflex-direction: column;
  212. \t\t\tgap: 10px;
  213. \t\t\twidth: 100%;
  214. \t\t\tflex: 1;
  215. \t\t\toverflow: hidden;
  216. \t\t\tposition: relative;
  217. \t\t}
  218. \t\t.thumbnails-wrapper {
  219. \t\t\tdisplay: flex;
  220. \t\t\tflex-direction: column;
  221. \t\t\tgap: 10px;
  222. \t\t\ttransition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  223. \t\t\twill-change: transform;
  224. \t\t}
  225. \t\t.thumbnail-nav-btn {
  226. \t\t\twidth: 30px;
  227. \t\t\theight: 30px;
  228. \t\t\tborder-radius: 50%;
  229. \t\t\tbackground: white;
  230. \t\t\tborder: 2px solid #e0e0e0;
  231. \t\t\tdisplay: flex !important;
  232. \t\t\talign-items: center;
  233. \t\t\tjustify-content: center;
  234. \t\t\tcursor: pointer;
  235. \t\t\ttransition: all 0.3s ease;
  236. \t\t\tmargin: 0;
  237. \t\t\tpadding: 0;
  238. \t\t\tcolor: #666;
  239. \t\t\tz-index: 10;
  240. \t\t\tbox-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  241. \t\t\tposition: relative;
  242. \t\t\tflex-shrink: 0;
  243. \t\t}
  244. \t\t.thumbnail-nav-btn:hover {
  245. \t\t\tbackground: #ffa200;
  246. \t\t\tborder-color: #ffa200;
  247. \t\t\tcolor: white;
  248. \t\t\ttransform: scale(1.1);
  249. \t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.3);
  250. \t\t}
  251. \t\t.thumbnail-nav-btn:active {
  252. \t\t\ttransform: scale(0.95);
  253. \t\t}
  254. \t\t.thumbnail-nav-btn.disabled {
  255. \t\t\topacity: 0.3;
  256. \t\t\tcursor: not-allowed;
  257. \t\t\tpointer-events: none;
  258. \t\t\tfilter: grayscale(1);
  259. \t\t}
  260. \t\t.thumbnail-nav-btn i {
  261. \t\t\tfont-size: 20px;
  262. \t\t\tfont-weight: bold;
  263. \t\t}
  264. \t\t.thumbnail-item {
  265. \t\t\twidth: 80px;
  266. \t\t\theight: 80px;
  267. \t\t\tmin-width: 80px;
  268. \t\t\tmin-height: 80px;
  269. \t\t\tmax-width: 80px;
  270. \t\t\tmax-height: 80px;
  271. \t\t\tborder: 2px solid #e0e0e0;
  272. \t\t\tborder-radius: 8px;
  273. \t\t\toverflow: hidden;
  274. \t\t\tcursor: pointer;
  275. \t\t\ttransition: border-color 0.3s ease, border-width 0.3s ease, box-shadow 0.3s ease;
  276. \t\t\tposition: relative;
  277. \t\t\tbackground: #fff;
  278. \t\t\tborder-bottom: 2px solid #e0e0e0;
  279. \t\t\tflex-shrink: 0;
  280. \t\t}
  281. \t\t.thumbnail-item:hover {
  282. \t\t\tborder-color: #ffa200;
  283. \t\t\tborder-bottom-color: #ffa200;
  284. \t\t\tborder-width: 3px;
  285. \t\t\tbox-shadow: 0 0 0 2px rgba(255, 162, 0, 0.2);
  286. \t\t}
  287. \t\t.thumbnail-item.active {
  288. \t\t\tborder-color: #ffa200 !important;
  289. \t\t\tborder-bottom-color: #ffa200 !important;
  290. \t\t\tborder-width: 3px !important;
  291. \t\t\tbox-shadow: 0 0 0 2px rgba(255, 162, 0, 0.2) !important;
  292. \t\t}
  293. \t\t.thumbnail-item::after,
  294. \t\t.thumbnail-item::before {
  295. \t\t\tdisplay: none !important;
  296. \t\t\tcontent: none !important;
  297. \t\t}
  298. \t\t.thumbnail-item img {
  299. \t\t\twidth: 100%;
  300. \t\t\theight: 100%;
  301. \t\t\tobject-fit: cover;
  302. \t\t\ttransition: transform 0.3s ease;
  303. \t\t}
  304. \t\t.thumbnail-item:hover img {
  305. \t\t\ttransform: scale(1.1);
  306. \t\t}
  307. \t\t.thumbnail-item.active img {
  308. \t\t\ttransform: scale(1.1);
  309. \t\t}
  310. \t\t.product-main-image-wrapper {
  311. \t\t\tflex: 1;
  312. \t\t\tposition: relative;
  313. \t\t\tbackground: #fff;
  314. \t\t\tborder-radius: 12px;
  315. \t\t\toverflow: hidden;
  316. \t\t\tborder: 1px solid #e0e0e0;
  317. \t\t\tcursor: crosshair;
  318. \t\t}
  319. \t\t.product-main-image {
  320. \t\t\tposition: relative;
  321. \t\t\twidth: 100%;
  322. \t\t\tpadding-top: 100%;
  323. \t\t\toverflow: hidden;
  324. \t\t\tbackground: #f8f9fa;
  325. \t\t}
  326. \t\t.product-main-image img {
  327. \t\t\tposition: absolute;
  328. \t\t\ttop: 0;
  329. \t\t\tleft: 0;
  330. \t\t\twidth: 100%;
  331. \t\t\theight: 100%;
  332. \t\t\tobject-fit: contain;
  333. \t\t\ttransition: transform 0.15s ease-out;
  334. \t\t\ttransform-origin: center center;
  335. \t\t}
  336. \t\t/* Supprimer le scale basique, géré en JS */
  337. \t\t/* .product-main-image:hover img {
  338. \t\t\ttransform: scale(1.05);
  339. \t\t} */
  340. \t\t.image-overlay-icons {
  341. \t\t\tposition: absolute;
  342. \t\t\ttop: 15px;
  343. \t\t\tright: 15px;
  344. \t\t\tdisplay: flex;
  345. \t\t\tflex-direction: column;
  346. \t\t\tgap: 10px;
  347. \t\t\tz-index: 20;
  348. \t\t\tpointer-events: none;
  349. \t\t}
  350. \t\t.icon-btn {
  351. \t\t\tpointer-events: all;
  352. \t\t\twidth: 45px;
  353. \t\t\theight: 45px;
  354. \t\t\tborder-radius: 50%;
  355. \t\t\tbackground: rgba(255, 255, 255, 0.95);
  356. \t\t\tborder: none;
  357. \t\t\tdisplay: flex;
  358. \t\t\talign-items: center;
  359. \t\t\tjustify-content: center;
  360. \t\t\tpadding: 0;
  361. \t\t\tcursor: pointer;
  362. \t\t\ttransition: all 0.3s ease;
  363. \t\t\tbox-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  364. \t\t\tcolor: #333;
  365. \t\t}
  366. \t\t.icon-btn:hover {
  367. \t\t\tbackground: #ffa200;
  368. \t\t\tcolor: white;
  369. \t\t\ttransform: scale(1.1);
  370. \t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.4);
  371. \t\t}
  372. \t\t.icon-btn i {
  373. \t\t\tfont-size: 18px;
  374. \t\t}
  375. \t\t.favorite-btn.active {
  376. \t\t\tbackground: #ff6b6b;
  377. \t\t\tcolor: white;
  378. \t\t}
  379. \t\t.image-counter {
  380. \t\t\tposition: absolute;
  381. \t\t\tbottom: 15px;
  382. \t\t\tright: 15px;
  383. \t\t\tbackground: rgba(0, 0, 0, 0.7);
  384. \t\t\tcolor: white;
  385. \t\t\tpadding: 6px 12px;
  386. \t\t\tborder-radius: 20px;
  387. \t\t\tfont-size: 13px;
  388. \t\t\tfont-weight: 600;
  389. \t\t}
  390. \t\t/* Modal de zoom d'image avec transitions modernes */
  391. \t\t.image-zoom-modal {
  392. \t\t\tdisplay: none;
  393. \t\t\tposition: fixed;
  394. \t\t\tz-index: 9999;
  395. \t\t\tleft: 0;
  396. \t\t\ttop: 0;
  397. \t\t\twidth: 100%;
  398. \t\t\theight: 100%;
  399. \t\t\tbackground: rgba(0, 0, 0, 0);
  400. \t\t\toverflow: auto;
  401. \t\t\topacity: 0;
  402. \t\t\ttransition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), background 0.3s ease;
  403. \t\t\tbackdrop-filter: blur(0px);
  404. \t\t}
  405. \t\t.image-zoom-modal.active {
  406. \t\t\tdisplay: flex;
  407. \t\t\talign-items: center;
  408. \t\t\tjustify-content: center;
  409. \t\t\topacity: 1;
  410. \t\t\tbackground: rgba(0, 0, 0, 0.95);
  411. \t\t\tbackdrop-filter: blur(10px);
  412. \t\t}
  413. \t\t.zoom-modal-content {
  414. \t\t\tposition: relative;
  415. \t\t\tmax-width: 90%;
  416. \t\t\tmax-height: 90%;
  417. \t\t\tmargin: auto;
  418. \t\t\topacity: 0;
  419. \t\t\ttransform: scale(0.8);
  420. \t\t\ttransition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1), transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  421. \t\t}
  422. \t\t.image-zoom-modal.active .zoom-modal-content {
  423. \t\t\topacity: 1;
  424. \t\t\ttransform: scale(1);
  425. \t\t}
  426. \t\t.zoom-modal-content img {
  427. \t\t\twidth: 100%;
  428. \t\t\theight: auto;
  429. \t\t\tmax-height: 90vh;
  430. \t\t\tobject-fit: contain;
  431. \t\t\tborder-radius: 8px;
  432. \t\t\tbox-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  433. \t\t\topacity: 0;
  434. \t\t\ttransition: opacity 0.3s ease;
  435. \t\t}
  436. \t\t.zoom-modal-content img.loaded {
  437. \t\t\topacity: 1;
  438. \t\t}
  439. \t\t/* Loader moderne */
  440. \t\t.zoom-loader {
  441. \t\t\tposition: absolute;
  442. \t\t\ttop: 50%;
  443. \t\t\tleft: 50%;
  444. \t\t\ttransform: translate(-50%, -50%);
  445. \t\t\tz-index: 1;
  446. \t\t\tdisplay: flex;
  447. \t\t\tflex-direction: column;
  448. \t\t\talign-items: center;
  449. \t\t\tjustify-content: center;
  450. \t\t}
  451. \t\t.zoom-loader-spinner {
  452. \t\t\twidth: 60px;
  453. \t\t\theight: 60px;
  454. \t\t\tborder: 4px solid rgba(255, 255, 255, 0.1);
  455. \t\t\tborder-top-color: #ffa200;
  456. \t\t\tborder-radius: 50%;
  457. \t\t\tanimation: spin 1s linear infinite;
  458. \t\t}
  459. \t\t@keyframes spin {
  460. \t\t\tto {
  461. \t\t\t\ttransform: rotate(360deg);
  462. \t\t\t}
  463. \t\t}
  464. \t\t.zoom-loader-text {
  465. \t\t\tcolor: white;
  466. \t\t\tmargin-top: 15px;
  467. \t\t\ttext-align: center;
  468. \t\t\tfont-size: 14px;
  469. \t\t\tfont-weight: 500;
  470. \t\t}
  471. \t\t.close-zoom {
  472. \t\t\tposition: absolute;
  473. \t\t\ttop: 30px;
  474. \t\t\tright: 30px;
  475. \t\t\tcolor: white;
  476. \t\t\tfont-size: 32px;
  477. \t\t\tfont-weight: 300;
  478. \t\t\tcursor: pointer;
  479. \t\t\tz-index: 10000;
  480. \t\t\twidth: 50px;
  481. \t\t\theight: 50px;
  482. \t\t\tdisplay: flex;
  483. \t\t\talign-items: center;
  484. \t\t\tjustify-content: center;
  485. \t\t\tbackground: rgba(255, 255, 255, 0.1);
  486. \t\t\tborder: 2px solid rgba(255, 255, 255, 0.2);
  487. \t\t\tborder-radius: 50%;
  488. \t\t\ttransition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  489. \t\t\topacity: 0;
  490. \t\t\ttransform: scale(0.8);
  491. \t\t}
  492. \t\t.image-zoom-modal.active .close-zoom {
  493. \t\t\topacity: 1;
  494. \t\t\ttransform: scale(1);
  495. \t\t\ttransition-delay: 0.2s;
  496. \t\t}
  497. \t\t.close-zoom:hover {
  498. \t\t\tbackground: rgba(255, 162, 0, 0.9);
  499. \t\t\tborder-color: #ffa200;
  500. \t\t\ttransform: scale(1.1) rotate(90deg);
  501. \t\t\tbox-shadow: 0 4px 20px rgba(255, 162, 0, 0.4);
  502. \t\t}
  503. \t\t/* Boutons de navigation sur l'image principale */
  504. \t\t.main-image-nav {
  505. \t\t\tposition: absolute;
  506. \t\t\ttop: 0;
  507. \t\t\tbottom: 0;
  508. \t\t\tleft: 0;
  509. \t\t\tright: 0;
  510. \t\t\tdisplay: flex;
  511. \t\t\talign-items: center;
  512. \t\t\tjustify-content: space-between;
  513. \t\t\tpadding: 0 15px;
  514. \t\t\tpointer-events: none;
  515. \t\t\tz-index: 15;
  516. \t\t}
  517. \t\t.main-image-nav-btn {
  518. \t\t\tpointer-events: all;
  519. \t\t\twidth: 44px;
  520. \t\t\theight: 44px;
  521. \t\t\tborder-radius: 50%;
  522. \t\t\tbackground: rgba(255, 255, 255, 0.95);
  523. \t\t\tborder: 2px solid #e0e0e0;
  524. \t\t\tdisplay: flex;
  525. \t\t\talign-items: center;
  526. \t\t\tjustify-content: center;
  527. \t\t\tpadding: 0;
  528. \t\t\tbox-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
  529. \t\t\tcolor: #333;
  530. \t\t\ttransition: all 0.2s ease;
  531. \t\t}
  532. \t\t.main-image-nav-btn:hover {
  533. \t\t\tbackground: #ffa200;
  534. \t\t\tborder-color: #ffa200;
  535. \t\t\tcolor: #fff;
  536. \t\t\ttransform: scale(1.08);
  537. \t\t}
  538. \t\t.main-image-nav-btn:active {
  539. \t\t\ttransform: scale(0.95);
  540. \t\t}
  541. \t\t/* Modal personnalisé pour remplacer les alert */
  542. \t\t.custom-alert-modal .modal-content {
  543. \t\t\tborder-radius: 12px;
  544. \t\t\tborder: none;
  545. \t\t\tbox-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  546. \t\t}
  547. \t\t.custom-alert-modal .modal-header {
  548. \t\t\tborder-bottom: none;
  549. \t\t\tpadding: 1.5rem 1.5rem 0.5rem;
  550. \t\t\tbackground: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  551. \t\t\tcolor: white;
  552. \t\t\tborder-radius: 12px 12px 0 0;
  553. \t\t}
  554. \t\t.custom-alert-modal .modal-header .btn-close {
  555. \t\t\tfilter: brightness(0) invert(1);
  556. \t\t\topacity: 0.8;
  557. \t\t}
  558. \t\t.custom-alert-modal .modal-header .btn-close:hover {
  559. \t\t\topacity: 1;
  560. \t\t}
  561. \t\t.custom-alert-modal .modal-body {
  562. \t\t\tpadding: 1.5rem;
  563. \t\t\ttext-align: center;
  564. \t\t}
  565. \t\t.custom-alert-modal .modal-body .alert-icon {
  566. \t\t\tfont-size: 3rem;
  567. \t\t\tmargin-bottom: 1rem;
  568. \t\t}
  569. \t\t.custom-alert-modal .modal-body .alert-icon.success {
  570. \t\t\tcolor: #28a745;
  571. \t\t}
  572. \t\t.custom-alert-modal .modal-body .alert-icon.error {
  573. \t\t\tcolor: #dc3545;
  574. \t\t}
  575. \t\t.custom-alert-modal .modal-body .alert-icon.warning {
  576. \t\t\tcolor: #ffc107;
  577. \t\t}
  578. \t\t.custom-alert-modal .modal-body .alert-icon.info {
  579. \t\t\tcolor: #17a2b8;
  580. \t\t}
  581. \t\t.custom-alert-modal .modal-body .alert-message {
  582. \t\t\tfont-size: 1.1rem;
  583. \t\t\tcolor: #333;
  584. \t\t\tmargin-bottom: 1rem;
  585. \t\t\tline-height: 1.6;
  586. \t\t}
  587. \t\t.custom-alert-modal .modal-footer {
  588. \t\t\tborder-top: none;
  589. \t\t\tpadding: 0.5rem 1.5rem 1.5rem;
  590. \t\t\tjustify-content: center;
  591. \t\t}
  592. \t\t.custom-alert-modal .modal-footer .btn {
  593. \t\t\tmin-width: 120px;
  594. \t\t\tpadding: 0.6rem 1.5rem;
  595. \t\t\tborder-radius: 8px;
  596. \t\t\tfont-weight: 500;
  597. \t\t}
  598. \t\t/* Responsive pour single-product */
  599. \t\t@media(max-width: 991.98px) {
  600. \t\t\t.product-gallery-modern {
  601. \t\t\t\tflex-direction: column;
  602. \t\t\t}
  603. \t\t\t.thumbnails-container {
  604. \t\t\t\tflex-direction: row;
  605. \t\t\t\twidth: 100%;
  606. \t\t\t\toverflow-x: auto;
  607. \t\t\t\tpadding: 10px 0;
  608. \t\t\t}
  609. \t\t\t.thumbnail-item {
  610. \t\t\t\tflex-shrink: 0;
  611. \t\t\t}
  612. \t\t\t.main-image-container {
  613. \t\t\t\twidth: 100%;
  614. \t\t\t}
  615. \t\t}
  616. \t\t@media(max-width: 768px) {
  617. \t\t\t.product-gallery-modern {
  618. \t\t\t\tflex-direction: column;
  619. \t\t\t}
  620. \t\t\t.thumbnails-container {
  621. \t\t\t\twidth: 100%;
  622. \t\t\t\tflex-direction: row;
  623. \t\t\t\talign-items: center;
  624. \t\t\t}
  625. \t\t\t.product-thumbnails {
  626. \t\t\t\tflex-direction: row;
  627. \t\t\t\twidth: 100%;
  628. \t\t\t\tmax-height: 100px;
  629. \t\t\t\toverflow-x: auto;
  630. \t\t\t\toverflow-y: hidden;
  631. \t\t\t}
  632. \t\t\t.thumbnail-item {
  633. \t\t\t\tflex-shrink: 0;
  634. \t\t\t}
  635. \t\t\t.thumbnail-nav-btn {
  636. \t\t\t\tmargin: 0 5px;
  637. \t\t\t}
  638. \t\t\t.thumbnail-nav-up {
  639. \t\t\t\torder: 1;
  640. \t\t\t}
  641. \t\t\t.product-thumbnails {
  642. \t\t\t\torder: 2;
  643. \t\t\t}
  644. \t\t\t.thumbnail-nav-down {
  645. \t\t\t\torder: 3;
  646. \t\t\t}
  647. \t\t\t.image-overlay-icons {
  648. \t\t\t\topacity: 1;
  649. \t\t\t}
  650. \t\t}
  651. \t</style>
  652. ";
  653.         
  654.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  655.         
  656.         $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  657.         yield from [];
  658.     }
  659.     // line 657
  660.     /**
  661.      * @return iterable<null|scalar|\Stringable>
  662.      */
  663.     public function block_body(array $context, array $blocks = []): iterable
  664.     {
  665.         $macros $this->macros;
  666.         $__internal_5a27a8ba21ca79b61932376b2fa922d2 $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  667.         $__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block""body"));
  668.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  669.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block""body"));
  670.         // line 658
  671.         yield "\t<!-- Start Banner Area -->
  672. \t<section class=\"banner-area organic-breadcrumb\">
  673. \t\t<div class=\"container\">
  674. \t\t\t<div class=\"breadcrumb-banner d-flex flex-wrap align-items-center justify-content-end\">
  675. \t\t\t\t<div class=\"col-first\">
  676. \t\t\t\t\t<h1>Product Details Page</h1>
  677. \t\t\t\t\t<nav class=\"d-flex align-items-center\">
  678. \t\t\t\t\t\t<a href=\"index.html\">Home<span class=\"lnr lnr-arrow-right\"></span>
  679. \t\t\t\t\t\t</a>
  680. \t\t\t\t\t\t<a href=\"#\">Shop<span class=\"lnr lnr-arrow-right\"></span>
  681. \t\t\t\t\t\t</a>
  682. \t\t\t\t\t\t<a href=\"single-product.html\">product-details</a>
  683. \t\t\t\t\t</nav>
  684. \t\t\t\t</div>
  685. \t\t\t</div>
  686. \t\t</div>
  687. \t</section>
  688. \t<!-- End Banner Area -->
  689. \t<!--================Single Product Area =================-->
  690. \t<div class=\"product_image_area\">
  691. \t\t<div class=\"container\">
  692. \t\t\t<div class=\"row s_product_inner\">
  693. \t\t\t\t<div class=\"col-lg-6\">
  694. \t\t\t\t\t<div
  695. \t\t\t\t\t\tclass=\"product-gallery-modern\">
  696. \t\t\t\t\t\t<!-- Colonne de miniatures à gauche avec navigation -->
  697. \t\t\t\t\t\t<div class=\"thumbnails-container\">
  698. \t\t\t\t\t\t\t<button class=\"thumbnail-nav-btn thumbnail-nav-up\" id=\"thumbnail-nav-up\" title=\"Image précédente\">
  699. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-up\"></i>
  700. \t\t\t\t\t\t\t</button>
  701. \t\t\t\t\t\t\t<div class=\"product-thumbnails\" id=\"product-thumbnails\">
  702. \t\t\t\t\t\t\t\t<div class=\"thumbnails-wrapper\" id=\"thumbnails-wrapper\">
  703. \t\t\t\t\t\t\t\t\t";
  704.         // line 691
  705.         if ((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"truetruefalse691) && (Twig\Extension\CoreExtension::length($this->env->getCharset(), CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"falsefalsefalse691)) > 0))) {
  706.             // line 692
  707.             yield "\t\t\t\t\t\t\t\t\t\t";
  708.             $context['_parent'] = $context;
  709.             $context['_seq'] = CoreExtension::ensureTraversable(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"falsefalsefalse692));
  710.             $context['loop'] = [
  711.               'parent' => $context['_parent'],
  712.               'index0' => 0,
  713.               'index'  => 1,
  714.               'first'  => true,
  715.             ];
  716.             if (is_array($context['_seq']) || (is_object($context['_seq']) && $context['_seq'] instanceof \Countable)) {
  717.                 $length count($context['_seq']);
  718.                 $context['loop']['revindex0'] = $length 1;
  719.                 $context['loop']['revindex'] = $length;
  720.                 $context['loop']['length'] = $length;
  721.                 $context['loop']['last'] = === $length;
  722.             }
  723.             foreach ($context['_seq'] as $context["_key"] => $context["img"]) {
  724.                 // line 693
  725.                 yield "\t\t\t\t\t\t\t\t\t\t\t<div class=\"thumbnail-item ";
  726.                 if ((($tmp CoreExtension::getAttribute($this->env$this->source$context["loop"], "first", [], "any"falsefalsefalse693)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  727.                     yield "active permanently-active";
  728.                 }
  729.                 yield "\" data-image=\"";
  730.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\AssetExtension']->getAssetUrl($context["img"]), "html"nulltrue);
  731.                 yield "\" data-index=\"";
  732.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source$context["loop"], "index0", [], "any"falsefalsefalse693), "html"nulltrue);
  733.                 yield "\">
  734. \t\t\t\t\t\t\t\t\t\t\t\t<img src=\"";
  735.                 // line 694
  736.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\AssetExtension']->getAssetUrl($context["img"]), "html"nulltrue);
  737.                 yield "\" alt=\"";
  738.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "name", [], "any"falsefalsefalse694), "html"nulltrue);
  739.                 yield " - Image ";
  740.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source$context["loop"], "index", [], "any"falsefalsefalse694), "html"nulltrue);
  741.                 yield "\">
  742. \t\t\t\t\t\t\t\t\t\t\t</div>
  743. \t\t\t\t\t\t\t\t\t\t";
  744.                 ++$context['loop']['index0'];
  745.                 ++$context['loop']['index'];
  746.                 $context['loop']['first'] = false;
  747.                 if (isset($context['loop']['revindex0'], $context['loop']['revindex'])) {
  748.                     --$context['loop']['revindex0'];
  749.                     --$context['loop']['revindex'];
  750.                     $context['loop']['last'] = === $context['loop']['revindex0'];
  751.                 }
  752.             }
  753.             $_parent $context['_parent'];
  754.             unset($context['_seq'], $context['_key'], $context['img'], $context['_parent'], $context['loop']);
  755.             $context array_intersect_key($context$_parent) + $_parent;
  756.             // line 697
  757.             yield "\t\t\t\t\t\t\t\t\t";
  758.         } else {
  759.             // line 698
  760.             yield "\t\t\t\t\t\t\t\t\t\t<div class=\"thumbnail-item active permanently-active\" data-image=\"";
  761.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\AssetExtension']->getAssetUrl("ui/img/category/s-p1.jpg"), "html"nulltrue);
  762.             yield "\" data-index=\"0\">
  763. \t\t\t\t\t\t\t\t\t\t\t<img src=\"";
  764.             // line 699
  765.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\AssetExtension']->getAssetUrl("ui/img/category/s-p1.jpg"), "html"nulltrue);
  766.             yield "\" alt=\"";
  767.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "name", [], "any"falsefalsefalse699), "html"nulltrue);
  768.             yield "\">
  769. \t\t\t\t\t\t\t\t\t\t</div>
  770. \t\t\t\t\t\t\t\t\t";
  771.         }
  772.         // line 702
  773.         yield "\t\t\t\t\t\t\t\t</div>
  774. \t\t\t\t\t\t\t</div>
  775. \t\t\t\t\t\t\t<button class=\"thumbnail-nav-btn thumbnail-nav-down\" id=\"thumbnail-nav-down\" title=\"Image suivante\">
  776. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-down\"></i>
  777. \t\t\t\t\t\t\t</button>
  778. \t\t\t\t\t\t</div>
  779. \t\t\t\t\t\t<!-- Grande image principale -->
  780. \t\t\t\t\t\t<div class=\"product-main-image-wrapper\">
  781. \t\t\t\t\t\t\t<div class=\"product-main-image\">
  782. \t\t\t\t\t\t\t\t";
  783.         // line 712
  784.         if ((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"truetruefalse712) && (Twig\Extension\CoreExtension::length($this->env->getCharset(), CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"falsefalsefalse712)) > 0))) {
  785.             // line 713
  786.             yield "\t\t\t\t\t\t\t\t\t<img id=\"main-product-image\" src=\"";
  787.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\AssetExtension']->getAssetUrl((($_v0 CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"falsefalsefalse713)) && is_array($_v0) || $_v0 instanceof ArrayAccess ? ($_v0[0] ?? null) : null)), "html"nulltrue);
  788.             yield "\" alt=\"";
  789.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "name", [], "any"falsefalsefalse713), "html"nulltrue);
  790.             yield "\">
  791. \t\t\t\t\t\t\t\t";
  792.         } else {
  793.             // line 715
  794.             yield "\t\t\t\t\t\t\t\t\t<img id=\"main-product-image\" src=\"";
  795.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\AssetExtension']->getAssetUrl("ui/img/category/s-p1.jpg"), "html"nulltrue);
  796.             yield "\" alt=\"";
  797.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "name", [], "any"falsefalsefalse715), "html"nulltrue);
  798.             yield "\">
  799. \t\t\t\t\t\t\t\t";
  800.         }
  801.         // line 717
  802.         yield "\t\t\t\t\t\t\t</div>
  803. \t\t\t\t\t\t\t<!-- Overlay avec icônes -->
  804. \t\t\t\t\t\t\t<div class=\"image-overlay-icons\">
  805. \t\t\t\t\t\t\t\t<button class=\"icon-btn zoom-btn\" onclick=\"openImageZoom()\" title=\"Agrandir l'image\">
  806. \t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-magnifier\"></i>
  807. \t\t\t\t\t\t\t\t</button>
  808. \t\t\t\t\t\t\t\t<button class=\"icon-btn favorite-btn\" ";
  809.         // line 724
  810.         if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["app"] ?? null), "user", [], "any"falsefalsefalse724)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  811.             yield " onclick=\"toggleWishlist(";
  812.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "id", [], "any"falsefalsefalse724), "html"nulltrue);
  813.             yield ", this); return false;\" ";
  814.         } else {
  815.             yield " onclick=\"showCustomAlert('Vous devez être connecté pour ajouter aux favoris', 'warning'); return false;\" ";
  816.         }
  817.         yield " title=\"Ajouter aux favoris\" data-product-id=\"";
  818.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "id", [], "any"falsefalsefalse724), "html"nulltrue);
  819.         yield "\">
  820. \t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-heart\"></i>
  821. \t\t\t\t\t\t\t\t</button>
  822. \t\t\t\t\t\t\t</div>
  823. \t\t\t\t\t\t\t<!-- Navigation sur l'image principale -->
  824. \t\t\t\t\t\t\t<div class=\"main-image-nav\">
  825. \t\t\t\t\t\t\t\t<button type=\"button\" class=\"main-image-nav-btn\" id=\"main-image-prev\" title=\"Image précédente\">
  826. \t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-left\"></i>
  827. \t\t\t\t\t\t\t\t</button>
  828. \t\t\t\t\t\t\t\t<button type=\"button\" class=\"main-image-nav-btn\" id=\"main-image-next\" title=\"Image suivante\">
  829. \t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-right\"></i>
  830. \t\t\t\t\t\t\t\t</button>
  831. \t\t\t\t\t\t\t</div>
  832. \t\t\t\t\t\t\t<!-- Indicateur d'image active -->
  833. \t\t\t\t\t\t\t<div class=\"image-counter\">
  834. \t\t\t\t\t\t\t\t<span id=\"current-image-index\">1</span>
  835. \t\t\t\t\t\t\t\t/
  836. \t\t\t\t\t\t\t\t<span id=\"total-images\">";
  837.         // line 743
  838.         yield ((Twig\Extension\CoreExtension::length($this->env->getCharset(), CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"falsefalsefalse743))) ? ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(Twig\Extension\CoreExtension::length($this->env->getCharset(), CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"falsefalsefalse743)), "html"nulltrue)) : (1));
  839.         yield "</span>
  840. \t\t\t\t\t\t\t</div>
  841. \t\t\t\t\t\t</div>
  842. \t\t\t\t\t</div>
  843. \t\t\t\t</div>
  844. \t\t\t\t<div class=\"col-lg-5 offset-lg-1\">
  845. \t\t\t\t\t<div class=\"s_product_text\">
  846. \t\t\t\t\t\t<h3>";
  847.         // line 750
  848.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "name", [], "any"falsefalsefalse750), "html"nulltrue);
  849.         yield "</h3>
  850. \t\t\t\t\t\t<h2>
  851. \t\t\t\t\t\t\t<span id=\"main-unit-price\">";
  852.         // line 752
  853.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatNumber(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "price", [], "any"falsefalsefalse752), 2"."" "), "html"nulltrue);
  854.         yield "</span>
  855. \t\t\t\t\t\t\tHTG
  856. \t\t\t\t\t\t</h2>
  857. \t\t\t\t\t\t<ul class=\"list\">
  858. \t\t\t\t\t\t\t<li>
  859. \t\t\t\t\t\t\t\t<a class=\"active\" href=\"#\">
  860. \t\t\t\t\t\t\t\t\t<span>Category</span>
  861. \t\t\t\t\t\t\t\t\t:
  862. \t\t\t\t\t\t\t\t\t";
  863.         // line 760
  864.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->sourceCoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "category", [], "any"falsefalsefalse760), "name", [], "any"falsefalsefalse760), "html"nulltrue);
  865.         yield "</a>
  866. \t\t\t\t\t\t\t</li>
  867. \t\t\t\t\t\t\t<li>
  868. \t\t\t\t\t\t\t\t<a href=\"#\">
  869. \t\t\t\t\t\t\t\t\t<span>Disponibilité</span>
  870. \t\t\t\t\t\t\t\t\t:
  871. \t\t\t\t\t\t\t\t\t";
  872.         // line 766
  873.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stockStatus", [], "any"falsefalsefalse766), "html"nulltrue);
  874.         yield "</a>
  875. \t\t\t\t\t\t\t</li>
  876. \t\t\t\t\t\t</ul>
  877. \t\t\t\t\t\t<p>";
  878.         // line 769
  879.         yield Twig\Extension\CoreExtension::nl2br($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(Twig\Extension\CoreExtension::striptags(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "description", [], "any"falsefalsefalse769)), "html"nulltrue));
  880.         yield "</p>
  881. \t\t\t\t\t\t<!-- Informations sur la boutique -->
  882. \t\t\t\t\t\t<div class=\"shop-info mb-3\">
  883. \t\t\t\t\t\t\t<small class=\"text-muted\">
  884. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-store\"></i>
  885. \t\t\t\t\t\t\t\tVendu par :
  886. \t\t\t\t\t\t\t\t";
  887.         // line 776
  888.         if ((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "shop", [], "any"truetruefalse776) && CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "shop", [], "any"falsefalsefalse776))) {
  889.             // line 777
  890.             yield "\t\t\t\t\t\t\t\t\t<a href=\"";
  891.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("ui_shop_show", ["slug" => CoreExtension::getAttribute($this->env$this->sourceCoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "shop", [], "any"falsefalsefalse777), "slug", [], "any"falsefalsefalse777)]), "html"nulltrue);
  892.             yield "\" class=\"shop-link\">
  893. \t\t\t\t\t\t\t\t\t\t";
  894.             // line 778
  895.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->sourceCoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "shop", [], "any"falsefalsefalse778), "name", [], "any"falsefalsefalse778), "html"nulltrue);
  896.             yield "
  897. \t\t\t\t\t\t\t\t\t</a>
  898. \t\t\t\t\t\t\t\t";
  899.         } else {
  900.             // line 781
  901.             yield "\t\t\t\t\t\t\t\t\t<span class=\"text-muted\">Boutique inconnue</span>
  902. \t\t\t\t\t\t\t\t";
  903.         }
  904.         // line 783
  905.         yield "\t\t\t\t\t\t\t</small>
  906. \t\t\t\t\t\t</div>
  907. \t\t\t\t\t\t<div class=\"product_count\">
  908. \t\t\t\t\t\t\t<label for=\"qty\">Quantité :</label>
  909. \t\t\t\t\t\t\t<input type=\"text\" name=\"qty\" id=\"sst\" maxlength=\"12\" value=\"1\" title=\"Quantité:\" class=\"input-text qty\" max=\"";
  910.         // line 788
  911.         yield (((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"truetruefalse788) &&  !(null === CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"falsefalsefalse788)))) ? ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"falsefalsefalse788), "html"nulltrue)) : (99));
  912.         yield "\">
  913. \t\t\t\t\t\t\t<button onclick=\"var result = document.getElementById('sst'); var sst = result.value; if( !isNaN( sst ) && sst < ";
  914.         // line 789
  915.         yield (((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"truetruefalse789) &&  !(null === CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"falsefalsefalse789)))) ? ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"falsefalsefalse789), "html"nulltrue)) : (99));
  916.         yield ") result.value++;return false;\" class=\"increase items-count\" type=\"button\">
  917. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-up\"></i>
  918. \t\t\t\t\t\t\t</button>
  919. \t\t\t\t\t\t\t<button onclick=\"var result = document.getElementById('sst'); var sst = result.value; if( !isNaN( sst ) && sst > 1 ) result.value--;return false;\" class=\"reduced items-count\" type=\"button\">
  920. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-down\"></i>
  921. \t\t\t\t\t\t\t</button>
  922. \t\t\t\t\t\t</div>
  923. \t\t\t\t\t\t<input
  924. \t\t\t\t\t\ttype=\"hidden\" id=\"unit-price-input\" value=\"";
  925.         // line 798
  926.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatNumber(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "price", [], "any"falsefalsefalse798), 2"."""), "html"nulltrue);
  927.         yield "\"/>
  928. \t\t\t\t\t\t";
  929.         // line 801
  930.         yield "\t\t\t\t\t\t";
  931.         $context["tierPrices"] = [];
  932.         // line 802
  933.         yield "\t\t\t\t\t\t";
  934.         if ((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "tierPrices", [], "any"truetruefalse802) && (Twig\Extension\CoreExtension::length($this->env->getCharset(), CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "tierPrices", [], "any"falsefalsefalse802)) > 0))) {
  935.             // line 803
  936.             yield "\t\t\t\t\t\t\t";
  937.             $context['_parent'] = $context;
  938.             $context['_seq'] = CoreExtension::ensureTraversable(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "tierPrices", [], "any"falsefalsefalse803));
  939.             foreach ($context['_seq'] as $context["_key"] => $context["tier"]) {
  940.                 // line 804
  941.                 yield "\t\t\t\t\t\t\t\t";
  942.                 $context["tierPrices"] = Twig\Extension\CoreExtension::merge(($context["tierPrices"] ?? null), [["min" => CoreExtension::getAttribute($this->env$this->source$context["tier"], "minQty", [], "any"falsefalsefalse804), "price" => CoreExtension::getAttribute($this->env$this->source$context["tier"], "price", [], "any"falsefalsefalse804)]]);
  943.                 // line 805
  944.                 yield "\t\t\t\t\t\t\t";
  945.             }
  946.             $_parent $context['_parent'];
  947.             unset($context['_seq'], $context['_key'], $context['tier'], $context['_parent']);
  948.             $context array_intersect_key($context$_parent) + $_parent;
  949.             // line 806
  950.             yield "\t\t\t\t\t\t";
  951.         } else {
  952.             // line 807
  953.             yield "\t\t\t\t\t\t\t";
  954.             // line 808
  955.             yield "\t\t\t\t\t\t\t";
  956.             $context["tierPrices"] = [["min" => 1"price" => CoreExtension::getAttribute($this->env$this->source,             // line 809
  957. ($context["product"] ?? null), "price", [], "any"falsefalsefalse809)], ["min" => 5"price" => Twig\Extension\CoreExtension::round((CoreExtension::getAttribute($this->env$this->source,             // line 810
  958. ($context["product"] ?? null), "price", [], "any"falsefalsefalse810) * 0.98), 2"floor")], ["min" => 10"price" => Twig\Extension\CoreExtension::round((CoreExtension::getAttribute($this->env$this->source,             // line 811
  959. ($context["product"] ?? null), "price", [], "any"falsefalsefalse811) * 0.95), 2"floor")], ["min" => 20"price" => Twig\Extension\CoreExtension::round((CoreExtension::getAttribute($this->env$this->source,             // line 812
  960. ($context["product"] ?? null), "price", [], "any"falsefalsefalse812) * 0.92), 2"floor")]];
  961.             // line 814
  962.             yield "\t\t\t\t\t\t";
  963.         }
  964.         // line 815
  965.         yield "
  966. \t\t\t\t\t\t<div id=\"bulk-pricing\" class=\"mt-3\" data-tiers='";
  967.         // line 816
  968.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(json_encode(($context["tierPrices"] ?? null)), "html_attr");
  969.         yield "'>
  970. \t\t\t\t\t\t\t<div class=\"d-flex align-items-center mb-2\">
  971. \t\t\t\t\t\t\t\t<strong class=\"me-2\">Prix unitaire selon quantité :</strong>
  972. \t\t\t\t\t\t\t\t<span id=\"unit-price-value\">";
  973.         // line 819
  974.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatNumber(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "price", [], "any"falsefalsefalse819), 2"."" "), "html"nulltrue);
  975.         yield "
  976. \t\t\t\t\t\t\t\t\tHTG</span>
  977. \t\t\t\t\t\t\t</div>
  978. \t\t\t\t\t\t\t<div class=\"table-responsive\">
  979. \t\t\t\t\t\t\t\t<table class=\"table table-sm mb-2\" style=\"border:1px solid #eee;\">
  980. \t\t\t\t\t\t\t\t\t<thead class=\"table-light\">
  981. \t\t\t\t\t\t\t\t\t\t<tr>
  982. \t\t\t\t\t\t\t\t\t\t\t<th>Quantité min.</th>
  983. \t\t\t\t\t\t\t\t\t\t\t<th>Prix unitaire (HTG)</th>
  984. \t\t\t\t\t\t\t\t\t\t</tr>
  985. \t\t\t\t\t\t\t\t\t</thead>
  986. \t\t\t\t\t\t\t\t\t<tbody id=\"bulk-tier-rows\">
  987. \t\t\t\t\t\t\t\t\t\t";
  988.         // line 831
  989.         $context['_parent'] = $context;
  990.         $context['_seq'] = CoreExtension::ensureTraversable(Twig\Extension\CoreExtension::sort($this->env, ($context["tierPrices"] ?? null), function ($__a__$__b__) use ($context$macros) { $context["a"] = $__a__$context["b"] = $__b__; return (CoreExtension::getAttribute($this->env$this->source, ($context["a"] ?? null), "min", [], "any"falsefalsefalse831) <=> CoreExtension::getAttribute($this->env$this->source, ($context["b"] ?? null), "min", [], "any"falsefalsefalse831)); }));
  991.         foreach ($context['_seq'] as $context["_key"] => $context["tier"]) {
  992.             // line 832
  993.             yield "\t\t\t\t\t\t\t\t\t\t\t<tr data-min=\"";
  994.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source$context["tier"], "min", [], "any"falsefalsefalse832), "html"nulltrue);
  995.             yield "\">
  996. \t\t\t\t\t\t\t\t\t\t\t\t<td>≥
  997. \t\t\t\t\t\t\t\t\t\t\t\t\t";
  998.             // line 834
  999.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source$context["tier"], "min", [], "any"falsefalsefalse834), "html"nulltrue);
  1000.             yield "</td>
  1001. \t\t\t\t\t\t\t\t\t\t\t\t<td>";
  1002.             // line 835
  1003.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatNumber(CoreExtension::getAttribute($this->env$this->source$context["tier"], "price", [], "any"falsefalsefalse835), 2"."" "), "html"nulltrue);
  1004.             yield "</td>
  1005. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1006. \t\t\t\t\t\t\t\t\t\t";
  1007.         }
  1008.         $_parent $context['_parent'];
  1009.         unset($context['_seq'], $context['_key'], $context['tier'], $context['_parent']);
  1010.         $context array_intersect_key($context$_parent) + $_parent;
  1011.         // line 838
  1012.         yield "\t\t\t\t\t\t\t\t\t</tbody>
  1013. \t\t\t\t\t\t\t\t</table>
  1014. \t\t\t\t\t\t\t</div>
  1015. \t\t\t\t\t\t\t<div class=\"d-flex flex-wrap gap-3 align-items-baseline mb-2\">
  1016. \t\t\t\t\t\t\t\t<div>
  1017. \t\t\t\t\t\t\t\t\t<strong>Total:</strong>
  1018. \t\t\t\t\t\t\t\t\t<span id=\"total-price-value\">";
  1019.         // line 844
  1020.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatNumber(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "price", [], "any"falsefalsefalse844), 2"."" "), "html"nulltrue);
  1021.         yield "</span>
  1022. \t\t\t\t\t\t\t\t\tHTG
  1023. \t\t\t\t\t\t\t\t</div>
  1024. \t\t\t\t\t\t\t\t<div class=\"text-success\">
  1025. \t\t\t\t\t\t\t\t\t<strong>Économies:</strong>
  1026. \t\t\t\t\t\t\t\t\t<span id=\"savings-amount\">0.00</span>
  1027. \t\t\t\t\t\t\t\t\tHTG
  1028. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(<span id=\"savings-percent\">0</span>%)
  1029. \t\t\t\t\t\t\t\t</div>
  1030. \t\t\t\t\t\t\t</div>
  1031. \t\t\t\t\t\t\t<small class=\"text-muted\">Le prix et le total s’adaptent automatiquement selon la quantité.</small>
  1032. \t\t\t\t\t\t</div>
  1033. \t\t\t\t\t\t<!-- Stock disponible -->
  1034. \t\t\t\t\t\t";
  1035.         // line 858
  1036.         if (CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"truetruefalse858)) {
  1037.             // line 859
  1038.             yield "\t\t\t\t\t\t\t<small class=\"text-muted\">Stock disponible :
  1039. \t\t\t\t\t\t\t\t";
  1040.             // line 860
  1041.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"falsefalsefalse860), "html"nulltrue);
  1042.             yield "
  1043. \t\t\t\t\t\t\t\tunités</small>
  1044. \t\t\t\t\t\t";
  1045.         }
  1046.         // line 863
  1047.         yield "
  1048. \t\t\t\t\t\t<!-- Statistiques du produit -->
  1049. \t\t\t\t\t\t";
  1050.         // line 865
  1051.         if (array_key_exists("productStats"$context)) {
  1052.             // line 866
  1053.             yield "\t\t\t\t\t\t\t<div class=\"product-stats mt-3\">
  1054. \t\t\t\t\t\t\t\t<div class=\"row text-center\">
  1055. \t\t\t\t\t\t\t\t\t<div class=\"col-4\">
  1056. \t\t\t\t\t\t\t\t\t\t<div class=\"stat-item\">
  1057. \t\t\t\t\t\t\t\t\t\t\t<span class=\"stat-number\">";
  1058.             // line 870
  1059.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["productStats"] ?? null), "totalViews", [], "any"falsefalsefalse870), "html"nulltrue);
  1060.             yield "</span>
  1061. \t\t\t\t\t\t\t\t\t\t\t<small class=\"stat-label\">Vues</small>
  1062. \t\t\t\t\t\t\t\t\t\t</div>
  1063. \t\t\t\t\t\t\t\t\t</div>
  1064. \t\t\t\t\t\t\t\t\t<div class=\"col-4\">
  1065. \t\t\t\t\t\t\t\t\t\t<div class=\"stat-item\">
  1066. \t\t\t\t\t\t\t\t\t\t\t<span class=\"stat-number\">";
  1067.             // line 876
  1068.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["productStats"] ?? null), "salesCount", [], "any"falsefalsefalse876), "html"nulltrue);
  1069.             yield "</span>
  1070. \t\t\t\t\t\t\t\t\t\t\t<small class=\"stat-label\">Ventes</small>
  1071. \t\t\t\t\t\t\t\t\t\t</div>
  1072. \t\t\t\t\t\t\t\t\t</div>
  1073. \t\t\t\t\t\t\t\t\t<div class=\"col-4\">
  1074. \t\t\t\t\t\t\t\t\t\t<div class=\"stat-item\">
  1075. \t\t\t\t\t\t\t\t\t\t\t<span class=\"stat-number\">";
  1076.             // line 882
  1077.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["productStats"] ?? null), "conversionRate", [], "any"falsefalsefalse882), "html"nulltrue);
  1078.             yield "%</span>
  1079. \t\t\t\t\t\t\t\t\t\t\t<small class=\"stat-label\">Conversion</small>
  1080. \t\t\t\t\t\t\t\t\t\t</div>
  1081. \t\t\t\t\t\t\t\t\t</div>
  1082. \t\t\t\t\t\t\t\t</div>
  1083. \t\t\t\t\t\t\t</div>
  1084. \t\t\t\t\t\t";
  1085.         }
  1086.         // line 889
  1087.         yield "
  1088. \t\t\t\t\t\t<div class=\"card_area d-flex align-items-center\">
  1089. \t\t\t\t\t\t\t<a class=\"primary-btn btn-add-to-cart\" href=\"javascript:void(0);\" id=\"add-to-cart-btn\" data-product-id=\"";
  1090.         // line 891
  1091.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "id", [], "any"falsefalsefalse891), "html"nulltrue);
  1092.         yield "\" data-qty=\"1\">
  1093. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-cart\"></i>
  1094. \t\t\t\t\t\t\t\tAjouter au panier
  1095. \t\t\t\t\t\t\t</a>
  1096. \t\t\t\t\t\t\t<a class=\"icon_btn wishlist-btn\" href=\"#\" title=\"Ajouter aux favoris\" data-product-id=\"";
  1097.         // line 895
  1098.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "id", [], "any"falsefalsefalse895), "html"nulltrue);
  1099.         yield "\" ";
  1100.         if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["app"] ?? null), "user", [], "any"falsefalsefalse895)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1101.             yield " onclick=\"toggleWishlist(";
  1102.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "id", [], "any"falsefalsefalse895), "html"nulltrue);
  1103.             yield ", this); return false;\" ";
  1104.         } else {
  1105.             yield " onclick=\"showCustomAlert('Vous devez être connecté pour ajouter aux favoris', 'warning'); return false;\" ";
  1106.         }
  1107.         yield ">
  1108. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-heart\"></i>
  1109. \t\t\t\t\t\t\t</a>
  1110. \t\t\t\t\t\t\t<a class=\"icon_btn comparison-btn\" href=\"#\" title=\"Comparer\" data-product-id=\"";
  1111.         // line 898
  1112.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "id", [], "any"falsefalsefalse898), "html"nulltrue);
  1113.         yield "\" onclick=\"toggleComparison(";
  1114.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "id", [], "any"falsefalsefalse898), "html"nulltrue);
  1115.         yield ", this); return false;\">
  1116. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-sync\"></i>
  1117. \t\t\t\t\t\t\t</a>
  1118. \t\t\t\t\t\t</div>
  1119. \t\t\t\t\t\t<!-- Message de confirmation -->
  1120. \t\t\t\t\t\t<div id=\"cart-message\" class=\"alert alert-success mt-3\" style=\"display: none;\">
  1121. \t\t\t\t\t\t\t<i class=\"lnr lnr-checkmark-circle\"></i>
  1122. \t\t\t\t\t\t\tProduit ajouté au panier avec succès !
  1123. \t\t\t\t\t\t</div>
  1124. \t\t\t\t\t\t<!-- ... existing code ... -->
  1125. \t\t\t\t\t</div>
  1126. \t\t\t\t</div>
  1127. \t\t\t</div>
  1128. \t\t</div>
  1129. \t</div>
  1130. \t<!--================End Single Product Area =================-->
  1131. \t<!--================Product Description Area =================-->
  1132. \t<section class=\"product_description_area\">
  1133. \t\t<div class=\"container\">
  1134. \t\t\t<ul class=\"nav nav-tabs\" id=\"myTab\" role=\"tablist\">
  1135. \t\t\t\t<li class=\"nav-item\">
  1136. \t\t\t\t\t<a class=\"nav-link active\" id=\"home-tab\" data-bs-toggle=\"tab\" href=\"#home\" role=\"tab\" aria-controls=\"home\" aria-selected=\"true\">Description</a>
  1137. \t\t\t\t</li>
  1138. \t\t\t\t<li class=\"nav-item\">
  1139. \t\t\t\t\t<a class=\"nav-link\" id=\"profile-tab\" data-bs-toggle=\"tab\" href=\"#profile\" role=\"tab\" aria-controls=\"profile\" aria-selected=\"false\">Spécifications</a>
  1140. \t\t\t\t</li>
  1141. \t\t\t\t<li class=\"nav-item\">
  1142. \t\t\t\t\t<a class=\"nav-link\" id=\"contact-tab\" data-bs-toggle=\"tab\" href=\"#contact\" role=\"tab\" aria-controls=\"contact\" aria-selected=\"false\">Commentaires</a>
  1143. \t\t\t\t</li>
  1144. \t\t\t\t<li class=\"nav-item\">
  1145. \t\t\t\t\t<a class=\"nav-link\" id=\"review-tab\" data-bs-toggle=\"tab\" href=\"#review\" role=\"tab\" aria-controls=\"review\" aria-selected=\"false\">Avis (";
  1146.         // line 930
  1147.         yield (((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"truetruefalse930) &&  !(null === CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"falsefalsefalse930)))) ? ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"falsefalsefalse930), "html"nulltrue)) : (0));
  1148.         yield ")</a>
  1149. \t\t\t\t</li>
  1150. \t\t\t</ul>
  1151. \t\t\t<div class=\"tab-content\" id=\"myTabContent\">
  1152. \t\t\t\t<div class=\"tab-pane fade show active\" id=\"home\" role=\"tabpanel\" aria-labelledby=\"home-tab\">
  1153. \t\t\t\t\t<div class=\"description\">
  1154. \t\t\t\t\t\t";
  1155.         // line 936
  1156.         if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "description", [], "any"falsefalsefalse936)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1157.             // line 937
  1158.             yield "\t\t\t\t\t\t\t<div class=\"product-description-content\">
  1159. \t\t\t\t\t\t\t\t";
  1160.             // line 938
  1161.             yield Twig\Extension\CoreExtension::nl2br(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "description", [], "any"falsefalsefalse938));
  1162.             yield "
  1163. \t\t\t\t\t\t\t</div>
  1164. \t\t\t\t\t\t";
  1165.         } else {
  1166.             // line 941
  1167.             yield "\t\t\t\t\t\t\t<p class=\"text-muted\">Aucune description disponible pour ce produit.</p>
  1168. \t\t\t\t\t\t";
  1169.         }
  1170.         // line 943
  1171.         yield "\t\t\t\t\t</div>
  1172. \t\t\t\t</div>
  1173. \t\t\t\t<div class=\"tab-pane fade\" id=\"profile\" role=\"tabpanel\" aria-labelledby=\"profile-tab\">
  1174. \t\t\t\t\t<div class=\"specification-table\">
  1175. \t\t\t\t\t\t";
  1176.         // line 947
  1177.         if (((((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "weight", [], "any"falsefalsefalse947) || CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "length", [], "any"falsefalsefalse947)) || CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "width", [], "any"falsefalsefalse947)) || CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "height", [], "any"falsefalsefalse947)) || CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "attributes", [], "any"falsefalsefalse947))) {
  1178.             // line 948
  1179.             yield "\t\t\t\t\t\t\t<div class=\"table-responsive\">
  1180. \t\t\t\t\t\t\t\t<table class=\"table\">
  1181. \t\t\t\t\t\t\t\t\t<tbody>
  1182. \t\t\t\t\t\t\t\t\t\t";
  1183.             // line 951
  1184.             if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "sku", [], "any"falsefalsefalse951)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1185.                 // line 952
  1186.                 yield "\t\t\t\t\t\t\t\t\t\t\t<tr>
  1187. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1188. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Référence (SKU)</h5>
  1189. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1190. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1191. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>";
  1192.                 // line 957
  1193.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "sku", [], "any"falsefalsefalse957), "html"nulltrue);
  1194.                 yield "</h5>
  1195. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1196. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1197. \t\t\t\t\t\t\t\t\t\t";
  1198.             }
  1199.             // line 961
  1200.             yield "\t\t\t\t\t\t\t\t\t\t";
  1201.             if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "barcode", [], "any"falsefalsefalse961)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1202.                 // line 962
  1203.                 yield "\t\t\t\t\t\t\t\t\t\t\t<tr>
  1204. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1205. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Code-barres</h5>
  1206. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1207. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1208. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>";
  1209.                 // line 967
  1210.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "barcode", [], "any"falsefalsefalse967), "html"nulltrue);
  1211.                 yield "</h5>
  1212. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1213. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1214. \t\t\t\t\t\t\t\t\t\t";
  1215.             }
  1216.             // line 971
  1217.             yield "\t\t\t\t\t\t\t\t\t\t";
  1218.             if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "brand", [], "any"falsefalsefalse971)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1219.                 // line 972
  1220.                 yield "\t\t\t\t\t\t\t\t\t\t\t<tr>
  1221. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1222. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Marque</h5>
  1223. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1224. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1225. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>";
  1226.                 // line 977
  1227.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->sourceCoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "brand", [], "any"falsefalsefalse977), "name", [], "any"falsefalsefalse977), "html"nulltrue);
  1228.                 yield "</h5>
  1229. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1230. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1231. \t\t\t\t\t\t\t\t\t\t";
  1232.             }
  1233.             // line 981
  1234.             yield "\t\t\t\t\t\t\t\t\t\t";
  1235.             if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "category", [], "any"falsefalsefalse981)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1236.                 // line 982
  1237.                 yield "\t\t\t\t\t\t\t\t\t\t\t<tr>
  1238. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1239. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Catégorie</h5>
  1240. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1241. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1242. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>";
  1243.                 // line 987
  1244.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->sourceCoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "category", [], "any"falsefalsefalse987), "name", [], "any"falsefalsefalse987), "html"nulltrue);
  1245.                 yield "</h5>
  1246. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1247. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1248. \t\t\t\t\t\t\t\t\t\t";
  1249.             }
  1250.             // line 991
  1251.             yield "\t\t\t\t\t\t\t\t\t\t";
  1252.             if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "weight", [], "any"falsefalsefalse991)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1253.                 // line 992
  1254.                 yield "\t\t\t\t\t\t\t\t\t\t\t<tr>
  1255. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1256. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Poids</h5>
  1257. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1258. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1259. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>";
  1260.                 // line 997
  1261.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "weight", [], "any"falsefalsefalse997), "html"nulltrue);
  1262.                 yield "
  1263. \t\t\t\t\t\t\t\t\t\t\t\t\t\tkg</h5>
  1264. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1265. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1266. \t\t\t\t\t\t\t\t\t\t";
  1267.             }
  1268.             // line 1002
  1269.             yield "\t\t\t\t\t\t\t\t\t\t";
  1270.             if (((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "length", [], "any"falsefalsefalse1002) || CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "width", [], "any"falsefalsefalse1002)) || CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "height", [], "any"falsefalsefalse1002))) {
  1271.                 // line 1003
  1272.                 yield "\t\t\t\t\t\t\t\t\t\t\t<tr>
  1273. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1274. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Dimensions</h5>
  1275. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1276. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1277. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>
  1278. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1279.                 // line 1009
  1280.                 if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "length", [], "any"falsefalsefalse1009)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1281.                     // line 1010
  1282.                     yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1283.                     yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "length", [], "any"falsefalsefalse1010), "html"nulltrue);
  1284.                     yield "cm
  1285. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1286.                 }
  1287.                 // line 1012
  1288.                 yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1289.                 if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "width", [], "any"falsefalsefalse1012)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1290.                     // line 1013
  1291.                     yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t×
  1292. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1293.                     // line 1014
  1294.                     yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "width", [], "any"falsefalsefalse1014), "html"nulltrue);
  1295.                     yield "cm
  1296. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1297.                 }
  1298.                 // line 1016
  1299.                 yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1300.                 if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "height", [], "any"falsefalsefalse1016)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1301.                     // line 1017
  1302.                     yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t×
  1303. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1304.                     // line 1018
  1305.                     yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "height", [], "any"falsefalsefalse1018), "html"nulltrue);
  1306.                     yield "cm
  1307. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1308.                 }
  1309.                 // line 1020
  1310.                 yield "\t\t\t\t\t\t\t\t\t\t\t\t\t</h5>
  1311. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1312. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1313. \t\t\t\t\t\t\t\t\t\t";
  1314.             }
  1315.             // line 1024
  1316.             yield "\t\t\t\t\t\t\t\t\t\t";
  1317.             if ((($tmp =  !(null === CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"falsefalsefalse1024))) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1318.                 // line 1025
  1319.                 yield "\t\t\t\t\t\t\t\t\t\t\t<tr>
  1320. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1321. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Stock disponible</h5>
  1322. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1323. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1324. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>";
  1325.                 // line 1030
  1326.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stock", [], "any"falsefalsefalse1030), "html"nulltrue);
  1327.                 yield "
  1328. \t\t\t\t\t\t\t\t\t\t\t\t\t\tunité(s)</h5>
  1329. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1330. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1331. \t\t\t\t\t\t\t\t\t\t";
  1332.             }
  1333.             // line 1035
  1334.             yield "\t\t\t\t\t\t\t\t\t\t";
  1335.             if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stockStatus", [], "any"falsefalsefalse1035)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1336.                 // line 1036
  1337.                 yield "\t\t\t\t\t\t\t\t\t\t\t<tr>
  1338. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1339. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Statut</h5>
  1340. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1341. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1342. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>
  1343. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1344.                 // line 1042
  1345.                 if ((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stockStatus", [], "any"falsefalsefalse1042) == "in_stock")) {
  1346.                     // line 1043
  1347.                     yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"badge bg-success\">En stock</span>
  1348. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1349.                 } elseif ((CoreExtension::getAttribute($this->env$this->source,                 // line 1044
  1350. ($context["product"] ?? null), "stockStatus", [], "any"falsefalsefalse1044) == "out_of_stock")) {
  1351.                     // line 1045
  1352.                     yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"badge bg-danger\">Rupture de stock</span>
  1353. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1354.                 } elseif ((CoreExtension::getAttribute($this->env$this->source,                 // line 1046
  1355. ($context["product"] ?? null), "stockStatus", [], "any"falsefalsefalse1046) == "backorder")) {
  1356.                     // line 1047
  1357.                     yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"badge bg-warning\">Sur commande</span>
  1358. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1359.                 } else {
  1360.                     // line 1049
  1361.                     yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1362.                     yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "stockStatus", [], "any"falsefalsefalse1049), "html"nulltrue);
  1363.                     yield "
  1364. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1365.                 }
  1366.                 // line 1051
  1367.                 yield "\t\t\t\t\t\t\t\t\t\t\t\t\t</h5>
  1368. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1369. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1370. \t\t\t\t\t\t\t\t\t\t";
  1371.             }
  1372.             // line 1055
  1373.             yield "\t\t\t\t\t\t\t\t\t\t";
  1374.             if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "isDigital", [], "any"falsefalsefalse1055)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1375.                 // line 1056
  1376.                 yield "\t\t\t\t\t\t\t\t\t\t\t<tr>
  1377. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1378. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Type</h5>
  1379. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1380. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  1381. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>
  1382. \t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"badge bg-info\">Produit digital</span>
  1383. \t\t\t\t\t\t\t\t\t\t\t\t\t</h5>
  1384. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  1385. \t\t\t\t\t\t\t\t\t\t\t</tr>
  1386. \t\t\t\t\t\t\t\t\t\t";
  1387.             }
  1388.             // line 1067
  1389.             yield "\t\t\t\t\t\t\t\t\t\t";
  1390.             if ((($tmp =  !Twig\Extension\CoreExtension::testEmpty(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "attributes", [], "any"falsefalsefalse1067))) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1391.                 // line 1068
  1392.                 yield "\t\t\t\t\t\t\t\t\t\t\t";
  1393.                 $context['_parent'] = $context;
  1394.                 $context['_seq'] = CoreExtension::ensureTraversable(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "attributes", [], "any"falsefalsefalse1068));
  1395.                 foreach ($context['_seq'] as $context["key"] => $context["value"]) {
  1396.                     // line 1069
  1397.                     yield "\t\t\t\t\t\t\t\t\t\t\t\t<tr>
  1398. \t\t\t\t\t\t\t\t\t\t\t\t\t<td>
  1399. \t\t\t\t\t\t\t\t\t\t\t\t\t\t<h5>";
  1400.                     // line 1071
  1401.                     yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(Twig\Extension\CoreExtension::titleCase($this->env->getCharset(), Twig\Extension\CoreExtension::replace($context["key"], ["_" => " "])), "html"nulltrue);
  1402.                     yield "</h5>
  1403. \t\t\t\t\t\t\t\t\t\t\t\t\t</td>
  1404. \t\t\t\t\t\t\t\t\t\t\t\t\t<td>
  1405. \t\t\t\t\t\t\t\t\t\t\t\t\t\t<h5>";
  1406.                     // line 1074
  1407.                     yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($context["value"], "html"nulltrue);
  1408.                     yield "</h5>
  1409. \t\t\t\t\t\t\t\t\t\t\t\t\t</td>
  1410. \t\t\t\t\t\t\t\t\t\t\t\t</tr>
  1411. \t\t\t\t\t\t\t\t\t\t\t";
  1412.                 }
  1413.                 $_parent $context['_parent'];
  1414.                 unset($context['_seq'], $context['key'], $context['value'], $context['_parent']);
  1415.                 $context array_intersect_key($context$_parent) + $_parent;
  1416.                 // line 1078
  1417.                 yield "\t\t\t\t\t\t\t\t\t\t";
  1418.             }
  1419.             // line 1079
  1420.             yield "\t\t\t\t\t\t\t\t\t</tbody>
  1421. \t\t\t\t\t\t\t\t</table>
  1422. \t\t\t\t\t\t\t</div>
  1423. \t\t\t\t\t\t";
  1424.         } else {
  1425.             // line 1083
  1426.             yield "\t\t\t\t\t\t\t<p class=\"text-muted\">Aucune spécification disponible pour ce produit.</p>
  1427. \t\t\t\t\t\t";
  1428.         }
  1429.         // line 1085
  1430.         yield "\t\t\t\t\t</div>
  1431. \t\t\t\t</div>
  1432. \t\t\t\t<div class=\"tab-pane fade\" id=\"contact\" role=\"tabpanel\" aria-labelledby=\"contact-tab\">
  1433. \t\t\t\t\t<div class=\"comment-wrapper\">
  1434. \t\t\t\t\t\t<div class=\"alert alert-info\">
  1435. \t\t\t\t\t\t\t<i class=\"lnr lnr-info-circle\"></i>
  1436. \t\t\t\t\t\t\tLa fonctionnalité de commentaires sera bientôt disponible. Vous pourrez laisser des commentaires et poser des questions sur ce produit.
  1437. \t\t\t\t\t\t</div>
  1438. \t\t\t\t\t\t";
  1439.         // line 1093
  1440.         if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["app"] ?? null), "user", [], "any"falsefalsefalse1093)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1441.             // line 1094
  1442.             yield "\t\t\t\t\t\t\t<div class=\"review_box mt-4\">
  1443. \t\t\t\t\t\t\t\t<h4>Poser une question</h4>
  1444. \t\t\t\t\t\t\t\t<form class=\"row contact_form\" method=\"post\" novalidate=\"novalidate\">
  1445. \t\t\t\t\t\t\t\t\t<div class=\"col-md-12\">
  1446. \t\t\t\t\t\t\t\t\t\t<div class=\"form-group\">
  1447. \t\t\t\t\t\t\t\t\t\t\t<textarea class=\"form-control\" name=\"comment\" id=\"comment\" rows=\"3\" placeholder=\"Votre question ou commentaire...\" required></textarea>
  1448. \t\t\t\t\t\t\t\t\t\t</div>
  1449. \t\t\t\t\t\t\t\t\t</div>
  1450. \t\t\t\t\t\t\t\t\t<div class=\"col-md-12 text-right\">
  1451. \t\t\t\t\t\t\t\t\t\t<button type=\"submit\" class=\"btn primary-btn\">Envoyer</button>
  1452. \t\t\t\t\t\t\t\t\t</div>
  1453. \t\t\t\t\t\t\t\t</form>
  1454. \t\t\t\t\t\t\t</div>
  1455. \t\t\t\t\t\t";
  1456.         } else {
  1457.             // line 1108
  1458.             yield "\t\t\t\t\t\t\t<p class=\"text-center\">
  1459. \t\t\t\t\t\t\t\t<a href=\"";
  1460.             // line 1109
  1461.             yield $this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("ui_app_login");
  1462.             yield "\" class=\"primary-btn\">Connectez-vous</a>
  1463. \t\t\t\t\t\t\t\tpour poser une question ou laisser un commentaire.
  1464. \t\t\t\t\t\t\t</p>
  1465. \t\t\t\t\t\t";
  1466.         }
  1467.         // line 1113
  1468.         yield "\t\t\t\t\t</div>
  1469. \t\t\t\t</div>
  1470. \t\t\t\t<div class=\"tab-pane fade\" id=\"review\" role=\"tabpanel\" aria-labelledby=\"review-tab\">
  1471. \t\t\t\t\t<div class=\"review-wrapper\">
  1472. \t\t\t\t\t\t<div class=\"row\">
  1473. \t\t\t\t\t\t\t<div class=\"col-lg-6\">
  1474. \t\t\t\t\t\t\t\t<div class=\"row total_rate\">
  1475. \t\t\t\t\t\t\t\t\t<div class=\"col-6\">
  1476. \t\t\t\t\t\t\t\t\t\t<div class=\"box_total\">
  1477. \t\t\t\t\t\t\t\t\t\t\t<h5>Note globale</h5>
  1478. \t\t\t\t\t\t\t\t\t\t\t<h4>";
  1479.         // line 1123
  1480.         yield (((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "averageRating", [], "any"falsefalsefalse1123)) && $tmp instanceof Markup ? (string) $tmp $tmp)) ? ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatNumber(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "averageRating", [], "any"falsefalsefalse1123), 1"."","), "html"nulltrue)) : ("0.0"));
  1481.         yield "</h4>
  1482. \t\t\t\t\t\t\t\t\t\t\t<h6>(";
  1483.         // line 1124
  1484.         yield (((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"truetruefalse1124) &&  !(null === CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"falsefalsefalse1124)))) ? ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"falsefalsefalse1124), "html"nulltrue)) : (0));
  1485.         yield "
  1486. \t\t\t\t\t\t\t\t\t\t\t\tAvis)</h6>
  1487. \t\t\t\t\t\t\t\t\t\t</div>
  1488. \t\t\t\t\t\t\t\t\t</div>
  1489. \t\t\t\t\t\t\t\t\t<div class=\"col-6\">
  1490. \t\t\t\t\t\t\t\t\t\t<div class=\"rating_list\">
  1491. \t\t\t\t\t\t\t\t\t\t\t<h3>Basé sur
  1492. \t\t\t\t\t\t\t\t\t\t\t\t";
  1493.         // line 1131
  1494.         yield (((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"truetruefalse1131) &&  !(null === CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"falsefalsefalse1131)))) ? ($this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"falsefalsefalse1131), "html"nulltrue)) : (0));
  1495.         yield "
  1496. \t\t\t\t\t\t\t\t\t\t\t\tavis</h3>
  1497. \t\t\t\t\t\t\t\t\t\t\t<div class=\"rating-summary\">
  1498. \t\t\t\t\t\t\t\t\t\t\t\t";
  1499.         // line 1134
  1500.         if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "averageRating", [], "any"falsefalsefalse1134)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1501.             // line 1135
  1502.             yield "\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rating-display mb-3\">
  1503. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1504.             // line 1136
  1505.             $context['_parent'] = $context;
  1506.             $context['_seq'] = CoreExtension::ensureTraversable(range(15));
  1507.             foreach ($context['_seq'] as $context["_key"] => $context["i"]) {
  1508.                 // line 1137
  1509.                 yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<i class=\"fa fa-star";
  1510.                 if (($context["i"] <= CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "averageRating", [], "any"falsefalsefalse1137))) {
  1511.                 } else {
  1512.                     yield "-o";
  1513.                 }
  1514.                 yield "\" style=\"color: #ffa200; font-size: 1.2rem;\"></i>
  1515. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1516.             }
  1517.             $_parent $context['_parent'];
  1518.             unset($context['_seq'], $context['_key'], $context['i'], $context['_parent']);
  1519.             $context array_intersect_key($context$_parent) + $_parent;
  1520.             // line 1139
  1521.             yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"ms-2\" style=\"font-size: 1.1rem; font-weight: bold;\">";
  1522.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatNumber(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "averageRating", [], "any"falsefalsefalse1139), 1"."","), "html"nulltrue);
  1523.             yield "
  1524. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/ 5.0</span>
  1525. \t\t\t\t\t\t\t\t\t\t\t\t\t</div>
  1526. \t\t\t\t\t\t\t\t\t\t\t\t";
  1527.         } else {
  1528.             // line 1143
  1529.             yield "\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-muted\">Aucune note disponible</p>
  1530. \t\t\t\t\t\t\t\t\t\t\t\t";
  1531.         }
  1532.         // line 1145
  1533.         yield "\t\t\t\t\t\t\t\t\t\t\t</div>
  1534. \t\t\t\t\t\t\t\t\t\t</div>
  1535. \t\t\t\t\t\t\t\t\t</div>
  1536. \t\t\t\t\t\t\t\t</div>
  1537. \t\t\t\t\t\t\t\t<div class=\"review_list\">
  1538. \t\t\t\t\t\t\t\t\t";
  1539.         // line 1150
  1540.         if ((CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"falsefalsefalse1150) && (CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"falsefalsefalse1150) > 0))) {
  1541.             // line 1151
  1542.             yield "\t\t\t\t\t\t\t\t\t\t<div class=\"alert alert-info\">
  1543. \t\t\t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-info-circle\"></i>
  1544. \t\t\t\t\t\t\t\t\t\t\tLe système d'affichage détaillé des avis sera disponible prochainement. 
  1545. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNote actuelle :
  1546. \t\t\t\t\t\t\t\t\t\t\t";
  1547.             // line 1155
  1548.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatNumber(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "averageRating", [], "any"falsefalsefalse1155), 1"."","), "html"nulltrue);
  1549.             yield "
  1550. \t\t\t\t\t\t\t\t\t\t\t/ 5.0 basée sur
  1551. \t\t\t\t\t\t\t\t\t\t\t";
  1552.             // line 1157
  1553.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "reviewCount", [], "any"falsefalsefalse1157), "html"nulltrue);
  1554.             yield "
  1555. \t\t\t\t\t\t\t\t\t\t\tavis.
  1556. \t\t\t\t\t\t\t\t\t\t</div>
  1557. \t\t\t\t\t\t\t\t\t";
  1558.         } else {
  1559.             // line 1161
  1560.             yield "\t\t\t\t\t\t\t\t\t\t<div class=\"alert alert-info\">
  1561. \t\t\t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-info-circle\"></i>
  1562. \t\t\t\t\t\t\t\t\t\t\tAucun avis pour ce produit pour le moment. Soyez le premier à laisser un avis après votre achat !
  1563. \t\t\t\t\t\t\t\t\t\t</div>
  1564. \t\t\t\t\t\t\t\t\t";
  1565.         }
  1566.         // line 1166
  1567.         yield "\t\t\t\t\t\t\t\t</div>
  1568. \t\t\t\t\t\t\t</div>
  1569. \t\t\t\t\t\t\t<div class=\"col-lg-6\">
  1570. \t\t\t\t\t\t\t\t<div class=\"review_box\">
  1571. \t\t\t\t\t\t\t\t\t<h4>Laisser un avis</h4>
  1572. \t\t\t\t\t\t\t\t\t";
  1573.         // line 1171
  1574.         if ((($tmp CoreExtension::getAttribute($this->env$this->source, ($context["app"] ?? null), "user", [], "any"falsefalsefalse1171)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  1575.             // line 1172
  1576.             yield "\t\t\t\t\t\t\t\t\t\t<p class=\"text-muted\">Vous pouvez laisser un avis après avoir acheté ce produit.</p>
  1577. \t\t\t\t\t\t\t\t\t\t<form class=\"row contact_form\" method=\"post\" novalidate=\"novalidate\">
  1578. \t\t\t\t\t\t\t\t\t\t\t<div class=\"col-md-12\">
  1579. \t\t\t\t\t\t\t\t\t\t\t\t<div class=\"form-group\">
  1580. \t\t\t\t\t\t\t\t\t\t\t\t\t<label>Votre note</label>
  1581. \t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rating-input mb-3\" style=\"display: flex; gap: 5px; flex-direction: row-reverse; justify-content: flex-end;\">
  1582. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1583.             // line 1178
  1584.             $context['_parent'] = $context;
  1585.             $context['_seq'] = CoreExtension::ensureTraversable(range(51));
  1586.             foreach ($context['_seq'] as $context["_key"] => $context["i"]) {
  1587.                 // line 1179
  1588.                 yield "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"rating\" id=\"rating";
  1589.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($context["i"], "html"nulltrue);
  1590.                 yield "\" value=\"";
  1591.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($context["i"], "html"nulltrue);
  1592.                 yield "\" required style=\"display: none;\">
  1593. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<label for=\"rating";
  1594.                 // line 1180
  1595.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($context["i"], "html"nulltrue);
  1596.                 yield "\" class=\"star-label\" style=\"cursor: pointer;\">
  1597. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<i class=\"fa fa-star-o\" style=\"color: #ccc; font-size: 1.5rem;\"></i>
  1598. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</label>
  1599. \t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  1600.             }
  1601.             $_parent $context['_parent'];
  1602.             unset($context['_seq'], $context['_key'], $context['i'], $context['_parent']);
  1603.             $context array_intersect_key($context$_parent) + $_parent;
  1604.             // line 1184
  1605.             yield "\t\t\t\t\t\t\t\t\t\t\t\t\t</div>
  1606. \t\t\t\t\t\t\t\t\t\t\t\t</div>
  1607. \t\t\t\t\t\t\t\t\t\t\t</div>
  1608. \t\t\t\t\t\t\t\t\t\t\t<div class=\"col-md-12\">
  1609. \t\t\t\t\t\t\t\t\t\t\t\t<div class=\"form-group\">
  1610. \t\t\t\t\t\t\t\t\t\t\t\t\t<textarea class=\"form-control\" name=\"review\" id=\"review\" rows=\"5\" placeholder=\"Votre avis...\" required onfocus=\"this.placeholder = ''\" onblur=\"this.placeholder = 'Votre avis...'\"></textarea>
  1611. \t\t\t\t\t\t\t\t\t\t\t\t</div>
  1612. \t\t\t\t\t\t\t\t\t\t\t</div>
  1613. \t\t\t\t\t\t\t\t\t\t\t<div class=\"col-md-12 text-right\">
  1614. \t\t\t\t\t\t\t\t\t\t\t\t<button type=\"submit\" class=\"btn primary-btn\">Publier l'avis</button>
  1615. \t\t\t\t\t\t\t\t\t\t\t</div>
  1616. \t\t\t\t\t\t\t\t\t\t</form>
  1617. \t\t\t\t\t\t\t\t\t";
  1618.         } else {
  1619.             // line 1197
  1620.             yield "\t\t\t\t\t\t\t\t\t\t<p class=\"text-center\">
  1621. \t\t\t\t\t\t\t\t\t\t\t<a href=\"";
  1622.             // line 1198
  1623.             yield $this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("ui_app_login");
  1624.             yield "\" class=\"primary-btn\">Connectez-vous</a>
  1625. \t\t\t\t\t\t\t\t\t\t\tpour laisser un avis sur ce produit.
  1626. \t\t\t\t\t\t\t\t\t\t</p>
  1627. \t\t\t\t\t\t\t\t\t";
  1628.         }
  1629.         // line 1202
  1630.         yield "\t\t\t\t\t\t\t\t</div>
  1631. \t\t\t\t\t\t\t</div>
  1632. \t\t\t\t\t\t</div>
  1633. \t\t\t\t\t</div>
  1634. \t\t\t\t</div>
  1635. \t\t\t</div>
  1636. \t\t</div>
  1637. \t</section>
  1638. \t<!--================End Product Description Area =================-->
  1639. \t<script>
  1640. \t\t// Initialiser les onglets Bootstrap
  1641. document.addEventListener('DOMContentLoaded', function () { // Activer les onglets Bootstrap 5
  1642. const triggerTabList = document.querySelectorAll('#myTab button[data-bs-toggle=\"tab\"]');
  1643. triggerTabList.forEach(triggerEl => {
  1644. const tabTrigger = new bootstrap.Tab(triggerEl);
  1645. triggerEl.addEventListener('click', event => {
  1646. event.preventDefault();
  1647. tabTrigger.show();
  1648. });
  1649. });
  1650. // Gestion des étoiles pour les avis
  1651. const ratingInputs = document.querySelectorAll('.rating-input input[type=\"radio\"]');
  1652. const starLabels = document.querySelectorAll('.rating-input .star-label');
  1653. ratingInputs.forEach((input, index) => {
  1654. input.addEventListener('change', function () {
  1655. const rating = parseInt(this.value);
  1656. starLabels.forEach((label) => { // Trouver l'input associé à ce label
  1657. const labelInput = label.previousElementSibling;
  1658. if (labelInput && labelInput.type === 'radio') {
  1659. const starValue = parseInt(labelInput.value);
  1660. const star = label.querySelector('i');
  1661. if (starValue <= rating) {
  1662. star.style.color = '#ffa200';
  1663. star.classList.remove('fa-star-o');
  1664. star.classList.add('fa-star');
  1665. } else {
  1666. star.style.color = '#ccc';
  1667. star.classList.remove('fa-star');
  1668. star.classList.add('fa-star-o');
  1669. }
  1670. }
  1671. });
  1672. });
  1673. });
  1674. // Pré-remplir les étoiles au survol
  1675. starLabels.forEach((label) => {
  1676. label.addEventListener('mouseenter', function () { // Trouver l'input associé à ce label
  1677. const input = label.previousElementSibling;
  1678. if (input && input.type === 'radio') {
  1679. const rating = parseInt(input.value);
  1680. starLabels.forEach((l) => {
  1681. const lInput = l.previousElementSibling;
  1682. if (lInput && lInput.type === 'radio') {
  1683. const starValue = parseInt(lInput.value);
  1684. const star = l.querySelector('i');
  1685. if (starValue <= rating) {
  1686. star.style.color = '#ffa200';
  1687. star.classList.remove('fa-star-o');
  1688. star.classList.add('fa-star');
  1689. } else {
  1690. star.style.color = '#ccc';
  1691. star.classList.remove('fa-star');
  1692. star.classList.add('fa-star-o');
  1693. }
  1694. }
  1695. });
  1696. }
  1697. });
  1698. });
  1699. const ratingContainer = document.querySelector('.rating-input');
  1700. if (ratingContainer) {
  1701. ratingContainer.addEventListener('mouseleave', function () {
  1702. const checkedInput = document.querySelector('.rating-input input[type=\"radio\"]:checked');
  1703. if (checkedInput) {
  1704. checkedInput.dispatchEvent(new Event('change'));
  1705. } else {
  1706. starLabels.forEach(label => {
  1707. const star = label.querySelector('i');
  1708. star.style.color = '#ccc';
  1709. });
  1710. }
  1711. });
  1712. }
  1713. });
  1714. \t</script>
  1715. \t";
  1716.         // line 1293
  1717.         if ((Twig\Extension\CoreExtension::length($this->env->getCharset(), ($context["youMightAlsoLike"] ?? null)) > 0)) {
  1718.             // line 1294
  1719.             yield "\t\t<section class=\"related-product-area section_gap_bottom py-5\" style=\"background: linear-gradient(to bottom, #f8f9fa 0%, #ffffff 100%);\">
  1720. \t\t\t<div class=\"container\">
  1721. \t\t\t\t<div class=\"row justify-content-center\">
  1722. \t\t\t\t\t<div class=\"col-lg-8 text-center\">
  1723. \t\t\t\t\t\t<div class=\"section-title\">
  1724. \t\t\t\t\t\t\t<h2 class=\"fw-bold mb-3\" style=\"font-size: 2.5rem; color: #2c3e50;\">Vous pourriez aussi aimer</h2>
  1725. \t\t\t\t\t\t\t<p class=\"text-muted\" style=\"font-size: 1.1rem;\">Produits sélectionnés pour vous en fonction de vos préférences</p>
  1726. \t\t\t\t\t\t</div>
  1727. \t\t\t\t\t</div>
  1728. \t\t\t\t</div>
  1729. \t\t\t\t<div class=\"row g-4\">
  1730. \t\t\t\t\t";
  1731.             // line 1305
  1732.             $context['_parent'] = $context;
  1733.             $context['_seq'] = CoreExtension::ensureTraversable(($context["youMightAlsoLike"] ?? null));
  1734.             $context['loop'] = [
  1735.               'parent' => $context['_parent'],
  1736.               'index0' => 0,
  1737.               'index'  => 1,
  1738.               'first'  => true,
  1739.             ];
  1740.             if (is_array($context['_seq']) || (is_object($context['_seq']) && $context['_seq'] instanceof \Countable)) {
  1741.                 $length count($context['_seq']);
  1742.                 $context['loop']['revindex0'] = $length 1;
  1743.                 $context['loop']['revindex'] = $length;
  1744.                 $context['loop']['length'] = $length;
  1745.                 $context['loop']['last'] = === $length;
  1746.             }
  1747.             foreach ($context['_seq'] as $context["_key"] => $context["relatedProduct"]) {
  1748.                 // line 1306
  1749.                 yield "\t\t\t\t\t\t<div class=\"col-xl-3 col-lg-4 col-md-6 col-sm-6 mb-4 d-flex align-items-stretch\">
  1750. \t\t\t\t\t\t\t";
  1751.                 // line 1307
  1752.                 yield from $this->load("home/_single_product_card.html.twig"1307)->unwrap()->yield(CoreExtension::merge($context, ["product" => $context["relatedProduct"]]));
  1753.                 // line 1308
  1754.                 yield "\t\t\t\t\t\t</div>
  1755. \t\t\t\t\t";
  1756.                 ++$context['loop']['index0'];
  1757.                 ++$context['loop']['index'];
  1758.                 $context['loop']['first'] = false;
  1759.                 if (isset($context['loop']['revindex0'], $context['loop']['revindex'])) {
  1760.                     --$context['loop']['revindex0'];
  1761.                     --$context['loop']['revindex'];
  1762.                     $context['loop']['last'] = === $context['loop']['revindex0'];
  1763.                 }
  1764.             }
  1765.             $_parent $context['_parent'];
  1766.             unset($context['_seq'], $context['_key'], $context['relatedProduct'], $context['_parent'], $context['loop']);
  1767.             $context array_intersect_key($context$_parent) + $_parent;
  1768.             // line 1310
  1769.             yield "\t\t\t\t</div>
  1770. \t\t\t</div>
  1771. \t\t</section>
  1772. \t\t<style>
  1773. \t\t\t.modern-product-card {
  1774. \t\t\t\tbackground: #ffffff;
  1775. \t\t\t\tborder-radius: 16px;
  1776. \t\t\t\toverflow: hidden;
  1777. \t\t\t\tbox-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  1778. \t\t\t\ttransition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  1779. \t\t\t\theight: 100%;
  1780. \t\t\t\tdisplay: flex;
  1781. \t\t\t\tflex-direction: column;
  1782. \t\t\t\tposition: relative;
  1783. \t\t\t}
  1784. \t\t\t.modern-product-card:hover {
  1785. \t\t\t\ttransform: translateY(-8px);
  1786. \t\t\t\tbox-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
  1787. \t\t\t}
  1788. \t\t\t.product-image-wrapper {
  1789. \t\t\t\tposition: relative;
  1790. \t\t\t\twidth: 100%;
  1791. \t\t\t\theight: 250px;
  1792. \t\t\t\toverflow: hidden;
  1793. \t\t\t\tbackground: #f8f9fa;
  1794. \t\t\t}
  1795. \t\t\t.product-image {
  1796. \t\t\t\twidth: 100%;
  1797. \t\t\t\theight: 100%;
  1798. \t\t\t\tobject-fit: cover;
  1799. \t\t\t\ttransition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  1800. \t\t\t}
  1801. \t\t\t.modern-product-card:hover .product-image {
  1802. \t\t\t\ttransform: scale(1.1);
  1803. \t\t\t}
  1804. \t\t\t.product-overlay {
  1805. \t\t\t\tposition: absolute;
  1806. \t\t\t\ttop: 0;
  1807. \t\t\t\tleft: 0;
  1808. \t\t\t\tright: 0;
  1809. \t\t\t\tbottom: 0;
  1810. \t\t\t\tbackground: rgba(0, 0, 0, 0.5);
  1811. \t\t\t\tdisplay: flex;
  1812. \t\t\t\talign-items: center;
  1813. \t\t\t\tjustify-content: center;
  1814. \t\t\t\topacity: 0;
  1815. \t\t\t\ttransition: opacity 0.3s ease;
  1816. \t\t\t}
  1817. \t\t\t.modern-product-card:hover .product-overlay {
  1818. \t\t\t\topacity: 1;
  1819. \t\t\t}
  1820. \t\t\t.view-product-btn {
  1821. \t\t\t\tcolor: white;
  1822. \t\t\t\tpadding: 12px 24px;
  1823. \t\t\t\tbackground: rgba(255, 255, 255, 0.2);
  1824. \t\t\t\tbackdrop-filter: blur(10px);
  1825. \t\t\t\tborder-radius: 8px;
  1826. \t\t\t\tfont-weight: 600;
  1827. \t\t\t\tdisplay: inline-flex;
  1828. \t\t\t\talign-items: center;
  1829. \t\t\t\tgap: 8px;
  1830. \t\t\t\ttransition: all 0.3s ease;
  1831. \t\t\t}
  1832. \t\t\t.view-product-btn:hover {
  1833. \t\t\t\tbackground: rgba(255, 255, 255, 0.3);
  1834. \t\t\t\ttransform: scale(1.05);
  1835. \t\t\t}
  1836. \t\t\t.product-badge {
  1837. \t\t\t\tposition: absolute;
  1838. \t\t\t\ttop: 12px;
  1839. \t\t\t\tright: 12px;
  1840. \t\t\t\tz-index: 2;
  1841. \t\t\t}
  1842. \t\t\t.badge-discount {
  1843. \t\t\t\tbackground: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
  1844. \t\t\t\tcolor: white;
  1845. \t\t\t\tpadding: 6px 12px;
  1846. \t\t\t\tborder-radius: 20px;
  1847. \t\t\t\tfont-size: 0.75rem;
  1848. \t\t\t\tfont-weight: 700;
  1849. \t\t\t\tbox-shadow: 0 2px 8px rgba(238, 90, 111, 0.4);
  1850. \t\t\t}
  1851. \t\t\t.product-info {
  1852. \t\t\t\tpadding: 20px;
  1853. \t\t\t\tflex-grow: 1;
  1854. \t\t\t\tdisplay: flex;
  1855. \t\t\t\tflex-direction: column;
  1856. \t\t\t}
  1857. \t\t\t.product-title {
  1858. \t\t\t\tmargin: 0 0 8px 0;
  1859. \t\t\t\tfont-size: 1rem;
  1860. \t\t\t\tfont-weight: 600;
  1861. \t\t\t\tline-height: 1.4;
  1862. \t\t\t\theight: 2.8em;
  1863. \t\t\t\toverflow: hidden;
  1864. \t\t\t\tdisplay: -webkit-box;
  1865. \t\t\t\t-webkit-line-clamp: 2;
  1866. \t\t\t\t-webkit-box-orient: vertical;
  1867. \t\t\t}
  1868. \t\t\t.product-title a {
  1869. \t\t\t\tcolor: #2c3e50;
  1870. \t\t\t\ttext-decoration: none;
  1871. \t\t\t\ttransition: color 0.3s ease;
  1872. \t\t\t}
  1873. \t\t\t.product-title a:hover {
  1874. \t\t\t\tcolor: #ffa200;
  1875. \t\t\t}
  1876. \t\t\t.product-shop {
  1877. \t\t\t\tfont-size: 0.85rem;
  1878. \t\t\t\tcolor: #6c757d;
  1879. \t\t\t\tmargin-bottom: 12px;
  1880. \t\t\t\tdisplay: flex;
  1881. \t\t\t\talign-items: center;
  1882. \t\t\t\tgap: 6px;
  1883. \t\t\t}
  1884. \t\t\t.product-shop i {
  1885. \t\t\t\tfont-size: 0.9rem;
  1886. \t\t\t}
  1887. \t\t\t.shop-link {
  1888. \t\t\t\tcolor: #6c757d;
  1889. \t\t\t\ttext-decoration: none;
  1890. \t\t\t\ttransition: color 0.3s ease;
  1891. \t\t\t}
  1892. \t\t\t.shop-link:hover {
  1893. \t\t\t\tcolor: #ffa200;
  1894. \t\t\t}
  1895. \t\t\t.product-price-wrapper {
  1896. \t\t\t\tmargin-bottom: 16px;
  1897. \t\t\t\tdisplay: flex;
  1898. \t\t\t\talign-items: center;
  1899. \t\t\t\tgap: 8px;
  1900. \t\t\t\tflex-wrap: wrap;
  1901. \t\t\t}
  1902. \t\t\t.product-price {
  1903. \t\t\t\tfont-size: 1.25rem;
  1904. \t\t\t\tfont-weight: 700;
  1905. \t\t\t\tcolor: #ffa200;
  1906. \t\t\t}
  1907. \t\t\t.product-compare-price {
  1908. \t\t\t\tfont-size: 0.9rem;
  1909. \t\t\t\tcolor: #adb5bd;
  1910. \t\t\t\ttext-decoration: line-through;
  1911. \t\t\t}
  1912. \t\t\t.product-actions {
  1913. \t\t\t\tmargin-top: auto;
  1914. \t\t\t}
  1915. \t\t\t/* Styles pour les boutons de la section \"Vous pourriez aussi aimer\" uniquement */
  1916. \t\t\t.modern-product-card .btn-add-to-cart {
  1917. \t\t\t\twidth: 100%;
  1918. \t\t\t\tpadding: 12px;
  1919. \t\t\t\tbackground: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
  1920. \t\t\t\tcolor: white;
  1921. \t\t\t\tborder: none;
  1922. \t\t\t\tborder-radius: 10px;
  1923. \t\t\t\tfont-weight: 600;
  1924. \t\t\t\tdisplay: flex;
  1925. \t\t\t\talign-items: center;
  1926. \t\t\t\tjustify-content: center;
  1927. \t\t\t\tgap: 8px;
  1928. \t\t\t\ttext-decoration: none;
  1929. \t\t\t\ttransition: all 0.3s ease;
  1930. \t\t\t\tcursor: pointer;
  1931. \t\t\t}
  1932. \t\t\t.modern-product-card .btn-add-to-cart:hover {
  1933. \t\t\t\tbackground: linear-gradient(135deg, #e8910a 0%, #d6820a 100%);
  1934. \t\t\t\ttransform: translateY(-2px);
  1935. \t\t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.4);
  1936. \t\t\t\tcolor: white;
  1937. \t\t\t}
  1938. \t\t\t/* Le bouton principal garde son design original */
  1939. \t\t\t.card_area .btn-add-to-cart {
  1940. \t\t\t\twidth: auto;
  1941. \t\t\t\tdisplay: inline-flex;
  1942. \t\t\t}
  1943. \t\t\t.modern-product-card .btn-add-to-cart:active {
  1944. \t\t\t\ttransform: translateY(0);
  1945. \t\t\t}
  1946. \t\t\t.modern-product-card .btn-add-to-cart i {
  1947. \t\t\t\tfont-size: 1.1rem;
  1948. \t\t\t}
  1949. \t\t\t/* Responsive adjustments */
  1950. \t\t\t@media (max-width: 768px) {
  1951. \t\t\t\t.product-image-wrapper {
  1952. \t\t\t\t\theight: 200px;
  1953. \t\t\t\t}
  1954. \t\t\t\t.product-info {
  1955. \t\t\t\t\tpadding: 16px;
  1956. \t\t\t\t}
  1957. \t\t\t\t.product-title {
  1958. \t\t\t\t\tfont-size: 0.95rem;
  1959. \t\t\t\t}
  1960. \t\t\t\t.product-price {
  1961. \t\t\t\t\tfont-size: 1.1rem;
  1962. \t\t\t\t}
  1963. \t\t\t\t.section-title h2 {
  1964. \t\t\t\t\tfont-size: 2rem !important;
  1965. \t\t\t\t}
  1966. \t\t\t}
  1967. \t\t\t@media (max-width: 576px) {
  1968. \t\t\t\t.product-image-wrapper {
  1969. \t\t\t\t\theight: 190px;
  1970. \t\t\t\t}
  1971. \t\t\t\t.product-info {
  1972. \t\t\t\t\tpadding: 14px;
  1973. \t\t\t\t}
  1974. \t\t\t\t.btn-add-to-cart {
  1975. \t\t\t\t\tpadding: 10px;
  1976. \t\t\t\t\tfont-size: 0.9rem;
  1977. \t\t\t\t}
  1978. \t\t\t}
  1979. \t\t\t/* Ensure fixed height for cards */
  1980. \t\t\t.col-6.col-md-4.col-lg-3 {
  1981. \t\t\t\tdisplay: flex;
  1982. \t\t\t}
  1983. \t\t\t.modern-product-card {
  1984. \t\t\t\twidth: 100%;
  1985. \t\t\t}
  1986. \t\t\t/* Loading state for add to cart button */
  1987. \t\t\t.btn-add-to-cart.loading {
  1988. \t\t\t\topacity: 0.7;
  1989. \t\t\t\tpointer-events: none;
  1990. \t\t\t}
  1991. \t\t\t.btn-add-to-cart.loading i {
  1992. \t\t\t\tanimation: spin 1s linear infinite;
  1993. \t\t\t}
  1994. \t\t\t@keyframes spin {
  1995. \t\t\t\tfrom { transform: rotate(0deg); }
  1996. \t\t\t\tto { transform: rotate(360deg); }
  1997. \t\t\t}
  1998. \t\t\t/* Success state - vert pour tous les boutons */
  1999. \t\t\t.btn-add-to-cart.success {
  2000. \t\t\t\tbackground: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important;
  2001. \t\t\t}
  2002. \t\t\t/* Le bouton principal garde son style original même en success */
  2003. \t\t\t.card_area .btn-add-to-cart.success {
  2004. \t\t\t\tbackground: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important;
  2005. \t\t\t}
  2006. \t\t</style>
  2007. \t\t<script>
  2008. \t\t\t// Gestion des boutons d'ajout au panier (principal et section \"Vous pourriez aussi aimer\")
  2009. \t\t\tdocument.addEventListener('DOMContentLoaded', function() {
  2010. \t\t\t\tconst addToCartButtons = document.querySelectorAll('.btn-add-to-cart');
  2011. \t\t\t\t
  2012. \t\t\t\taddToCartButtons.forEach(button => {
  2013. \t\t\t\t\tbutton.addEventListener('click', function(e) {
  2014. \t\t\t\t\t\te.preventDefault();
  2015. \t\t\t\t\t\te.stopPropagation();
  2016. \t\t\t\t\t\t
  2017. \t\t\t\t\t\tconst productId = this.getAttribute('data-product-id');
  2018. \t\t\t\t\t\t
  2019. \t\t\t\t\t\t// Pour le bouton principal, récupérer la quantité depuis l'input sst si disponible
  2020. \t\t\t\t\t\tlet qty = this.getAttribute('data-qty') || '1';
  2021. \t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  2022. \t\t\t\t\t\t\tconst qtyInput = document.getElementById('sst');
  2023. \t\t\t\t\t\t\tif (qtyInput) {
  2024. \t\t\t\t\t\t\t\tqty = parseInt(qtyInput.value) || 1;
  2025. \t\t\t\t\t\t\t}
  2026. \t\t\t\t\t\t}
  2027. \t\t\t\t\t\t
  2028. \t\t\t\t\t\tif (!productId) {
  2029. \t\t\t\t\t\t\tconsole.error('Product ID manquant');
  2030. \t\t\t\t\t\t\treturn false;
  2031. \t\t\t\t\t\t}
  2032. \t\t\t\t\t\t// Sauvegarder le contenu original
  2033. \t\t\t\t\t\tconst originalContent = this.innerHTML;
  2034. \t\t\t\t\t\t
  2035. \t\t\t\t\t\t// Désactiver le bouton et afficher le loading
  2036. \t\t\t\t\t\tthis.classList.add('loading');
  2037. \t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  2038. \t\t\t\t\t\t\tthis.style.pointerEvents = 'none';
  2039. \t\t\t\t\t\t\tthis.innerHTML = '<i class=\"lnr lnr-spinner\"></i> Ajout en cours...';
  2040. \t\t\t\t\t\t} else {
  2041. \t\t\t\t\t\t\tthis.innerHTML = '<i class=\"ti ti-loader-2\"></i> <span>Ajout...</span>';
  2042. \t\t\t\t\t\t}
  2043. \t\t\t\t\t\t
  2044. \t\t\t\t\t\t// Construire le body de la requête
  2045. \t\t\t\t\t\tlet body = 'productId=' + encodeURIComponent(productId) + '&qty=' + encodeURIComponent(qty);
  2046. \t\t\t\t\t\t
  2047. \t\t\t\t\t\t// Pour le bouton principal, vérifier aussi le prix unitaire pour les prix de gros
  2048. \t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  2049. \t\t\t\t\t\t\tconst unitPriceInput = document.getElementById('unit-price-input');
  2050. \t\t\t\t\t\t\tconst unitPrice = unitPriceInput ? unitPriceInput.value : '';
  2051. \t\t\t\t\t\t\tif (unitPrice) {
  2052. \t\t\t\t\t\t\t\tbody += '&unitPrice=' + encodeURIComponent(unitPrice);
  2053. \t\t\t\t\t\t\t}
  2054. \t\t\t\t\t\t}
  2055. \t\t\t\t\t\t
  2056. \t\t\t\t\t\t// Faire la requête AJAX
  2057. \t\t\t\t\t\tfetch('";
  2058.             // line 1643
  2059.             yield $this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("ui_cart_add");
  2060.             yield "', {
  2061. \t\t\t\t\t\t\tmethod: 'POST',
  2062. \t\t\t\t\t\t\theaders: {
  2063. \t\t\t\t\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',
  2064. \t\t\t\t\t\t\t\t'X-Requested-With': 'XMLHttpRequest'
  2065. \t\t\t\t\t\t\t},
  2066. \t\t\t\t\t\t\tbody: body
  2067. \t\t\t\t\t\t})
  2068. \t\t\t\t\t\t.then(response => {
  2069. \t\t\t\t\t\t\tif (!response.ok) {
  2070. \t\t\t\t\t\t\t\tthrow new Error('Erreur réseau: ' + response.status);
  2071. \t\t\t\t\t\t\t}
  2072. \t\t\t\t\t\t\treturn response.json();
  2073. \t\t\t\t\t\t})
  2074. \t\t\t\t\t\t.then(data => {
  2075. \t\t\t\t\t\t\tif (data.ok || data.success) {
  2076. \t\t\t\t\t\t\t\t// Succès - Afficher l'animation de succès
  2077. \t\t\t\t\t\t\t\tthis.classList.remove('loading');
  2078. \t\t\t\t\t\t\t\tthis.classList.add('success');
  2079. \t\t\t\t\t\t\t\t
  2080. \t\t\t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  2081. \t\t\t\t\t\t\t\t\tthis.style.background = 'linear-gradient(135deg, #28a745 0%, #20c997 100%)';
  2082. \t\t\t\t\t\t\t\t\tthis.innerHTML = '<i class=\"lnr lnr-checkmark-circle\"></i> Ajouté !';
  2083. \t\t\t\t\t\t\t\t\t
  2084. \t\t\t\t\t\t\t\t\t// Afficher le message de confirmation si présent
  2085. \t\t\t\t\t\t\t\t\tconst cartMessage = document.getElementById('cart-message');
  2086. \t\t\t\t\t\t\t\t\tif (cartMessage) {
  2087. \t\t\t\t\t\t\t\t\t\tcartMessage.style.display = 'block';
  2088. \t\t\t\t\t\t\t\t\t\tsetTimeout(() => {
  2089. \t\t\t\t\t\t\t\t\t\t\tcartMessage.style.display = 'none';
  2090. \t\t\t\t\t\t\t\t\t\t}, 3000);
  2091. \t\t\t\t\t\t\t\t\t}
  2092. \t\t\t\t\t\t\t\t} else {
  2093. \t\t\t\t\t\t\t\t\tthis.innerHTML = '<i class=\"ti ti-check\"></i> <span>Ajouté !</span>';
  2094. \t\t\t\t\t\t\t\t}
  2095. \t\t\t\t\t\t\t\t
  2096. \t\t\t\t\t\t\t\t// Mettre à jour le compteur du panier si présent
  2097. \t\t\t\t\t\t\t\tconst cartBadge = document.querySelector('.cart-badge');
  2098. \t\t\t\t\t\t\t\tif (cartBadge) {
  2099. \t\t\t\t\t\t\t\t\tcartBadge.textContent = data.totalQty || data.quantity || 0;
  2100. \t\t\t\t\t\t\t\t\t// Animation du badge
  2101. \t\t\t\t\t\t\t\t\tcartBadge.style.transform = 'scale(1.2)';
  2102. \t\t\t\t\t\t\t\t\tsetTimeout(() => {
  2103. \t\t\t\t\t\t\t\t\t\tcartBadge.style.transform = 'scale(1)';
  2104. \t\t\t\t\t\t\t\t\t}, 300);
  2105. \t\t\t\t\t\t\t\t}
  2106. \t\t\t\t\t\t\t\t
  2107. \t\t\t\t\t\t\t\t// Afficher une notification si la fonction existe
  2108. \t\t\t\t\t\t\t\tif (typeof showCustomAlert === 'function') {
  2109. \t\t\t\t\t\t\t\t\tshowCustomAlert('Produit ajouté au panier avec succès !', 'success');
  2110. \t\t\t\t\t\t\t\t}
  2111. \t\t\t\t\t\t\t\t
  2112. \t\t\t\t\t\t\t\t// Restaurer le bouton après 2 secondes
  2113. \t\t\t\t\t\t\t\tsetTimeout(() => {
  2114. \t\t\t\t\t\t\t\t\tthis.classList.remove('success');
  2115. \t\t\t\t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  2116. \t\t\t\t\t\t\t\t\t\tthis.style.background = '';
  2117. \t\t\t\t\t\t\t\t\t\tthis.style.pointerEvents = 'auto';
  2118. \t\t\t\t\t\t\t\t\t}
  2119. \t\t\t\t\t\t\t\t\tthis.innerHTML = originalContent;
  2120. \t\t\t\t\t\t\t\t}, 2000);
  2121. \t\t\t\t\t\t\t} else {
  2122. \t\t\t\t\t\t\t\t// Erreur
  2123. \t\t\t\t\t\t\t\tthis.classList.remove('loading');
  2124. \t\t\t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  2125. \t\t\t\t\t\t\t\t\tthis.style.pointerEvents = 'auto';
  2126. \t\t\t\t\t\t\t\t}
  2127. \t\t\t\t\t\t\t\tthis.innerHTML = originalContent;
  2128. \t\t\t\t\t\t\t\t
  2129. \t\t\t\t\t\t\t\tconst message = data.message || 'Erreur lors de l\\'ajout au panier';
  2130. \t\t\t\t\t\t\t\tif (typeof showCustomAlert === 'function') {
  2131. \t\t\t\t\t\t\t\t\tshowCustomAlert(message, 'error');
  2132. \t\t\t\t\t\t\t\t} else {
  2133. \t\t\t\t\t\t\t\t\talert(message);
  2134. \t\t\t\t\t\t\t\t}
  2135. \t\t\t\t\t\t\t}
  2136. \t\t\t\t\t\t})
  2137. \t\t\t\t\t\t.catch(error => {
  2138. \t\t\t\t\t\t\tconsole.error('Erreur:', error);
  2139. \t\t\t\t\t\t\tthis.classList.remove('loading');
  2140. \t\t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  2141. \t\t\t\t\t\t\t\tthis.style.pointerEvents = 'auto';
  2142. \t\t\t\t\t\t\t}
  2143. \t\t\t\t\t\t\tthis.innerHTML = originalContent;
  2144. \t\t\t\t\t\t\t
  2145. \t\t\t\t\t\t\tif (typeof showCustomAlert === 'function') {
  2146. \t\t\t\t\t\t\t\tshowCustomAlert('Erreur lors de l\\'ajout au panier. Veuillez réessayer.', 'error');
  2147. \t\t\t\t\t\t\t} else {
  2148. \t\t\t\t\t\t\t\talert('Erreur lors de l\\'ajout au panier. Veuillez réessayer.');
  2149. \t\t\t\t\t\t\t}
  2150. \t\t\t\t\t\t});
  2151. \t\t\t\t\t\t
  2152. \t\t\t\t\t\treturn false;
  2153. \t\t\t\t\t});
  2154. \t\t\t\t});
  2155. \t\t\t});
  2156. \t\t</script>
  2157. \t";
  2158.         }
  2159.         // line 1741
  2160.         yield "
  2161. \t<script src=\"js/vendor/jquery-2.2.4.min.js\"></script>
  2162. \t<script src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js\" integrity=\"sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4\" crossorigin=\"anonymous\"></script>
  2163. \t<script src=\"js/vendor/bootstrap.min.js\"></script>
  2164. \t<script src=\"js/jquery.ajaxchimp.min.js\"></script>
  2165. \t<script src=\"js/jquery.nice-select.min.js\"></script>
  2166. \t<script src=\"js/jquery.sticky.js\"></script>
  2167. \t<script src=\"js/nouislider.min.js\"></script>
  2168. \t<script src=\"js/jquery.magnific-popup.min.js\"></script>
  2169. \t<script src=\"js/owl.carousel.min.js\"></script>
  2170. \t<!--gmaps Js-->
  2171. \t<script src=\"https://maps.googleapis.com/maps/api/js?key=AIzaSyCjCGmQ0Uq4exrzdcL6rvxywDDOvfAu6eE\"></script>
  2172. \t<script src=\"js/gmaps.min.js\"></script>
  2173. \t<script src=\"js/main.js\"></script>
  2174. \t<script>
  2175. \t\t// Le gestionnaire d'événements pour les boutons d'ajout au panier est déjà défini
  2176. \t\t// dans la section \"Vous pourriez aussi aimer\" et gère maintenant aussi le bouton principal
  2177. // Gestion de la galerie d'images
  2178. document.addEventListener('DOMContentLoaded', function () {
  2179. const thumbnails = document.querySelectorAll('.thumbnail-item');
  2180. const mainImage = document.getElementById('main-product-image');
  2181. const currentIndex = document.getElementById('current-image-index');
  2182. const totalImages = document.getElementById('total-images');
  2183. // Fonction pour changer l'image principale
  2184. function changeMainImage(thumbnail, index) {
  2185. const imageUrl = thumbnail.getAttribute('data-image');
  2186. if (mainImage && imageUrl) {
  2187. mainImage.style.opacity = '0';
  2188. setTimeout(() => {
  2189. mainImage.src = imageUrl;
  2190. mainImage.style.opacity = '1';
  2191. // Reset zoom
  2192. mainImage.style.transform = 'scale(1)';
  2193. mainImage.style.transformOrigin = 'center center';
  2194. }, 150);
  2195. }
  2196. // Mettre à jour l'indicateur
  2197. if (currentIndex) {
  2198. currentIndex.textContent = index + 1;
  2199. }
  2200. }
  2201. // Ajouter le zoom sur l'image principale au survol
  2202. const mainImageContainer = document.querySelector('.product-main-image-wrapper');
  2203. if (mainImageContainer && mainImage) {
  2204. \tmainImageContainer.addEventListener('mousemove', function(e) {
  2205. \t\tconst rect = mainImageContainer.getBoundingClientRect();
  2206. \t\tconst x = e.clientX - rect.left;
  2207. \t\tconst y = e.clientY - rect.top;
  2208. \t\t
  2209. \t\tconst xPercent = (x / rect.width) * 100;
  2210. \t\tconst yPercent = (y / rect.height) * 100;
  2211. \t\t
  2212. \t\tmainImage.style.transformOrigin = `\${xPercent}% \${yPercent}%`;
  2213. \t\tmainImage.style.transform = 'scale(2.2)'; // Zoom Alibaba-style
  2214. \t});
  2215. \t
  2216. \tmainImageContainer.addEventListener('mouseleave', function() {
  2217. \t\tmainImage.style.transformOrigin = 'center center';
  2218. \t\tmainImage.style.transform = 'scale(1)';
  2219. \t});
  2220. }
  2221. // Gérer le survol sur les miniatures (sélection permanente au survol)
  2222. thumbnails.forEach((thumbnail, index) => {
  2223. thumbnail.addEventListener('mouseenter', function () { // Retirer toutes les sélections permanentes
  2224. thumbnails.forEach(t => {
  2225. t.classList.remove('active', 'permanently-active');
  2226. });
  2227. // Ajouter active et permanently-active à la miniature survolée
  2228. thumbnail.classList.add('active', 'permanently-active');
  2229. // Changer l'image principale
  2230. changeMainImage(thumbnail, index);
  2231. });
  2232. });
  2233. // Gérer le clic sur les miniatures (sélection permanente)
  2234. thumbnails.forEach((thumbnail, index) => {
  2235. thumbnail.addEventListener('click', function () { // Retirer la classe active et permanently-active de toutes les miniatures
  2236. thumbnails.forEach(t => {
  2237. t.classList.remove('active', 'permanently-active');
  2238. });
  2239. // Ajouter les classes active et permanently-active à la miniature cliquée
  2240. thumbnail.classList.add('active', 'permanently-active');
  2241. // Changer l'image principale
  2242. changeMainImage(thumbnail, index);
  2243. });
  2244. });
  2245. // Gestion du carrousel de miniatures avec taille fixe carrée
  2246. const navUpBtn = document.getElementById('thumbnail-nav-up');
  2247. const navDownBtn = document.getElementById('thumbnail-nav-down');
  2248. const thumbnailsContainer = document.getElementById('product-thumbnails');
  2249. const thumbnailsWrapper = document.getElementById('thumbnails-wrapper');
  2250. const thumbnailItems = Array.from(document.querySelectorAll('.thumbnail-item'));
  2251. let currentThumbnailOffset = 0;
  2252. const thumbnailHeight = 90;
  2253. // 80px (hauteur) + 10px (gap)
  2254. // Calculer la hauteur disponible pour les miniatures dynamiquement
  2255. const thumbnailsEl = document.getElementById('product-thumbnails');
  2256. let containerHeight = thumbnailsEl ? thumbnailsEl.clientHeight : 520; // Hauteur par défaut si non disponible
  2257. let visibleThumbnails = Math.floor(containerHeight / thumbnailHeight);
  2258. // Recalculer après le chargement pour avoir la vraie hauteur
  2259. setTimeout(() => {
  2260. if (thumbnailsEl) {
  2261. containerHeight = thumbnailsEl.clientHeight;
  2262. visibleThumbnails = Math.floor(containerHeight / thumbnailHeight);
  2263. updateThumbnailsPosition();
  2264. }
  2265. }, 100);
  2266. function getCurrentIndex() {
  2267. const current = document.querySelector('.thumbnail-item.permanently-active') || document.querySelector('.thumbnail-item.active');
  2268. if (! current) 
  2269. return 0;
  2270. const idxAttr = current.getAttribute('data-index');
  2271. return idxAttr ? parseInt(idxAttr, 10) : Math.max(0, thumbnailItems.indexOf(current));
  2272. }
  2273. function selectThumbnailByIndex(newIndex) {
  2274. if (! thumbnailItems.length) 
  2275. return;
  2276. const total = thumbnailItems.length;
  2277. if (newIndex < 0) 
  2278. newIndex = 0;
  2279. if (newIndex > total - 1) 
  2280. newIndex = total - 1;
  2281. // Reset classes
  2282. thumbnailItems.forEach(t => t.classList.remove('active', 'permanently-active'));
  2283. const target = thumbnailItems[newIndex];
  2284. if (! target) 
  2285. return;
  2286. target.classList.add('active', 'permanently-active');
  2287. changeMainImage(target, newIndex);
  2288. // Ajuster le carrousel pour voir la miniature sélectionnée
  2289. ensureThumbnailVisible(newIndex);
  2290. }
  2291. function ensureThumbnailVisible(index) {
  2292. if (! thumbnailsWrapper || ! thumbnailItems.length) 
  2293. return;
  2294. const targetTop = index * thumbnailHeight;
  2295. const visibleTop = currentThumbnailOffset * thumbnailHeight;
  2296. const visibleBottom = visibleTop + containerHeight;
  2297. // Si la miniature est au-dessus de la zone visible
  2298. if (targetTop < visibleTop) {
  2299. currentThumbnailOffset = index;
  2300. updateThumbnailsPosition();
  2301. }
  2302. // Si la miniature est en-dessous de la zone visible 
  2303. else if (targetTop + thumbnailHeight > visibleBottom) {
  2304. currentThumbnailOffset = Math.max(0, index - visibleThumbnails + 1);
  2305. updateThumbnailsPosition();
  2306. }
  2307. }
  2308. function updateThumbnailsPosition() {
  2309. if (!thumbnailsWrapper) 
  2310. return;
  2311. const maxOffset = Math.max(0, thumbnailItems.length - visibleThumbnails);
  2312. currentThumbnailOffset = Math.max(0, Math.min(currentThumbnailOffset, maxOffset));
  2313. thumbnailsWrapper.style.transform = `translateY(-\${
  2314. currentThumbnailOffset * thumbnailHeight
  2315. }px)`;
  2316. updateNavButtons();
  2317. }
  2318. function updateNavButtons() {
  2319. if (!thumbnailItems.length) 
  2320. return;
  2321. const maxOffset = Math.max(0, thumbnailItems.length - visibleThumbnails);
  2322. const canScrollUp = currentThumbnailOffset > 0;
  2323. const canScrollDown = currentThumbnailOffset < maxOffset;
  2324. if (navUpBtn) {
  2325. navUpBtn.classList.toggle('disabled', ! canScrollUp);
  2326. }
  2327. if (navDownBtn) {
  2328. navDownBtn.classList.toggle('disabled', ! canScrollDown);
  2329. }
  2330. }
  2331. // Navigation avec les boutons
  2332. if(navUpBtn) {
  2333. navUpBtn.addEventListener('click', function (e) {
  2334. e.preventDefault();
  2335. if (currentThumbnailOffset > 0) {
  2336. currentThumbnailOffset --;
  2337. updateThumbnailsPosition();
  2338. }
  2339. });
  2340. }
  2341. if(navDownBtn) {
  2342. navDownBtn.addEventListener('click', function (e) {
  2343. e.preventDefault();
  2344. const maxOffset = Math.max(0, thumbnailItems.length - visibleThumbnails);
  2345. if (currentThumbnailOffset < maxOffset) {
  2346. currentThumbnailOffset ++;
  2347. updateThumbnailsPosition();
  2348. }
  2349. });
  2350. }
  2351. // Initialiser le carrousel
  2352. if(thumbnailsWrapper && thumbnailItems.length > 0) {
  2353. updateThumbnailsPosition();
  2354. // S'assurer que la première miniature est visible
  2355. ensureThumbnailVisible(0);
  2356. }
  2357. // Navigation sur l'image principale
  2358. const mainPrev = document.getElementById('main-image-prev');
  2359. const mainNext = document.getElementById('main-image-next');
  2360. if(mainPrev) {
  2361. mainPrev.addEventListener('click', function (e) {
  2362. e.preventDefault();
  2363. const idx = getCurrentIndex();
  2364. selectThumbnailByIndex(idx - 1);
  2365. });
  2366. }
  2367. if(mainNext) {
  2368. mainNext.addEventListener('click', function (e) {
  2369. e.preventDefault();
  2370. const idx = getCurrentIndex();
  2371. selectThumbnailByIndex(idx + 1);
  2372. });
  2373. }
  2374. }
  2375. );
  2376. // --- Prix de gros : calcul du prix unitaire dynamique ---
  2377. (function () {
  2378. const bulk = document.getElementById('bulk-pricing');
  2379. const qtyInputEl = document.getElementById('sst');
  2380. const unitPriceSpan = document.getElementById('unit-price-value');
  2381. const mainUnitPriceSpan = document.getElementById('main-unit-price');
  2382. const totalPriceSpan = document.getElementById('total-price-value');
  2383. const savingsAmountSpan = document.getElementById('savings-amount');
  2384. const savingsPercentSpan = document.getElementById('savings-percent');
  2385. const unitPriceInput = document.getElementById('unit-price-input');
  2386. const tierRowsTbody = document.getElementById('bulk-tier-rows');
  2387. if (! bulk || ! qtyInputEl || ! unitPriceSpan || ! mainUnitPriceSpan || ! unitPriceInput) 
  2388. return;
  2389. let tiers = [];
  2390. try {
  2391. tiers = JSON.parse(bulk.getAttribute('data-tiers') || '[]');
  2392. } catch (e) {
  2393. tiers = [];
  2394. }
  2395. if (!Array.isArray(tiers) || tiers.length === 0) 
  2396. return;
  2397. tiers.sort(function (a, b) {
  2398. return(a.min || 0) - (b.min || 0);
  2399. });
  2400. const basePrice = (function () {
  2401. const one = tiers.find(t => (t.min || 0) <= 1);
  2402. return one ? parseFloat(one.price) : parseFloat(tiers[0].price);
  2403. })();
  2404. function getUnitPriceForQty(qty) {
  2405. let candidate = tiers[0] || null;
  2406. for (let i = 0; i < tiers.length; i++) {
  2407. if (qty >= (tiers[i].min || 1)) {
  2408. candidate = tiers[i];
  2409. }
  2410. }
  2411. return candidate ? parseFloat(candidate.price) : (tiers[0] ? parseFloat(tiers[0].price) : basePrice);
  2412. }
  2413. function formatPrice(val) {
  2414. return(parseFloat(val) || 0).toFixed(2);
  2415. }
  2416. function highlightActiveTier(qty) {
  2417. if (! tierRowsTbody) 
  2418. return;
  2419. const rows = tierRowsTbody.querySelectorAll('tr');
  2420. rows.forEach(r => r.classList.remove('table-warning'));
  2421. let activeRow = null;
  2422. rows.forEach(r => {
  2423. const m = parseInt(r.getAttribute('data-min'), 10) || 1;
  2424. if (qty >= m) 
  2425. activeRow = r;
  2426. });
  2427. if (activeRow) 
  2428. activeRow.classList.add('table-warning');
  2429. }
  2430. function refreshPrices() {
  2431. const qty = Math.max(1, parseInt(qtyInputEl.value, 10) || 1);
  2432. const unit = getUnitPriceForQty(qty);
  2433. const total = unit * qty;
  2434. const saveAmount = Math.max(0, (basePrice - unit) * qty);
  2435. const savePercent = Math.max(0, 100 * (basePrice - unit) / basePrice);
  2436. unitPriceSpan.textContent = formatPrice(unit) + ' HTG';
  2437. mainUnitPriceSpan.textContent = formatPrice(unit);
  2438. if (totalPriceSpan) 
  2439. totalPriceSpan.textContent = formatPrice(total);
  2440. if (savingsAmountSpan) 
  2441. savingsAmountSpan.textContent = formatPrice(saveAmount);
  2442. if (savingsPercentSpan) 
  2443. savingsPercentSpan.textContent = Math.round(savePercent);
  2444. unitPriceInput.value = formatPrice(unit);
  2445. highlightActiveTier(qty);
  2446. }
  2447. qtyInputEl.addEventListener('input', refreshPrices);
  2448. qtyInputEl.addEventListener('change', refreshPrices);
  2449. setTimeout(refreshPrices, 50);
  2450. setInterval(refreshPrices, 350);
  2451. })();
  2452. // --- /Prix de gros ---
  2453. // Injection du prix unitaire dans l'ajout au panier
  2454. (function () {
  2455. const addToCartBtn = document.getElementById('add-to-cart-btn');
  2456. if (! addToCartBtn) 
  2457. return;
  2458. addToCartBtn.addEventListener('click', function () {
  2459. const unitPriceInput = document.getElementById('unit-price-input');
  2460. if (unitPriceInput) {
  2461. addToCartBtn.setAttribute('data-unit-price', unitPriceInput.value);
  2462. }
  2463. });
  2464. })();
  2465. // Fonction pour ouvrir le modal de zoom avec loader
  2466. function openImageZoom () {
  2467. const mainImage = document.getElementById('main-product-image');
  2468. const zoomModal = document.getElementById('image-zoom-modal');
  2469. const zoomImage = document.getElementById('zoom-modal-image');
  2470. const zoomLoader = document.getElementById('zoom-loader');
  2471. if (mainImage && zoomModal && zoomImage) { // Afficher le loader
  2472. if (zoomLoader) {
  2473. zoomLoader.style.display = 'flex';
  2474. }
  2475. // Masquer l'image et réinitialiser
  2476. zoomImage.classList.remove('loaded');
  2477. zoomImage.style.opacity = '0';
  2478. // Ouvrir le modal avec animation
  2479. zoomModal.classList.add('active');
  2480. document.body.style.overflow = 'hidden';
  2481. // Charger l'image
  2482. const imageUrl = mainImage.src;
  2483. const tempImage = new Image();
  2484. tempImage.onload = function () {
  2485. zoomImage.src = imageUrl;
  2486. // Masquer le loader et afficher l'image avec transition
  2487. setTimeout(function () {
  2488. if (zoomLoader) {
  2489. zoomLoader.style.display = 'none';
  2490. }
  2491. zoomImage.classList.add('loaded');
  2492. zoomImage.style.opacity = '1';
  2493. }, 200);
  2494. };
  2495. tempImage.onerror = function () {
  2496. if (zoomLoader) {
  2497. zoomLoader.style.display = 'none';
  2498. }
  2499. zoomImage.src = imageUrl;
  2500. zoomImage.classList.add('loaded');
  2501. };
  2502. tempImage.src = imageUrl;
  2503. }
  2504. }
  2505. // Fonction pour fermer le modal de zoom avec animation
  2506. function closeImageZoom () {
  2507. const zoomModal = document.getElementById('image-zoom-modal');
  2508. const zoomImage = document.getElementById('zoom-modal-image');
  2509. const zoomLoader = document.getElementById('zoom-loader');
  2510. if (zoomModal) { // Animation de fermeture
  2511. if (zoomImage) {
  2512. zoomImage.style.opacity = '0';
  2513. zoomImage.classList.remove('loaded');
  2514. }
  2515. setTimeout(function () {
  2516. zoomModal.classList.remove('active');
  2517. document.body.style.overflow = 'auto';
  2518. // Réinitialiser le loader pour la prochaine ouverture
  2519. if (zoomLoader) {
  2520. zoomLoader.style.display = 'none';
  2521. }
  2522. }, 200);
  2523. }
  2524. }
  2525. // Fermer le modal en cliquant en dehors
  2526. document.addEventListener('DOMContentLoaded', function () {
  2527. const zoomModal = document.getElementById('image-zoom-modal');
  2528. if (zoomModal) {
  2529. zoomModal.addEventListener('click', function (e) {
  2530. if (e.target === zoomModal) {
  2531. closeImageZoom();
  2532. }
  2533. });
  2534. }
  2535. });
  2536. \t</script>
  2537. \t<!-- Modal de zoom d'image -->
  2538. \t<div id=\"image-zoom-modal\" class=\"image-zoom-modal\">
  2539. \t\t<span class=\"close-zoom\" onclick=\"closeImageZoom()\" title=\"Fermer\">&times;</span>
  2540. \t\t<div
  2541. \t\t\tclass=\"zoom-modal-content\">
  2542. \t\t\t<!-- Loader -->
  2543. \t\t\t<div class=\"zoom-loader\" id=\"zoom-loader\" style=\"display: none;\">
  2544. \t\t\t\t<div class=\"zoom-loader-spinner\"></div>
  2545. \t\t\t\t<div class=\"zoom-loader-text\">Chargement...</div>
  2546. \t\t\t</div>
  2547. \t\t\t<!-- Image -->
  2548. \t\t\t<img id=\"zoom-modal-image\" src=\"\" alt=\"";
  2549.         // line 2222
  2550.         yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "name", [], "any"falsefalsefalse2222), "html"nulltrue);
  2551.         yield "\">
  2552. \t\t</div>
  2553. \t</div>
  2554. \t";
  2555.         // line 2226
  2556.         if ((($tmp = ($context["dropshipReferral"] ?? null)) && $tmp instanceof Markup ? (string) $tmp $tmp)) {
  2557.             // line 2227
  2558.             yield "\t\t<!-- Modal d'affiliation -->
  2559. \t\t<div id=\"dropship-modal\" class=\"dropship-referral-modal\" style=\"display: none;\">
  2560. \t\t\t<div class=\"dropship-modal-overlay\"></div>
  2561. \t\t\t<div class=\"dropship-modal-content\">
  2562. \t\t\t\t<button type=\"button\" class=\"dropship-modal-close\" onclick=\"closeDropshipModal()\" aria-label=\"Fermer\">
  2563. \t\t\t\t\t&times;
  2564. \t\t\t\t</button>
  2565. \t\t\t\t<div class=\"dropship-modal-header\">
  2566. \t\t\t\t\t<div class=\"dropship-icon\">
  2567. \t\t\t\t\t\t<i class=\"fas fa-gift\"></i>
  2568. \t\t\t\t\t</div>
  2569. \t\t\t\t\t<h3>Produit recommandé par
  2570. \t\t\t\t\t\t";
  2571.             // line 2239
  2572.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["dropshipReferral"] ?? null), "affiliateName", [], "any"falsefalsefalse2239), "html"nulltrue);
  2573.             yield "</h3>
  2574. \t\t\t\t\t<p class=\"dropship-subtitle\">Vous avez été dirigé vers ce produit par un de nos partenaires</p>
  2575. \t\t\t\t</div>
  2576. \t\t\t\t<div class=\"dropship-modal-body\">
  2577. \t\t\t\t\t<div class=\"dropship-product-info\">
  2578. \t\t\t\t\t\t";
  2579.             // line 2244
  2580.             if ((Twig\Extension\CoreExtension::length($this->env->getCharset(), CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"falsefalsefalse2244)) > 0)) {
  2581.                 // line 2245
  2582.                 yield "\t\t\t\t\t\t\t<img src=\"";
  2583.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\AssetExtension']->getAssetUrl((($_v1 CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "images", [], "any"falsefalsefalse2245)) && is_array($_v1) || $_v1 instanceof ArrayAccess ? ($_v1[0] ?? null) : null)), "html"nulltrue);
  2584.                 yield "\" alt=\"";
  2585.                 yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "name", [], "any"falsefalsefalse2245), "html"nulltrue);
  2586.                 yield "\" class=\"dropship-product-image\" onerror=\"this.style.display='none'; this.nextElementSibling.style.display='flex';\">
  2587. \t\t\t\t\t\t\t<div class=\"dropship-product-image-placeholder\" style=\"display: none; width: 100px; height: 100px; background: #f0f0f0; border-radius: 8px; align-items: center; justify-content: center; color: #999;\">
  2588. \t\t\t\t\t\t\t\t<i class=\"fas fa-image\" style=\"font-size: 32px;\"></i>
  2589. \t\t\t\t\t\t\t</div>
  2590. \t\t\t\t\t\t";
  2591.             } else {
  2592.                 // line 2250
  2593.                 yield "\t\t\t\t\t\t\t<div class=\"dropship-product-image-placeholder\" style=\"width: 100px; height: 100px; background: #f0f0f0; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #999;\">
  2594. \t\t\t\t\t\t\t\t<i class=\"fas fa-image\" style=\"font-size: 32px;\"></i>
  2595. \t\t\t\t\t\t\t</div>
  2596. \t\t\t\t\t\t";
  2597.             }
  2598.             // line 2254
  2599.             yield "\t\t\t\t\t\t<div class=\"dropship-product-details\">
  2600. \t\t\t\t\t\t\t<h4>";
  2601.             // line 2255
  2602.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "name", [], "any"falsefalsefalse2255), "html"nulltrue);
  2603.             yield "</h4>
  2604. \t\t\t\t\t\t\t<div class=\"dropship-product-price\">
  2605. \t\t\t\t\t\t\t\t<span class=\"price\">";
  2606.             // line 2257
  2607.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Twig\Extension\CoreExtension']->formatNumber(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "price", [], "any"falsefalsefalse2257), 2","" "), "html"nulltrue);
  2608.             yield "
  2609. \t\t\t\t\t\t\t\t\tHTG</span>
  2610. \t\t\t\t\t\t\t</div>
  2611. \t\t\t\t\t\t</div>
  2612. \t\t\t\t\t</div>
  2613. \t\t\t\t\t<div class=\"dropship-message\">
  2614. \t\t\t\t\t\t<p>
  2615. \t\t\t\t\t\t\t<strong>";
  2616.             // line 2264
  2617.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["dropshipReferral"] ?? null), "affiliateName", [], "any"falsefalsefalse2264), "html"nulltrue);
  2618.             yield "</strong>
  2619. \t\t\t\t\t\t\tvous recommande ce produit. Souhaitez-vous l'ajouter à votre panier ?</p>
  2620. \t\t\t\t\t</div>
  2621. \t\t\t\t</div>
  2622. \t\t\t\t<div class=\"dropship-modal-footer\">
  2623. \t\t\t\t\t<button type=\"button\" class=\"btn btn-secondary\" onclick=\"closeDropshipModal()\">
  2624. \t\t\t\t\t\tParcourir d'abord
  2625. \t\t\t\t\t</button>
  2626. \t\t\t\t\t<button type=\"button\" class=\"btn btn-primary dropship-add-to-cart\" onclick=\"addToCartFromDropship()\">
  2627. \t\t\t\t\t\t<i class=\"fas fa-shopping-cart\"></i>
  2628. \t\t\t\t\t\tAjouter au panier
  2629. \t\t\t\t\t</button>
  2630. \t\t\t\t</div>
  2631. \t\t\t</div>
  2632. \t\t</div>
  2633. \t\t<style>
  2634. \t\t\t.dropship-referral-modal {
  2635. \t\t\t\tposition: fixed;
  2636. \t\t\t\ttop: 0;
  2637. \t\t\t\tleft: 0;
  2638. \t\t\t\twidth: 100%;
  2639. \t\t\t\theight: 100%;
  2640. \t\t\t\tz-index: 10000;
  2641. \t\t\t\tdisplay: flex;
  2642. \t\t\t\talign-items: center;
  2643. \t\t\t\tjustify-content: center;
  2644. \t\t\t}
  2645. \t\t\t.dropship-modal-overlay {
  2646. \t\t\t\tposition: absolute;
  2647. \t\t\t\ttop: 0;
  2648. \t\t\t\tleft: 0;
  2649. \t\t\t\twidth: 100%;
  2650. \t\t\t\theight: 100%;
  2651. \t\t\t\tbackground: rgba(0, 0, 0, 0.6);
  2652. \t\t\t\tbackdrop-filter: blur(4px);
  2653. \t\t\t}
  2654. \t\t\t.dropship-modal-content {
  2655. \t\t\t\tposition: relative;
  2656. \t\t\t\tbackground: white;
  2657. \t\t\t\tborder-radius: 16px;
  2658. \t\t\t\tbox-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
  2659. \t\t\t\tmax-width: 500px;
  2660. \t\t\t\twidth: 90%;
  2661. \t\t\t\tmax-height: 90vh;
  2662. \t\t\t\toverflow-y: auto;
  2663. \t\t\t\tz-index: 10001;
  2664. \t\t\t\tanimation: dropshipModalSlideIn 0.3s ease-out;
  2665. \t\t\t}
  2666. \t\t\t@keyframes dropshipModalSlideIn {
  2667. \t\t\t\tfrom {
  2668. \t\t\t\t\topacity: 0;
  2669. \t\t\t\t\ttransform: translateY(-30px) scale(0.95);
  2670. \t\t\t\t}
  2671. \t\t\t\tto {
  2672. \t\t\t\t\topacity: 1;
  2673. \t\t\t\t\ttransform: translateY(0) scale(1);
  2674. \t\t\t\t}
  2675. \t\t\t}
  2676. \t\t\t.dropship-modal-close {
  2677. \t\t\t\tposition: absolute;
  2678. \t\t\t\ttop: 15px;
  2679. \t\t\t\tright: 15px;
  2680. \t\t\t\tbackground: none;
  2681. \t\t\t\tborder: none;
  2682. \t\t\t\tfont-size: 28px;
  2683. \t\t\t\tcolor: #666;
  2684. \t\t\t\tcursor: pointer;
  2685. \t\t\t\twidth: 35px;
  2686. \t\t\t\theight: 35px;
  2687. \t\t\t\tdisplay: flex;
  2688. \t\t\t\talign-items: center;
  2689. \t\t\t\tjustify-content: center;
  2690. \t\t\t\tborder-radius: 50%;
  2691. \t\t\t\ttransition: all 0.2s;
  2692. \t\t\t\tz-index: 10;
  2693. \t\t\t}
  2694. \t\t\t.dropship-modal-close:hover {
  2695. \t\t\t\tbackground: #f0f0f0;
  2696. \t\t\t\tcolor: #333;
  2697. \t\t\t}
  2698. \t\t\t.dropship-modal-header {
  2699. \t\t\t\tpadding: 30px 30px 20px;
  2700. \t\t\t\ttext-align: center;
  2701. \t\t\t\tborder-bottom: 1px solid #f0f0f0;
  2702. \t\t\t}
  2703. \t\t\t.dropship-icon {
  2704. \t\t\t\twidth: 70px;
  2705. \t\t\t\theight: 70px;
  2706. \t\t\t\tmargin: 0 auto 15px;
  2707. \t\t\t\tbackground: linear-gradient(135deg, #ffa200 0%, #ff8c00 100%);
  2708. \t\t\t\tborder-radius: 50%;
  2709. \t\t\t\tdisplay: flex;
  2710. \t\t\t\talign-items: center;
  2711. \t\t\t\tjustify-content: center;
  2712. \t\t\t\tfont-size: 32px;
  2713. \t\t\t\tcolor: white;
  2714. \t\t\t\tbox-shadow: 0 4px 15px rgba(255, 162, 0, 0.3);
  2715. \t\t\t}
  2716. \t\t\t.dropship-modal-header h3 {
  2717. \t\t\t\tmargin: 0 0 8px;
  2718. \t\t\t\tfont-size: 22px;
  2719. \t\t\t\tfont-weight: 600;
  2720. \t\t\t\tcolor: #333;
  2721. \t\t\t}
  2722. \t\t\t.dropship-subtitle {
  2723. \t\t\t\tmargin: 0;
  2724. \t\t\t\tcolor: #666;
  2725. \t\t\t\tfont-size: 14px;
  2726. \t\t\t}
  2727. \t\t\t.dropship-modal-body {
  2728. \t\t\t\tpadding: 25px 30px;
  2729. \t\t\t}
  2730. \t\t\t.dropship-product-info {
  2731. \t\t\t\tdisplay: flex;
  2732. \t\t\t\tgap: 15px;
  2733. \t\t\t\tmargin-bottom: 20px;
  2734. \t\t\t\tpadding: 15px;
  2735. \t\t\t\tbackground: #f8f9fa;
  2736. \t\t\t\tborder-radius: 10px;
  2737. \t\t\t}
  2738. \t\t\t.dropship-product-image {
  2739. \t\t\t\twidth: 80px;
  2740. \t\t\t\theight: 80px;
  2741. \t\t\t\tobject-fit: cover;
  2742. \t\t\t\tborder-radius: 8px;
  2743. \t\t\t\tflex-shrink: 0;
  2744. \t\t\t}
  2745. \t\t\t.dropship-product-details {
  2746. \t\t\t\tflex: 1;
  2747. \t\t\t}
  2748. \t\t\t.dropship-product-details h4 {
  2749. \t\t\t\tmargin: 0 0 8px;
  2750. \t\t\t\tfont-size: 16px;
  2751. \t\t\t\tfont-weight: 600;
  2752. \t\t\t\tcolor: #333;
  2753. \t\t\t}
  2754. \t\t\t.dropship-product-price {
  2755. \t\t\t\tmargin-top: 5px;
  2756. \t\t\t}
  2757. \t\t\t.dropship-product-price .price {
  2758. \t\t\t\tfont-size: 20px;
  2759. \t\t\t\tfont-weight: 700;
  2760. \t\t\t\tcolor: #ffa200;
  2761. \t\t\t}
  2762. \t\t\t.dropship-message {
  2763. \t\t\t\tpadding: 15px;
  2764. \t\t\t\tbackground: #fff9e6;
  2765. \t\t\t\tborder-left: 4px solid #ffa200;
  2766. \t\t\t\tborder-radius: 6px;
  2767. \t\t\t}
  2768. \t\t\t.dropship-message p {
  2769. \t\t\t\tmargin: 0;
  2770. \t\t\t\tcolor: #666;
  2771. \t\t\t\tline-height: 1.6;
  2772. \t\t\t}
  2773. \t\t\t.dropship-modal-footer {
  2774. \t\t\t\tpadding: 20px 30px 30px;
  2775. \t\t\t\tdisplay: flex;
  2776. \t\t\t\tgap: 12px;
  2777. \t\t\t\tjustify-content: flex-end;
  2778. \t\t\t\tborder-top: 1px solid #f0f0f0;
  2779. \t\t\t}
  2780. \t\t\t.dropship-modal-footer .btn {
  2781. \t\t\t\tpadding: 12px 24px;
  2782. \t\t\t\tborder-radius: 8px;
  2783. \t\t\t\tfont-weight: 500;
  2784. \t\t\t\tborder: none;
  2785. \t\t\t\tcursor: pointer;
  2786. \t\t\t\ttransition: all 0.2s;
  2787. \t\t\t\tfont-size: 15px;
  2788. \t\t\t}
  2789. \t\t\t.dropship-modal-footer .btn-secondary {
  2790. \t\t\t\tbackground: #f0f0f0;
  2791. \t\t\t\tcolor: #666;
  2792. \t\t\t}
  2793. \t\t\t.dropship-modal-footer .btn-secondary:hover {
  2794. \t\t\t\tbackground: #e0e0e0;
  2795. \t\t\t}
  2796. \t\t\t.dropship-modal-footer .btn-primary {
  2797. \t\t\t\tbackground: #ffa200;
  2798. \t\t\t\tcolor: white;
  2799. \t\t\t}
  2800. \t\t\t.dropship-modal-footer .btn-primary:hover {
  2801. \t\t\t\tbackground: #e8910a;
  2802. \t\t\t\ttransform: translateY(-2px);
  2803. \t\t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.3);
  2804. \t\t\t}
  2805. \t\t\t.dropship-modal-footer .btn-primary:disabled {
  2806. \t\t\t\topacity: 0.6;
  2807. \t\t\t\tcursor: not-allowed;
  2808. \t\t\t\ttransform: none;
  2809. \t\t\t}
  2810. \t\t</style>
  2811. \t\t<script>
  2812. \t\t\t// Afficher le modal automatiquement au chargement de la page
  2813. document.addEventListener('DOMContentLoaded', function () {
  2814. const dropshipModal = document.getElementById('dropship-modal');
  2815. if (dropshipModal) {
  2816. setTimeout(function () {
  2817. dropshipModal.style.display = 'flex';
  2818. document.body.style.overflow = 'hidden';
  2819. }, 500); // Délai de 500ms pour une meilleure UX
  2820. }
  2821. });
  2822. function closeDropshipModal () {
  2823. const dropshipModal = document.getElementById('dropship-modal');
  2824. if (dropshipModal) {
  2825. dropshipModal.style.display = 'none';
  2826. document.body.style.overflow = 'auto';
  2827. }
  2828. }
  2829. function addToCartFromDropship () {
  2830. const addBtn = document.querySelector('.dropship-add-to-cart');
  2831. if (! addBtn) 
  2832. return;
  2833. // Désactiver le bouton pendant le traitement
  2834. addBtn.disabled = true;
  2835. addBtn.innerHTML = '<i class=\"fas fa-spinner fa-spin\"></i> Ajout en cours...';
  2836. const productId = ";
  2837.             // line 2515
  2838.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["product"] ?? null), "id", [], "any"falsefalsefalse2515), "html"nulltrue);
  2839.             yield ";
  2840. const qty = parseInt(document.getElementById('sst') ?. value || '1');
  2841. // Utiliser fetch pour ajouter au panier
  2842. fetch('";
  2843.             // line 2519
  2844.             yield $this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cart_add");
  2845.             yield "', {
  2846. method: 'POST',
  2847. headers: {
  2848. 'Content-Type': 'application/x-www-form-urlencoded',
  2849. 'X-Requested-With': 'XMLHttpRequest'
  2850. },
  2851. body: new URLSearchParams(
  2852. {productId: productId, qty: qty}
  2853. )
  2854. }).then(response => response.json()).then(data => {
  2855. if (data.ok) { // Afficher un message de succès
  2856. const modalBody = document.querySelector('.dropship-modal-body');
  2857. if (modalBody) {
  2858. modalBody.innerHTML = `
  2859. \t\t\t\t\t\t\t\t<div style=\"text-align: center; padding: 30px 20px;\">
  2860. \t\t\t\t\t\t\t\t\t<div style=\"width: 80px; height: 80px; margin: 0 auto 20px; background: #d4edda; border-radius: 50%; display: flex; align-items: center; justify-content: center;\">
  2861. \t\t\t\t\t\t\t\t\t\t<i class=\"fas fa-check\" style=\"font-size: 40px; color: #28a745;\"></i>
  2862. \t\t\t\t\t\t\t\t\t</div>
  2863. \t\t\t\t\t\t\t\t\t<h3 style=\"color: #28a745; margin-bottom: 10px;\">Produit ajouté avec succès !</h3>
  2864. \t\t\t\t\t\t\t\t\t<p style=\"color: #666; margin-bottom: 20px;\">Merci d'avoir utilisé le lien de recommandation de <strong>";
  2865.             // line 2538
  2866.             yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env$this->source, ($context["dropshipReferral"] ?? null), "affiliateName", [], "any"falsefalsefalse2538), "html"nulltrue);
  2867.             yield "</strong></p>
  2868. \t\t\t\t\t\t\t\t\t<p style=\"color: #666; font-size: 14px;\">Vous pouvez continuer vos achats ou aller directement au panier.</p>
  2869. \t\t\t\t\t\t\t\t</div>
  2870. \t\t\t\t\t\t\t`;
  2871. }
  2872. const modalFooter = document.querySelector('.dropship-modal-footer');
  2873. if (modalFooter) {
  2874. modalFooter.innerHTML = `
  2875. \t\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-secondary\" onclick=\"closeDropshipModal()\">
  2876. \t\t\t\t\t\t\t\t\tContinuer les achats
  2877. \t\t\t\t\t\t\t\t</button>
  2878. \t\t\t\t\t\t\t\t<a href=\"";
  2879.             // line 2550
  2880.             yield $this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("cart");
  2881.             yield "\" class=\"btn btn-primary\" style=\"text-decoration: none; display: inline-block;\">
  2882. \t\t\t\t\t\t\t\t\t<i class=\"fas fa-shopping-cart\"></i> Voir le panier
  2883. \t\t\t\t\t\t\t\t</a>
  2884. \t\t\t\t\t\t\t`;
  2885. }
  2886. // Mettre à jour le compteur du panier si présent
  2887. const cartCount = document.querySelector('.cart-count, .cart_count, [data-cart-count]');
  2888. if (cartCount) {
  2889. cartCount.textContent = data.totalQty || 0;
  2890. }
  2891. } else {
  2892. showCustomAlert('Erreur lors de l\\'ajout au panier. Veuillez réessayer.', 'error');
  2893. addBtn.disabled = false;
  2894. addBtn.innerHTML = '<i class=\"fas fa-shopping-cart\"></i> Ajouter au panier';
  2895. }
  2896. }).catch(error => {
  2897. console.error('Erreur:', error);
  2898. showCustomAlert('Une erreur est survenue. Veuillez réessayer.', 'error');
  2899. addBtn.disabled = false;
  2900. addBtn.innerHTML = '<i class=\"fas fa-shopping-cart\"></i> Ajouter au panier';
  2901. });
  2902. }
  2903. // Fermer le modal en cliquant sur l'overlay
  2904. document.addEventListener('DOMContentLoaded', function () {
  2905. const dropshipModal = document.getElementById('dropship-modal');
  2906. if (dropshipModal) {
  2907. const overlay = dropshipModal.querySelector('.dropship-modal-overlay');
  2908. if (overlay) {
  2909. overlay.addEventListener('click', function () {
  2910. closeDropshipModal();
  2911. });
  2912. }
  2913. }
  2914. });
  2915. \t\t</script>
  2916. \t";
  2917.         }
  2918.         // line 2588
  2919.         yield "
  2920. \t<!-- Le Modal personnalisé et showCustomAlert ont été déplacés dans base_home.html.twig pour une portée globale -->
  2921. \t<script>
  2922. \t\t// Remplacer window.alert par showCustomAlert
  2923. \t\twindow.alert = window.showCustomAlert || function(message) { console.log(\"Alert:\", message); };
  2924. \t</script>
  2925. ";
  2926.         
  2927.         $__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
  2928.         
  2929.         $__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
  2930.         yield from [];
  2931.     }
  2932.     /**
  2933.      * @codeCoverageIgnore
  2934.      */
  2935.     public function getTemplateName(): string
  2936.     {
  2937.         return "home/single-product.html.twig";
  2938.     }
  2939.     /**
  2940.      * @codeCoverageIgnore
  2941.      */
  2942.     public function isTraitable(): bool
  2943.     {
  2944.         return false;
  2945.     }
  2946.     /**
  2947.      * @codeCoverageIgnore
  2948.      */
  2949.     public function getDebugInfo(): array
  2950.     {
  2951.         return array (  3218 => 2588,  3177 => 2550,  3162 => 2538,  3140 => 2519,  3133 => 2515,  2879 => 2264,  2869 => 2257,  2864 => 2255,  2861 => 2254,  2855 => 2250,  2844 => 2245,  2842 => 2244,  2834 => 2239,  2820 => 2227,  2818 => 2226,  2811 => 2222,  2328 => 1741,  2227 => 1643,  1892 => 1310,  1877 => 1308,  1875 => 1307,  1872 => 1306,  1855 => 1305,  1842 => 1294,  1840 => 1293,  1747 => 1202,  1740 => 1198,  1737 => 1197,  1722 => 1184,  1712 => 1180,  1705 => 1179,  1701 => 1178,  1693 => 1172,  1691 => 1171,  1684 => 1166,  1677 => 1161,  1670 => 1157,  1665 => 1155,  1659 => 1151,  1657 => 1150,  1650 => 1145,  1646 => 1143,  1638 => 1139,  1626 => 1137,  1622 => 1136,  1619 => 1135,  1617 => 1134,  1611 => 1131,  1601 => 1124,  1597 => 1123,  1585 => 1113,  1578 => 1109,  1575 => 1108,  1559 => 1094,  1557 => 1093,  1547 => 1085,  1543 => 1083,  1537 => 1079,  1534 => 1078,  1524 => 1074,  1518 => 1071,  1514 => 1069,  1509 => 1068,  1506 => 1067,  1493 => 1056,  1490 => 1055,  1484 => 1051,  1478 => 1049,  1474 => 1047,  1472 => 1046,  1469 => 1045,  1467 => 1044,  1464 => 1043,  1462 => 1042,  1454 => 1036,  1451 => 1035,  1443 => 1030,  1436 => 1025,  1433 => 1024,  1427 => 1020,  1422 => 1018,  1419 => 1017,  1416 => 1016,  1411 => 1014,  1408 => 1013,  1405 => 1012,  1399 => 1010,  1397 => 1009,  1389 => 1003,  1386 => 1002,  1378 => 997,  1371 => 992,  1368 => 991,  1361 => 987,  1354 => 982,  1351 => 981,  1344 => 977,  1337 => 972,  1334 => 971,  1327 => 967,  1320 => 962,  1317 => 961,  1310 => 957,  1303 => 952,  1301 => 951,  1296 => 948,  1294 => 947,  1288 => 943,  1284 => 941,  1278 => 938,  1275 => 937,  1273 => 936,  1264 => 930,  1227 => 898,  1213 => 895,  1206 => 891,  1202 => 889,  1192 => 882,  1183 => 876,  1174 => 870,  1168 => 866,  1166 => 865,  1162 => 863,  1156 => 860,  1153 => 859,  1151 => 858,  1134 => 844,  1126 => 838,  1117 => 835,  1113 => 834,  1107 => 832,  1103 => 831,  1088 => 819,  1082 => 816,  1079 => 815,  1076 => 814,  1074 => 812,  1073 => 811,  1072 => 810,  1071 => 809,  1069 => 808,  1067 => 807,  1064 => 806,  1058 => 805,  1055 => 804,  1050 => 803,  1047 => 802,  1044 => 801,  1039 => 798,  1027 => 789,  1023 => 788,  1016 => 783,  1012 => 781,  1006 => 778,  1001 => 777,  999 => 776,  989 => 769,  983 => 766,  974 => 760,  963 => 752,  958 => 750,  948 => 743,  918 => 724,  909 => 717,  901 => 715,  893 => 713,  891 => 712,  879 => 702,  871 => 699,  866 => 698,  863 => 697,  842 => 694,  831 => 693,  813 => 692,  811 => 691,  776 => 658,  763 => 657,  106 => 9,  93 => 8,  78 => 4,  65 => 3,  42 => 1,);
  2952.     }
  2953.     public function getSourceContext(): Source
  2954.     {
  2955.         return new Source("{% extends 'base_home.html.twig' %}
  2956. {% block title %}
  2957. \t{{ product.name }}
  2958. \t| MaketOu
  2959. {% endblock %}
  2960. {% block stylesheets %}
  2961. \t<style>
  2962. \t\t/* Styles pour l'affichage de la boutique */
  2963. \t\t.shop-info {
  2964. \t\t\tpadding: 10px 0;
  2965. \t\t\tborder-bottom: 1px solid #f0f0f0;
  2966. \t\t\tmargin-bottom: 15px;
  2967. \t\t}
  2968. \t\t.shop-link {
  2969. \t\t\tcolor: #ffa200;
  2970. \t\t\ttext-decoration: none;
  2971. \t\t\tfont-weight: 500;
  2972. \t\t}
  2973. \t\t.shop-link:hover {
  2974. \t\t\tcolor: #e8910a;
  2975. \t\t\ttext-decoration: underline;
  2976. \t\t}
  2977. \t\t/* Styles pour les statistiques du produit */
  2978. \t\t.product-stats {
  2979. \t\t\tbackground: #f8f9fa;
  2980. \t\t\tpadding: 15px;
  2981. \t\t\tborder-radius: 8px;
  2982. \t\t\tborder: 1px solid #e9ecef;
  2983. \t\t}
  2984. \t\t.product-stats .stat-item {
  2985. \t\t\ttext-align: center;
  2986. \t\t}
  2987. \t\t.product-stats .stat-number {
  2988. \t\t\tdisplay: block;
  2989. \t\t\tfont-size: 1.2rem;
  2990. \t\t\tfont-weight: bold;
  2991. \t\t\tcolor: #ffa200;
  2992. \t\t}
  2993. \t\t.product-stats .stat-label {
  2994. \t\t\tcolor: #666;
  2995. \t\t\tfont-size: 0.8rem;
  2996. \t\t\ttext-transform: uppercase;
  2997. \t\t\tletter-spacing: 0.5px;
  2998. \t\t}
  2999. \t\t/* Message de confirmation */
  3000. \t\t#cart-message {
  3001. \t\t\tborder: none;
  3002. \t\t\tbackground: #d4edda;
  3003. \t\t\tcolor: #155724;
  3004. \t\t\tborder-radius: 6px;
  3005. \t\t\tpadding: 12px 16px;
  3006. \t\t\tmargin-top: 15px;
  3007. \t\t}
  3008. \t\t/* Amélioration des boutons d'actions */
  3009. \t\t.card_area {
  3010. \t\t\tmargin-top: 20px;
  3011. \t\t\tgap: 12px;
  3012. \t\t}
  3013. \t\t.card_area .primary-btn {
  3014. \t\t\tbackground: #ffa200;
  3015. \t\t\tborder: none;
  3016. \t\t\tpadding: 0 30px;
  3017. \t\t\theight: 50px;
  3018. \t\t\tdisplay: inline-flex;
  3019. \t\t\talign-items: center;
  3020. \t\t\tjustify-content: center;
  3021. \t\t\tborder-radius: 8px;
  3022. \t\t\tcolor: white;
  3023. \t\t\tfont-weight: 600;
  3024. \t\t\tfont-size: 16px;
  3025. \t\t\ttransition: all 0.3s ease;
  3026. \t\t\ttext-decoration: none;
  3027. \t\t\tgap: 8px;
  3028. \t\t\tflex: 1;
  3029. \t\t\tmax-width: 250px;
  3030. \t\t}
  3031. \t\t.card_area .primary-btn i {
  3032. \t\t\tfont-size: 20px;
  3033. \t\t}
  3034. \t\t.card_area .primary-btn:hover {
  3035. \t\t\tbackground: #e8910a;
  3036. \t\t\ttransform: translateY(-2px);
  3037. \t\t\tbox-shadow: 0 4px 15px rgba(255, 162, 0, 0.3);
  3038. \t\t\tcolor: white;
  3039. \t\t}
  3040. \t\t.card_area .icon_btn {
  3041. \t\t\tdisplay: inline-flex;
  3042. \t\t\talign-items: center;
  3043. \t\t\tjustify-content: center;
  3044. \t\t\twidth: 50px;
  3045. \t\t\theight: 50px;
  3046. \t\t\tborder-radius: 8px;
  3047. \t\t\tbackground: #fff;
  3048. \t\t\tcolor: #333;
  3049. \t\t\tborder: 1px solid #ddd;
  3050. \t\t\ttransition: all 0.3s ease;
  3051. \t\t\ttext-decoration: none;
  3052. \t\t}
  3053. \t\t.card_area .icon_btn i {
  3054. \t\t\tfont-size: 22px;
  3055. \t\t}
  3056. \t\t.card_area .icon_btn:hover {
  3057. \t\t\tbackground: #ffa200;
  3058. \t\t\tcolor: white;
  3059. \t\t\tborder-color: #ffa200;
  3060. \t\t\ttransform: translateY(-2px);
  3061. \t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.2);
  3062. \t\t}
  3063. \t\t/* Styles pour la galerie moderne de produits */
  3064. \t\t.product-gallery-modern {
  3065. \t\t\tdisplay: flex;
  3066. \t\t\tgap: 15px;
  3067. \t\t\tposition: relative;
  3068. \t\t\talign-items: stretch;
  3069. \t\t}
  3070. \t\t.thumbnails-container {
  3071. \t\t\tposition: relative;
  3072. \t\t\tdisplay: flex;
  3073. \t\t\tflex-direction: column;
  3074. \t\t\talign-items: center;
  3075. \t\t\twidth: 80px;
  3076. \t\t\tflex-shrink: 0;
  3077. \t\t\theight: auto;
  3078. \t\t}
  3079. \t\t/* Les boutons sont toujours visibles (meilleur pour le mobile et l'UX) */
  3080. \t\t.thumbnails-container .thumbnail-nav-btn {
  3081. \t\t\ttransition: all 0.2s ease;
  3082. \t\t}
  3083. \t\t.product-thumbnails {
  3084. \t\t\tdisplay: flex;
  3085. \t\t\tflex-direction: column;
  3086. \t\t\tgap: 10px;
  3087. \t\t\twidth: 100%;
  3088. \t\t\tflex: 1;
  3089. \t\t\toverflow: hidden;
  3090. \t\t\tposition: relative;
  3091. \t\t}
  3092. \t\t.thumbnails-wrapper {
  3093. \t\t\tdisplay: flex;
  3094. \t\t\tflex-direction: column;
  3095. \t\t\tgap: 10px;
  3096. \t\t\ttransition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  3097. \t\t\twill-change: transform;
  3098. \t\t}
  3099. \t\t.thumbnail-nav-btn {
  3100. \t\t\twidth: 30px;
  3101. \t\t\theight: 30px;
  3102. \t\t\tborder-radius: 50%;
  3103. \t\t\tbackground: white;
  3104. \t\t\tborder: 2px solid #e0e0e0;
  3105. \t\t\tdisplay: flex !important;
  3106. \t\t\talign-items: center;
  3107. \t\t\tjustify-content: center;
  3108. \t\t\tcursor: pointer;
  3109. \t\t\ttransition: all 0.3s ease;
  3110. \t\t\tmargin: 0;
  3111. \t\t\tpadding: 0;
  3112. \t\t\tcolor: #666;
  3113. \t\t\tz-index: 10;
  3114. \t\t\tbox-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  3115. \t\t\tposition: relative;
  3116. \t\t\tflex-shrink: 0;
  3117. \t\t}
  3118. \t\t.thumbnail-nav-btn:hover {
  3119. \t\t\tbackground: #ffa200;
  3120. \t\t\tborder-color: #ffa200;
  3121. \t\t\tcolor: white;
  3122. \t\t\ttransform: scale(1.1);
  3123. \t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.3);
  3124. \t\t}
  3125. \t\t.thumbnail-nav-btn:active {
  3126. \t\t\ttransform: scale(0.95);
  3127. \t\t}
  3128. \t\t.thumbnail-nav-btn.disabled {
  3129. \t\t\topacity: 0.3;
  3130. \t\t\tcursor: not-allowed;
  3131. \t\t\tpointer-events: none;
  3132. \t\t\tfilter: grayscale(1);
  3133. \t\t}
  3134. \t\t.thumbnail-nav-btn i {
  3135. \t\t\tfont-size: 20px;
  3136. \t\t\tfont-weight: bold;
  3137. \t\t}
  3138. \t\t.thumbnail-item {
  3139. \t\t\twidth: 80px;
  3140. \t\t\theight: 80px;
  3141. \t\t\tmin-width: 80px;
  3142. \t\t\tmin-height: 80px;
  3143. \t\t\tmax-width: 80px;
  3144. \t\t\tmax-height: 80px;
  3145. \t\t\tborder: 2px solid #e0e0e0;
  3146. \t\t\tborder-radius: 8px;
  3147. \t\t\toverflow: hidden;
  3148. \t\t\tcursor: pointer;
  3149. \t\t\ttransition: border-color 0.3s ease, border-width 0.3s ease, box-shadow 0.3s ease;
  3150. \t\t\tposition: relative;
  3151. \t\t\tbackground: #fff;
  3152. \t\t\tborder-bottom: 2px solid #e0e0e0;
  3153. \t\t\tflex-shrink: 0;
  3154. \t\t}
  3155. \t\t.thumbnail-item:hover {
  3156. \t\t\tborder-color: #ffa200;
  3157. \t\t\tborder-bottom-color: #ffa200;
  3158. \t\t\tborder-width: 3px;
  3159. \t\t\tbox-shadow: 0 0 0 2px rgba(255, 162, 0, 0.2);
  3160. \t\t}
  3161. \t\t.thumbnail-item.active {
  3162. \t\t\tborder-color: #ffa200 !important;
  3163. \t\t\tborder-bottom-color: #ffa200 !important;
  3164. \t\t\tborder-width: 3px !important;
  3165. \t\t\tbox-shadow: 0 0 0 2px rgba(255, 162, 0, 0.2) !important;
  3166. \t\t}
  3167. \t\t.thumbnail-item::after,
  3168. \t\t.thumbnail-item::before {
  3169. \t\t\tdisplay: none !important;
  3170. \t\t\tcontent: none !important;
  3171. \t\t}
  3172. \t\t.thumbnail-item img {
  3173. \t\t\twidth: 100%;
  3174. \t\t\theight: 100%;
  3175. \t\t\tobject-fit: cover;
  3176. \t\t\ttransition: transform 0.3s ease;
  3177. \t\t}
  3178. \t\t.thumbnail-item:hover img {
  3179. \t\t\ttransform: scale(1.1);
  3180. \t\t}
  3181. \t\t.thumbnail-item.active img {
  3182. \t\t\ttransform: scale(1.1);
  3183. \t\t}
  3184. \t\t.product-main-image-wrapper {
  3185. \t\t\tflex: 1;
  3186. \t\t\tposition: relative;
  3187. \t\t\tbackground: #fff;
  3188. \t\t\tborder-radius: 12px;
  3189. \t\t\toverflow: hidden;
  3190. \t\t\tborder: 1px solid #e0e0e0;
  3191. \t\t\tcursor: crosshair;
  3192. \t\t}
  3193. \t\t.product-main-image {
  3194. \t\t\tposition: relative;
  3195. \t\t\twidth: 100%;
  3196. \t\t\tpadding-top: 100%;
  3197. \t\t\toverflow: hidden;
  3198. \t\t\tbackground: #f8f9fa;
  3199. \t\t}
  3200. \t\t.product-main-image img {
  3201. \t\t\tposition: absolute;
  3202. \t\t\ttop: 0;
  3203. \t\t\tleft: 0;
  3204. \t\t\twidth: 100%;
  3205. \t\t\theight: 100%;
  3206. \t\t\tobject-fit: contain;
  3207. \t\t\ttransition: transform 0.15s ease-out;
  3208. \t\t\ttransform-origin: center center;
  3209. \t\t}
  3210. \t\t/* Supprimer le scale basique, géré en JS */
  3211. \t\t/* .product-main-image:hover img {
  3212. \t\t\ttransform: scale(1.05);
  3213. \t\t} */
  3214. \t\t.image-overlay-icons {
  3215. \t\t\tposition: absolute;
  3216. \t\t\ttop: 15px;
  3217. \t\t\tright: 15px;
  3218. \t\t\tdisplay: flex;
  3219. \t\t\tflex-direction: column;
  3220. \t\t\tgap: 10px;
  3221. \t\t\tz-index: 20;
  3222. \t\t\tpointer-events: none;
  3223. \t\t}
  3224. \t\t.icon-btn {
  3225. \t\t\tpointer-events: all;
  3226. \t\t\twidth: 45px;
  3227. \t\t\theight: 45px;
  3228. \t\t\tborder-radius: 50%;
  3229. \t\t\tbackground: rgba(255, 255, 255, 0.95);
  3230. \t\t\tborder: none;
  3231. \t\t\tdisplay: flex;
  3232. \t\t\talign-items: center;
  3233. \t\t\tjustify-content: center;
  3234. \t\t\tpadding: 0;
  3235. \t\t\tcursor: pointer;
  3236. \t\t\ttransition: all 0.3s ease;
  3237. \t\t\tbox-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  3238. \t\t\tcolor: #333;
  3239. \t\t}
  3240. \t\t.icon-btn:hover {
  3241. \t\t\tbackground: #ffa200;
  3242. \t\t\tcolor: white;
  3243. \t\t\ttransform: scale(1.1);
  3244. \t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.4);
  3245. \t\t}
  3246. \t\t.icon-btn i {
  3247. \t\t\tfont-size: 18px;
  3248. \t\t}
  3249. \t\t.favorite-btn.active {
  3250. \t\t\tbackground: #ff6b6b;
  3251. \t\t\tcolor: white;
  3252. \t\t}
  3253. \t\t.image-counter {
  3254. \t\t\tposition: absolute;
  3255. \t\t\tbottom: 15px;
  3256. \t\t\tright: 15px;
  3257. \t\t\tbackground: rgba(0, 0, 0, 0.7);
  3258. \t\t\tcolor: white;
  3259. \t\t\tpadding: 6px 12px;
  3260. \t\t\tborder-radius: 20px;
  3261. \t\t\tfont-size: 13px;
  3262. \t\t\tfont-weight: 600;
  3263. \t\t}
  3264. \t\t/* Modal de zoom d'image avec transitions modernes */
  3265. \t\t.image-zoom-modal {
  3266. \t\t\tdisplay: none;
  3267. \t\t\tposition: fixed;
  3268. \t\t\tz-index: 9999;
  3269. \t\t\tleft: 0;
  3270. \t\t\ttop: 0;
  3271. \t\t\twidth: 100%;
  3272. \t\t\theight: 100%;
  3273. \t\t\tbackground: rgba(0, 0, 0, 0);
  3274. \t\t\toverflow: auto;
  3275. \t\t\topacity: 0;
  3276. \t\t\ttransition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), background 0.3s ease;
  3277. \t\t\tbackdrop-filter: blur(0px);
  3278. \t\t}
  3279. \t\t.image-zoom-modal.active {
  3280. \t\t\tdisplay: flex;
  3281. \t\t\talign-items: center;
  3282. \t\t\tjustify-content: center;
  3283. \t\t\topacity: 1;
  3284. \t\t\tbackground: rgba(0, 0, 0, 0.95);
  3285. \t\t\tbackdrop-filter: blur(10px);
  3286. \t\t}
  3287. \t\t.zoom-modal-content {
  3288. \t\t\tposition: relative;
  3289. \t\t\tmax-width: 90%;
  3290. \t\t\tmax-height: 90%;
  3291. \t\t\tmargin: auto;
  3292. \t\t\topacity: 0;
  3293. \t\t\ttransform: scale(0.8);
  3294. \t\t\ttransition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1), transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  3295. \t\t}
  3296. \t\t.image-zoom-modal.active .zoom-modal-content {
  3297. \t\t\topacity: 1;
  3298. \t\t\ttransform: scale(1);
  3299. \t\t}
  3300. \t\t.zoom-modal-content img {
  3301. \t\t\twidth: 100%;
  3302. \t\t\theight: auto;
  3303. \t\t\tmax-height: 90vh;
  3304. \t\t\tobject-fit: contain;
  3305. \t\t\tborder-radius: 8px;
  3306. \t\t\tbox-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  3307. \t\t\topacity: 0;
  3308. \t\t\ttransition: opacity 0.3s ease;
  3309. \t\t}
  3310. \t\t.zoom-modal-content img.loaded {
  3311. \t\t\topacity: 1;
  3312. \t\t}
  3313. \t\t/* Loader moderne */
  3314. \t\t.zoom-loader {
  3315. \t\t\tposition: absolute;
  3316. \t\t\ttop: 50%;
  3317. \t\t\tleft: 50%;
  3318. \t\t\ttransform: translate(-50%, -50%);
  3319. \t\t\tz-index: 1;
  3320. \t\t\tdisplay: flex;
  3321. \t\t\tflex-direction: column;
  3322. \t\t\talign-items: center;
  3323. \t\t\tjustify-content: center;
  3324. \t\t}
  3325. \t\t.zoom-loader-spinner {
  3326. \t\t\twidth: 60px;
  3327. \t\t\theight: 60px;
  3328. \t\t\tborder: 4px solid rgba(255, 255, 255, 0.1);
  3329. \t\t\tborder-top-color: #ffa200;
  3330. \t\t\tborder-radius: 50%;
  3331. \t\t\tanimation: spin 1s linear infinite;
  3332. \t\t}
  3333. \t\t@keyframes spin {
  3334. \t\t\tto {
  3335. \t\t\t\ttransform: rotate(360deg);
  3336. \t\t\t}
  3337. \t\t}
  3338. \t\t.zoom-loader-text {
  3339. \t\t\tcolor: white;
  3340. \t\t\tmargin-top: 15px;
  3341. \t\t\ttext-align: center;
  3342. \t\t\tfont-size: 14px;
  3343. \t\t\tfont-weight: 500;
  3344. \t\t}
  3345. \t\t.close-zoom {
  3346. \t\t\tposition: absolute;
  3347. \t\t\ttop: 30px;
  3348. \t\t\tright: 30px;
  3349. \t\t\tcolor: white;
  3350. \t\t\tfont-size: 32px;
  3351. \t\t\tfont-weight: 300;
  3352. \t\t\tcursor: pointer;
  3353. \t\t\tz-index: 10000;
  3354. \t\t\twidth: 50px;
  3355. \t\t\theight: 50px;
  3356. \t\t\tdisplay: flex;
  3357. \t\t\talign-items: center;
  3358. \t\t\tjustify-content: center;
  3359. \t\t\tbackground: rgba(255, 255, 255, 0.1);
  3360. \t\t\tborder: 2px solid rgba(255, 255, 255, 0.2);
  3361. \t\t\tborder-radius: 50%;
  3362. \t\t\ttransition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  3363. \t\t\topacity: 0;
  3364. \t\t\ttransform: scale(0.8);
  3365. \t\t}
  3366. \t\t.image-zoom-modal.active .close-zoom {
  3367. \t\t\topacity: 1;
  3368. \t\t\ttransform: scale(1);
  3369. \t\t\ttransition-delay: 0.2s;
  3370. \t\t}
  3371. \t\t.close-zoom:hover {
  3372. \t\t\tbackground: rgba(255, 162, 0, 0.9);
  3373. \t\t\tborder-color: #ffa200;
  3374. \t\t\ttransform: scale(1.1) rotate(90deg);
  3375. \t\t\tbox-shadow: 0 4px 20px rgba(255, 162, 0, 0.4);
  3376. \t\t}
  3377. \t\t/* Boutons de navigation sur l'image principale */
  3378. \t\t.main-image-nav {
  3379. \t\t\tposition: absolute;
  3380. \t\t\ttop: 0;
  3381. \t\t\tbottom: 0;
  3382. \t\t\tleft: 0;
  3383. \t\t\tright: 0;
  3384. \t\t\tdisplay: flex;
  3385. \t\t\talign-items: center;
  3386. \t\t\tjustify-content: space-between;
  3387. \t\t\tpadding: 0 15px;
  3388. \t\t\tpointer-events: none;
  3389. \t\t\tz-index: 15;
  3390. \t\t}
  3391. \t\t.main-image-nav-btn {
  3392. \t\t\tpointer-events: all;
  3393. \t\t\twidth: 44px;
  3394. \t\t\theight: 44px;
  3395. \t\t\tborder-radius: 50%;
  3396. \t\t\tbackground: rgba(255, 255, 255, 0.95);
  3397. \t\t\tborder: 2px solid #e0e0e0;
  3398. \t\t\tdisplay: flex;
  3399. \t\t\talign-items: center;
  3400. \t\t\tjustify-content: center;
  3401. \t\t\tpadding: 0;
  3402. \t\t\tbox-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
  3403. \t\t\tcolor: #333;
  3404. \t\t\ttransition: all 0.2s ease;
  3405. \t\t}
  3406. \t\t.main-image-nav-btn:hover {
  3407. \t\t\tbackground: #ffa200;
  3408. \t\t\tborder-color: #ffa200;
  3409. \t\t\tcolor: #fff;
  3410. \t\t\ttransform: scale(1.08);
  3411. \t\t}
  3412. \t\t.main-image-nav-btn:active {
  3413. \t\t\ttransform: scale(0.95);
  3414. \t\t}
  3415. \t\t/* Modal personnalisé pour remplacer les alert */
  3416. \t\t.custom-alert-modal .modal-content {
  3417. \t\t\tborder-radius: 12px;
  3418. \t\t\tborder: none;
  3419. \t\t\tbox-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  3420. \t\t}
  3421. \t\t.custom-alert-modal .modal-header {
  3422. \t\t\tborder-bottom: none;
  3423. \t\t\tpadding: 1.5rem 1.5rem 0.5rem;
  3424. \t\t\tbackground: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  3425. \t\t\tcolor: white;
  3426. \t\t\tborder-radius: 12px 12px 0 0;
  3427. \t\t}
  3428. \t\t.custom-alert-modal .modal-header .btn-close {
  3429. \t\t\tfilter: brightness(0) invert(1);
  3430. \t\t\topacity: 0.8;
  3431. \t\t}
  3432. \t\t.custom-alert-modal .modal-header .btn-close:hover {
  3433. \t\t\topacity: 1;
  3434. \t\t}
  3435. \t\t.custom-alert-modal .modal-body {
  3436. \t\t\tpadding: 1.5rem;
  3437. \t\t\ttext-align: center;
  3438. \t\t}
  3439. \t\t.custom-alert-modal .modal-body .alert-icon {
  3440. \t\t\tfont-size: 3rem;
  3441. \t\t\tmargin-bottom: 1rem;
  3442. \t\t}
  3443. \t\t.custom-alert-modal .modal-body .alert-icon.success {
  3444. \t\t\tcolor: #28a745;
  3445. \t\t}
  3446. \t\t.custom-alert-modal .modal-body .alert-icon.error {
  3447. \t\t\tcolor: #dc3545;
  3448. \t\t}
  3449. \t\t.custom-alert-modal .modal-body .alert-icon.warning {
  3450. \t\t\tcolor: #ffc107;
  3451. \t\t}
  3452. \t\t.custom-alert-modal .modal-body .alert-icon.info {
  3453. \t\t\tcolor: #17a2b8;
  3454. \t\t}
  3455. \t\t.custom-alert-modal .modal-body .alert-message {
  3456. \t\t\tfont-size: 1.1rem;
  3457. \t\t\tcolor: #333;
  3458. \t\t\tmargin-bottom: 1rem;
  3459. \t\t\tline-height: 1.6;
  3460. \t\t}
  3461. \t\t.custom-alert-modal .modal-footer {
  3462. \t\t\tborder-top: none;
  3463. \t\t\tpadding: 0.5rem 1.5rem 1.5rem;
  3464. \t\t\tjustify-content: center;
  3465. \t\t}
  3466. \t\t.custom-alert-modal .modal-footer .btn {
  3467. \t\t\tmin-width: 120px;
  3468. \t\t\tpadding: 0.6rem 1.5rem;
  3469. \t\t\tborder-radius: 8px;
  3470. \t\t\tfont-weight: 500;
  3471. \t\t}
  3472. \t\t/* Responsive pour single-product */
  3473. \t\t@media(max-width: 991.98px) {
  3474. \t\t\t.product-gallery-modern {
  3475. \t\t\t\tflex-direction: column;
  3476. \t\t\t}
  3477. \t\t\t.thumbnails-container {
  3478. \t\t\t\tflex-direction: row;
  3479. \t\t\t\twidth: 100%;
  3480. \t\t\t\toverflow-x: auto;
  3481. \t\t\t\tpadding: 10px 0;
  3482. \t\t\t}
  3483. \t\t\t.thumbnail-item {
  3484. \t\t\t\tflex-shrink: 0;
  3485. \t\t\t}
  3486. \t\t\t.main-image-container {
  3487. \t\t\t\twidth: 100%;
  3488. \t\t\t}
  3489. \t\t}
  3490. \t\t@media(max-width: 768px) {
  3491. \t\t\t.product-gallery-modern {
  3492. \t\t\t\tflex-direction: column;
  3493. \t\t\t}
  3494. \t\t\t.thumbnails-container {
  3495. \t\t\t\twidth: 100%;
  3496. \t\t\t\tflex-direction: row;
  3497. \t\t\t\talign-items: center;
  3498. \t\t\t}
  3499. \t\t\t.product-thumbnails {
  3500. \t\t\t\tflex-direction: row;
  3501. \t\t\t\twidth: 100%;
  3502. \t\t\t\tmax-height: 100px;
  3503. \t\t\t\toverflow-x: auto;
  3504. \t\t\t\toverflow-y: hidden;
  3505. \t\t\t}
  3506. \t\t\t.thumbnail-item {
  3507. \t\t\t\tflex-shrink: 0;
  3508. \t\t\t}
  3509. \t\t\t.thumbnail-nav-btn {
  3510. \t\t\t\tmargin: 0 5px;
  3511. \t\t\t}
  3512. \t\t\t.thumbnail-nav-up {
  3513. \t\t\t\torder: 1;
  3514. \t\t\t}
  3515. \t\t\t.product-thumbnails {
  3516. \t\t\t\torder: 2;
  3517. \t\t\t}
  3518. \t\t\t.thumbnail-nav-down {
  3519. \t\t\t\torder: 3;
  3520. \t\t\t}
  3521. \t\t\t.image-overlay-icons {
  3522. \t\t\t\topacity: 1;
  3523. \t\t\t}
  3524. \t\t}
  3525. \t</style>
  3526. {% endblock %}
  3527. {% block body %}
  3528. \t<!-- Start Banner Area -->
  3529. \t<section class=\"banner-area organic-breadcrumb\">
  3530. \t\t<div class=\"container\">
  3531. \t\t\t<div class=\"breadcrumb-banner d-flex flex-wrap align-items-center justify-content-end\">
  3532. \t\t\t\t<div class=\"col-first\">
  3533. \t\t\t\t\t<h1>Product Details Page</h1>
  3534. \t\t\t\t\t<nav class=\"d-flex align-items-center\">
  3535. \t\t\t\t\t\t<a href=\"index.html\">Home<span class=\"lnr lnr-arrow-right\"></span>
  3536. \t\t\t\t\t\t</a>
  3537. \t\t\t\t\t\t<a href=\"#\">Shop<span class=\"lnr lnr-arrow-right\"></span>
  3538. \t\t\t\t\t\t</a>
  3539. \t\t\t\t\t\t<a href=\"single-product.html\">product-details</a>
  3540. \t\t\t\t\t</nav>
  3541. \t\t\t\t</div>
  3542. \t\t\t</div>
  3543. \t\t</div>
  3544. \t</section>
  3545. \t<!-- End Banner Area -->
  3546. \t<!--================Single Product Area =================-->
  3547. \t<div class=\"product_image_area\">
  3548. \t\t<div class=\"container\">
  3549. \t\t\t<div class=\"row s_product_inner\">
  3550. \t\t\t\t<div class=\"col-lg-6\">
  3551. \t\t\t\t\t<div
  3552. \t\t\t\t\t\tclass=\"product-gallery-modern\">
  3553. \t\t\t\t\t\t<!-- Colonne de miniatures à gauche avec navigation -->
  3554. \t\t\t\t\t\t<div class=\"thumbnails-container\">
  3555. \t\t\t\t\t\t\t<button class=\"thumbnail-nav-btn thumbnail-nav-up\" id=\"thumbnail-nav-up\" title=\"Image précédente\">
  3556. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-up\"></i>
  3557. \t\t\t\t\t\t\t</button>
  3558. \t\t\t\t\t\t\t<div class=\"product-thumbnails\" id=\"product-thumbnails\">
  3559. \t\t\t\t\t\t\t\t<div class=\"thumbnails-wrapper\" id=\"thumbnails-wrapper\">
  3560. \t\t\t\t\t\t\t\t\t{% if product.images is defined and product.images|length > 0 %}
  3561. \t\t\t\t\t\t\t\t\t\t{% for img in product.images %}
  3562. \t\t\t\t\t\t\t\t\t\t\t<div class=\"thumbnail-item {% if loop.first %}active permanently-active{% endif %}\" data-image=\"{{ asset(img) }}\" data-index=\"{{ loop.index0 }}\">
  3563. \t\t\t\t\t\t\t\t\t\t\t\t<img src=\"{{ asset(img) }}\" alt=\"{{ product.name }} - Image {{ loop.index }}\">
  3564. \t\t\t\t\t\t\t\t\t\t\t</div>
  3565. \t\t\t\t\t\t\t\t\t\t{% endfor %}
  3566. \t\t\t\t\t\t\t\t\t{% else %}
  3567. \t\t\t\t\t\t\t\t\t\t<div class=\"thumbnail-item active permanently-active\" data-image=\"{{ asset('ui/img/category/s-p1.jpg') }}\" data-index=\"0\">
  3568. \t\t\t\t\t\t\t\t\t\t\t<img src=\"{{ asset('ui/img/category/s-p1.jpg') }}\" alt=\"{{ product.name }}\">
  3569. \t\t\t\t\t\t\t\t\t\t</div>
  3570. \t\t\t\t\t\t\t\t\t{% endif %}
  3571. \t\t\t\t\t\t\t\t</div>
  3572. \t\t\t\t\t\t\t</div>
  3573. \t\t\t\t\t\t\t<button class=\"thumbnail-nav-btn thumbnail-nav-down\" id=\"thumbnail-nav-down\" title=\"Image suivante\">
  3574. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-down\"></i>
  3575. \t\t\t\t\t\t\t</button>
  3576. \t\t\t\t\t\t</div>
  3577. \t\t\t\t\t\t<!-- Grande image principale -->
  3578. \t\t\t\t\t\t<div class=\"product-main-image-wrapper\">
  3579. \t\t\t\t\t\t\t<div class=\"product-main-image\">
  3580. \t\t\t\t\t\t\t\t{% if product.images is defined and product.images|length > 0 %}
  3581. \t\t\t\t\t\t\t\t\t<img id=\"main-product-image\" src=\"{{ asset(product.images[0]) }}\" alt=\"{{ product.name }}\">
  3582. \t\t\t\t\t\t\t\t{% else %}
  3583. \t\t\t\t\t\t\t\t\t<img id=\"main-product-image\" src=\"{{ asset('ui/img/category/s-p1.jpg') }}\" alt=\"{{ product.name }}\">
  3584. \t\t\t\t\t\t\t\t{% endif %}
  3585. \t\t\t\t\t\t\t</div>
  3586. \t\t\t\t\t\t\t<!-- Overlay avec icônes -->
  3587. \t\t\t\t\t\t\t<div class=\"image-overlay-icons\">
  3588. \t\t\t\t\t\t\t\t<button class=\"icon-btn zoom-btn\" onclick=\"openImageZoom()\" title=\"Agrandir l'image\">
  3589. \t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-magnifier\"></i>
  3590. \t\t\t\t\t\t\t\t</button>
  3591. \t\t\t\t\t\t\t\t<button class=\"icon-btn favorite-btn\" {% if app.user %} onclick=\"toggleWishlist({{ product.id }}, this); return false;\" {% else %} onclick=\"showCustomAlert('Vous devez être connecté pour ajouter aux favoris', 'warning'); return false;\" {% endif %} title=\"Ajouter aux favoris\" data-product-id=\"{{ product.id }}\">
  3592. \t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-heart\"></i>
  3593. \t\t\t\t\t\t\t\t</button>
  3594. \t\t\t\t\t\t\t</div>
  3595. \t\t\t\t\t\t\t<!-- Navigation sur l'image principale -->
  3596. \t\t\t\t\t\t\t<div class=\"main-image-nav\">
  3597. \t\t\t\t\t\t\t\t<button type=\"button\" class=\"main-image-nav-btn\" id=\"main-image-prev\" title=\"Image précédente\">
  3598. \t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-left\"></i>
  3599. \t\t\t\t\t\t\t\t</button>
  3600. \t\t\t\t\t\t\t\t<button type=\"button\" class=\"main-image-nav-btn\" id=\"main-image-next\" title=\"Image suivante\">
  3601. \t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-right\"></i>
  3602. \t\t\t\t\t\t\t\t</button>
  3603. \t\t\t\t\t\t\t</div>
  3604. \t\t\t\t\t\t\t<!-- Indicateur d'image active -->
  3605. \t\t\t\t\t\t\t<div class=\"image-counter\">
  3606. \t\t\t\t\t\t\t\t<span id=\"current-image-index\">1</span>
  3607. \t\t\t\t\t\t\t\t/
  3608. \t\t\t\t\t\t\t\t<span id=\"total-images\">{{ (product.images|length) ?: 1 }}</span>
  3609. \t\t\t\t\t\t\t</div>
  3610. \t\t\t\t\t\t</div>
  3611. \t\t\t\t\t</div>
  3612. \t\t\t\t</div>
  3613. \t\t\t\t<div class=\"col-lg-5 offset-lg-1\">
  3614. \t\t\t\t\t<div class=\"s_product_text\">
  3615. \t\t\t\t\t\t<h3>{{ product.name }}</h3>
  3616. \t\t\t\t\t\t<h2>
  3617. \t\t\t\t\t\t\t<span id=\"main-unit-price\">{{ product.price|number_format(2, '.', ' ') }}</span>
  3618. \t\t\t\t\t\t\tHTG
  3619. \t\t\t\t\t\t</h2>
  3620. \t\t\t\t\t\t<ul class=\"list\">
  3621. \t\t\t\t\t\t\t<li>
  3622. \t\t\t\t\t\t\t\t<a class=\"active\" href=\"#\">
  3623. \t\t\t\t\t\t\t\t\t<span>Category</span>
  3624. \t\t\t\t\t\t\t\t\t:
  3625. \t\t\t\t\t\t\t\t\t{{ product.category.name }}</a>
  3626. \t\t\t\t\t\t\t</li>
  3627. \t\t\t\t\t\t\t<li>
  3628. \t\t\t\t\t\t\t\t<a href=\"#\">
  3629. \t\t\t\t\t\t\t\t\t<span>Disponibilité</span>
  3630. \t\t\t\t\t\t\t\t\t:
  3631. \t\t\t\t\t\t\t\t\t{{ product.stockStatus }}</a>
  3632. \t\t\t\t\t\t\t</li>
  3633. \t\t\t\t\t\t</ul>
  3634. \t\t\t\t\t\t<p>{{ product.description|raw|striptags|nl2br }}</p>
  3635. \t\t\t\t\t\t<!-- Informations sur la boutique -->
  3636. \t\t\t\t\t\t<div class=\"shop-info mb-3\">
  3637. \t\t\t\t\t\t\t<small class=\"text-muted\">
  3638. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-store\"></i>
  3639. \t\t\t\t\t\t\t\tVendu par :
  3640. \t\t\t\t\t\t\t\t{% if product.shop is defined and product.shop %}
  3641. \t\t\t\t\t\t\t\t\t<a href=\"{{ path('ui_shop_show', {'slug': product.shop.slug}) }}\" class=\"shop-link\">
  3642. \t\t\t\t\t\t\t\t\t\t{{ product.shop.name }}
  3643. \t\t\t\t\t\t\t\t\t</a>
  3644. \t\t\t\t\t\t\t\t{% else %}
  3645. \t\t\t\t\t\t\t\t\t<span class=\"text-muted\">Boutique inconnue</span>
  3646. \t\t\t\t\t\t\t\t{% endif %}
  3647. \t\t\t\t\t\t\t</small>
  3648. \t\t\t\t\t\t</div>
  3649. \t\t\t\t\t\t<div class=\"product_count\">
  3650. \t\t\t\t\t\t\t<label for=\"qty\">Quantité :</label>
  3651. \t\t\t\t\t\t\t<input type=\"text\" name=\"qty\" id=\"sst\" maxlength=\"12\" value=\"1\" title=\"Quantité:\" class=\"input-text qty\" max=\"{{ product.stock ?? 99 }}\">
  3652. \t\t\t\t\t\t\t<button onclick=\"var result = document.getElementById('sst'); var sst = result.value; if( !isNaN( sst ) && sst < {{ product.stock ?? 99 }}) result.value++;return false;\" class=\"increase items-count\" type=\"button\">
  3653. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-up\"></i>
  3654. \t\t\t\t\t\t\t</button>
  3655. \t\t\t\t\t\t\t<button onclick=\"var result = document.getElementById('sst'); var sst = result.value; if( !isNaN( sst ) && sst > 1 ) result.value--;return false;\" class=\"reduced items-count\" type=\"button\">
  3656. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-chevron-down\"></i>
  3657. \t\t\t\t\t\t\t</button>
  3658. \t\t\t\t\t\t</div>
  3659. \t\t\t\t\t\t<input
  3660. \t\t\t\t\t\ttype=\"hidden\" id=\"unit-price-input\" value=\"{{ product.price|number_format(2, '.', '') }}\"/>
  3661. \t\t\t\t\t\t{# Prix de gros (bulk pricing) #}
  3662. \t\t\t\t\t\t{% set tierPrices = [] %}
  3663. \t\t\t\t\t\t{% if product.tierPrices is defined and product.tierPrices|length > 0 %}
  3664. \t\t\t\t\t\t\t{% for tier in product.tierPrices %}
  3665. \t\t\t\t\t\t\t\t{% set tierPrices = tierPrices|merge([{ 'min': tier.minQty, 'price': tier.price }]) %}
  3666. \t\t\t\t\t\t\t{% endfor %}
  3667. \t\t\t\t\t\t{% else %}
  3668. \t\t\t\t\t\t\t{# Facultatif: exemple si pas de data backend #}
  3669. \t\t\t\t\t\t\t{% set tierPrices = [
  3670. \t\t\t\t\t\t\t\t{ 'min': 1, 'price': product.price },
  3671. \t\t\t\t\t\t\t\t{ 'min': 5, 'price': (product.price * 0.98)|round(2, 'floor') },
  3672. \t\t\t\t\t\t\t\t{ 'min': 10, 'price': (product.price * 0.95)|round(2, 'floor') },
  3673. \t\t\t\t\t\t\t\t{ 'min': 20, 'price': (product.price * 0.92)|round(2, 'floor') }
  3674. \t\t\t\t\t\t\t] %}
  3675. \t\t\t\t\t\t{% endif %}
  3676. \t\t\t\t\t\t<div id=\"bulk-pricing\" class=\"mt-3\" data-tiers='{{ tierPrices|json_encode|e('html_attr') }}'>
  3677. \t\t\t\t\t\t\t<div class=\"d-flex align-items-center mb-2\">
  3678. \t\t\t\t\t\t\t\t<strong class=\"me-2\">Prix unitaire selon quantité :</strong>
  3679. \t\t\t\t\t\t\t\t<span id=\"unit-price-value\">{{ product.price|number_format(2, '.', ' ') }}
  3680. \t\t\t\t\t\t\t\t\tHTG</span>
  3681. \t\t\t\t\t\t\t</div>
  3682. \t\t\t\t\t\t\t<div class=\"table-responsive\">
  3683. \t\t\t\t\t\t\t\t<table class=\"table table-sm mb-2\" style=\"border:1px solid #eee;\">
  3684. \t\t\t\t\t\t\t\t\t<thead class=\"table-light\">
  3685. \t\t\t\t\t\t\t\t\t\t<tr>
  3686. \t\t\t\t\t\t\t\t\t\t\t<th>Quantité min.</th>
  3687. \t\t\t\t\t\t\t\t\t\t\t<th>Prix unitaire (HTG)</th>
  3688. \t\t\t\t\t\t\t\t\t\t</tr>
  3689. \t\t\t\t\t\t\t\t\t</thead>
  3690. \t\t\t\t\t\t\t\t\t<tbody id=\"bulk-tier-rows\">
  3691. \t\t\t\t\t\t\t\t\t\t{% for tier in tierPrices|sort((a,b)=> a.min <=> b.min) %}
  3692. \t\t\t\t\t\t\t\t\t\t\t<tr data-min=\"{{ tier.min }}\">
  3693. \t\t\t\t\t\t\t\t\t\t\t\t<td>≥
  3694. \t\t\t\t\t\t\t\t\t\t\t\t\t{{ tier.min }}</td>
  3695. \t\t\t\t\t\t\t\t\t\t\t\t<td>{{ tier.price|number_format(2, '.', ' ') }}</td>
  3696. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3697. \t\t\t\t\t\t\t\t\t\t{% endfor %}
  3698. \t\t\t\t\t\t\t\t\t</tbody>
  3699. \t\t\t\t\t\t\t\t</table>
  3700. \t\t\t\t\t\t\t</div>
  3701. \t\t\t\t\t\t\t<div class=\"d-flex flex-wrap gap-3 align-items-baseline mb-2\">
  3702. \t\t\t\t\t\t\t\t<div>
  3703. \t\t\t\t\t\t\t\t\t<strong>Total:</strong>
  3704. \t\t\t\t\t\t\t\t\t<span id=\"total-price-value\">{{ product.price|number_format(2, '.', ' ') }}</span>
  3705. \t\t\t\t\t\t\t\t\tHTG
  3706. \t\t\t\t\t\t\t\t</div>
  3707. \t\t\t\t\t\t\t\t<div class=\"text-success\">
  3708. \t\t\t\t\t\t\t\t\t<strong>Économies:</strong>
  3709. \t\t\t\t\t\t\t\t\t<span id=\"savings-amount\">0.00</span>
  3710. \t\t\t\t\t\t\t\t\tHTG
  3711. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(<span id=\"savings-percent\">0</span>%)
  3712. \t\t\t\t\t\t\t\t</div>
  3713. \t\t\t\t\t\t\t</div>
  3714. \t\t\t\t\t\t\t<small class=\"text-muted\">Le prix et le total s’adaptent automatiquement selon la quantité.</small>
  3715. \t\t\t\t\t\t</div>
  3716. \t\t\t\t\t\t<!-- Stock disponible -->
  3717. \t\t\t\t\t\t{% if product.stock is defined %}
  3718. \t\t\t\t\t\t\t<small class=\"text-muted\">Stock disponible :
  3719. \t\t\t\t\t\t\t\t{{ product.stock }}
  3720. \t\t\t\t\t\t\t\tunités</small>
  3721. \t\t\t\t\t\t{% endif %}
  3722. \t\t\t\t\t\t<!-- Statistiques du produit -->
  3723. \t\t\t\t\t\t{% if productStats is defined %}
  3724. \t\t\t\t\t\t\t<div class=\"product-stats mt-3\">
  3725. \t\t\t\t\t\t\t\t<div class=\"row text-center\">
  3726. \t\t\t\t\t\t\t\t\t<div class=\"col-4\">
  3727. \t\t\t\t\t\t\t\t\t\t<div class=\"stat-item\">
  3728. \t\t\t\t\t\t\t\t\t\t\t<span class=\"stat-number\">{{ productStats.totalViews }}</span>
  3729. \t\t\t\t\t\t\t\t\t\t\t<small class=\"stat-label\">Vues</small>
  3730. \t\t\t\t\t\t\t\t\t\t</div>
  3731. \t\t\t\t\t\t\t\t\t</div>
  3732. \t\t\t\t\t\t\t\t\t<div class=\"col-4\">
  3733. \t\t\t\t\t\t\t\t\t\t<div class=\"stat-item\">
  3734. \t\t\t\t\t\t\t\t\t\t\t<span class=\"stat-number\">{{ productStats.salesCount }}</span>
  3735. \t\t\t\t\t\t\t\t\t\t\t<small class=\"stat-label\">Ventes</small>
  3736. \t\t\t\t\t\t\t\t\t\t</div>
  3737. \t\t\t\t\t\t\t\t\t</div>
  3738. \t\t\t\t\t\t\t\t\t<div class=\"col-4\">
  3739. \t\t\t\t\t\t\t\t\t\t<div class=\"stat-item\">
  3740. \t\t\t\t\t\t\t\t\t\t\t<span class=\"stat-number\">{{ productStats.conversionRate }}%</span>
  3741. \t\t\t\t\t\t\t\t\t\t\t<small class=\"stat-label\">Conversion</small>
  3742. \t\t\t\t\t\t\t\t\t\t</div>
  3743. \t\t\t\t\t\t\t\t\t</div>
  3744. \t\t\t\t\t\t\t\t</div>
  3745. \t\t\t\t\t\t\t</div>
  3746. \t\t\t\t\t\t{% endif %}
  3747. \t\t\t\t\t\t<div class=\"card_area d-flex align-items-center\">
  3748. \t\t\t\t\t\t\t<a class=\"primary-btn btn-add-to-cart\" href=\"javascript:void(0);\" id=\"add-to-cart-btn\" data-product-id=\"{{ product.id }}\" data-qty=\"1\">
  3749. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-cart\"></i>
  3750. \t\t\t\t\t\t\t\tAjouter au panier
  3751. \t\t\t\t\t\t\t</a>
  3752. \t\t\t\t\t\t\t<a class=\"icon_btn wishlist-btn\" href=\"#\" title=\"Ajouter aux favoris\" data-product-id=\"{{ product.id }}\" {% if app.user %} onclick=\"toggleWishlist({{ product.id }}, this); return false;\" {% else %} onclick=\"showCustomAlert('Vous devez être connecté pour ajouter aux favoris', 'warning'); return false;\" {% endif %}>
  3753. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-heart\"></i>
  3754. \t\t\t\t\t\t\t</a>
  3755. \t\t\t\t\t\t\t<a class=\"icon_btn comparison-btn\" href=\"#\" title=\"Comparer\" data-product-id=\"{{ product.id }}\" onclick=\"toggleComparison({{ product.id }}, this); return false;\">
  3756. \t\t\t\t\t\t\t\t<i class=\"lnr lnr-sync\"></i>
  3757. \t\t\t\t\t\t\t</a>
  3758. \t\t\t\t\t\t</div>
  3759. \t\t\t\t\t\t<!-- Message de confirmation -->
  3760. \t\t\t\t\t\t<div id=\"cart-message\" class=\"alert alert-success mt-3\" style=\"display: none;\">
  3761. \t\t\t\t\t\t\t<i class=\"lnr lnr-checkmark-circle\"></i>
  3762. \t\t\t\t\t\t\tProduit ajouté au panier avec succès !
  3763. \t\t\t\t\t\t</div>
  3764. \t\t\t\t\t\t<!-- ... existing code ... -->
  3765. \t\t\t\t\t</div>
  3766. \t\t\t\t</div>
  3767. \t\t\t</div>
  3768. \t\t</div>
  3769. \t</div>
  3770. \t<!--================End Single Product Area =================-->
  3771. \t<!--================Product Description Area =================-->
  3772. \t<section class=\"product_description_area\">
  3773. \t\t<div class=\"container\">
  3774. \t\t\t<ul class=\"nav nav-tabs\" id=\"myTab\" role=\"tablist\">
  3775. \t\t\t\t<li class=\"nav-item\">
  3776. \t\t\t\t\t<a class=\"nav-link active\" id=\"home-tab\" data-bs-toggle=\"tab\" href=\"#home\" role=\"tab\" aria-controls=\"home\" aria-selected=\"true\">Description</a>
  3777. \t\t\t\t</li>
  3778. \t\t\t\t<li class=\"nav-item\">
  3779. \t\t\t\t\t<a class=\"nav-link\" id=\"profile-tab\" data-bs-toggle=\"tab\" href=\"#profile\" role=\"tab\" aria-controls=\"profile\" aria-selected=\"false\">Spécifications</a>
  3780. \t\t\t\t</li>
  3781. \t\t\t\t<li class=\"nav-item\">
  3782. \t\t\t\t\t<a class=\"nav-link\" id=\"contact-tab\" data-bs-toggle=\"tab\" href=\"#contact\" role=\"tab\" aria-controls=\"contact\" aria-selected=\"false\">Commentaires</a>
  3783. \t\t\t\t</li>
  3784. \t\t\t\t<li class=\"nav-item\">
  3785. \t\t\t\t\t<a class=\"nav-link\" id=\"review-tab\" data-bs-toggle=\"tab\" href=\"#review\" role=\"tab\" aria-controls=\"review\" aria-selected=\"false\">Avis ({{ product.reviewCount ?? 0 }})</a>
  3786. \t\t\t\t</li>
  3787. \t\t\t</ul>
  3788. \t\t\t<div class=\"tab-content\" id=\"myTabContent\">
  3789. \t\t\t\t<div class=\"tab-pane fade show active\" id=\"home\" role=\"tabpanel\" aria-labelledby=\"home-tab\">
  3790. \t\t\t\t\t<div class=\"description\">
  3791. \t\t\t\t\t\t{% if product.description %}
  3792. \t\t\t\t\t\t\t<div class=\"product-description-content\">
  3793. \t\t\t\t\t\t\t\t{{ product.description|raw|nl2br }}
  3794. \t\t\t\t\t\t\t</div>
  3795. \t\t\t\t\t\t{% else %}
  3796. \t\t\t\t\t\t\t<p class=\"text-muted\">Aucune description disponible pour ce produit.</p>
  3797. \t\t\t\t\t\t{% endif %}
  3798. \t\t\t\t\t</div>
  3799. \t\t\t\t</div>
  3800. \t\t\t\t<div class=\"tab-pane fade\" id=\"profile\" role=\"tabpanel\" aria-labelledby=\"profile-tab\">
  3801. \t\t\t\t\t<div class=\"specification-table\">
  3802. \t\t\t\t\t\t{% if product.weight or product.length or product.width or product.height or product.attributes %}
  3803. \t\t\t\t\t\t\t<div class=\"table-responsive\">
  3804. \t\t\t\t\t\t\t\t<table class=\"table\">
  3805. \t\t\t\t\t\t\t\t\t<tbody>
  3806. \t\t\t\t\t\t\t\t\t\t{% if product.sku %}
  3807. \t\t\t\t\t\t\t\t\t\t\t<tr>
  3808. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3809. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Référence (SKU)</h5>
  3810. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3811. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3812. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>{{ product.sku }}</h5>
  3813. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3814. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3815. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3816. \t\t\t\t\t\t\t\t\t\t{% if product.barcode %}
  3817. \t\t\t\t\t\t\t\t\t\t\t<tr>
  3818. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3819. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Code-barres</h5>
  3820. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3821. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3822. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>{{ product.barcode }}</h5>
  3823. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3824. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3825. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3826. \t\t\t\t\t\t\t\t\t\t{% if product.brand %}
  3827. \t\t\t\t\t\t\t\t\t\t\t<tr>
  3828. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3829. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Marque</h5>
  3830. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3831. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3832. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>{{ product.brand.name }}</h5>
  3833. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3834. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3835. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3836. \t\t\t\t\t\t\t\t\t\t{% if product.category %}
  3837. \t\t\t\t\t\t\t\t\t\t\t<tr>
  3838. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3839. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Catégorie</h5>
  3840. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3841. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3842. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>{{ product.category.name }}</h5>
  3843. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3844. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3845. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3846. \t\t\t\t\t\t\t\t\t\t{% if product.weight %}
  3847. \t\t\t\t\t\t\t\t\t\t\t<tr>
  3848. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3849. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Poids</h5>
  3850. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3851. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3852. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>{{ product.weight }}
  3853. \t\t\t\t\t\t\t\t\t\t\t\t\t\tkg</h5>
  3854. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3855. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3856. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3857. \t\t\t\t\t\t\t\t\t\t{% if product.length or product.width or product.height %}
  3858. \t\t\t\t\t\t\t\t\t\t\t<tr>
  3859. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3860. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Dimensions</h5>
  3861. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3862. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3863. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>
  3864. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% if product.length %}
  3865. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ product.length }}cm
  3866. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% endif %}
  3867. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% if product.width %}
  3868. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t×
  3869. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ product.width }}cm
  3870. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% endif %}
  3871. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% if product.height %}
  3872. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t×
  3873. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ product.height }}cm
  3874. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% endif %}
  3875. \t\t\t\t\t\t\t\t\t\t\t\t\t</h5>
  3876. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3877. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3878. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3879. \t\t\t\t\t\t\t\t\t\t{% if product.stock is not null %}
  3880. \t\t\t\t\t\t\t\t\t\t\t<tr>
  3881. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3882. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Stock disponible</h5>
  3883. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3884. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3885. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>{{ product.stock }}
  3886. \t\t\t\t\t\t\t\t\t\t\t\t\t\tunité(s)</h5>
  3887. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3888. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3889. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3890. \t\t\t\t\t\t\t\t\t\t{% if product.stockStatus %}
  3891. \t\t\t\t\t\t\t\t\t\t\t<tr>
  3892. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3893. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Statut</h5>
  3894. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3895. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3896. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>
  3897. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% if product.stockStatus == 'in_stock' %}
  3898. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"badge bg-success\">En stock</span>
  3899. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% elseif product.stockStatus == 'out_of_stock' %}
  3900. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"badge bg-danger\">Rupture de stock</span>
  3901. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% elseif product.stockStatus == 'backorder' %}
  3902. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"badge bg-warning\">Sur commande</span>
  3903. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% else %}
  3904. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ product.stockStatus }}
  3905. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% endif %}
  3906. \t\t\t\t\t\t\t\t\t\t\t\t\t</h5>
  3907. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3908. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3909. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3910. \t\t\t\t\t\t\t\t\t\t{% if product.isDigital %}
  3911. \t\t\t\t\t\t\t\t\t\t\t<tr>
  3912. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3913. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>Type</h5>
  3914. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3915. \t\t\t\t\t\t\t\t\t\t\t\t<td>
  3916. \t\t\t\t\t\t\t\t\t\t\t\t\t<h5>
  3917. \t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"badge bg-info\">Produit digital</span>
  3918. \t\t\t\t\t\t\t\t\t\t\t\t\t</h5>
  3919. \t\t\t\t\t\t\t\t\t\t\t\t</td>
  3920. \t\t\t\t\t\t\t\t\t\t\t</tr>
  3921. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3922. \t\t\t\t\t\t\t\t\t\t{% if product.attributes is not empty %}
  3923. \t\t\t\t\t\t\t\t\t\t\t{% for key, value in product.attributes %}
  3924. \t\t\t\t\t\t\t\t\t\t\t\t<tr>
  3925. \t\t\t\t\t\t\t\t\t\t\t\t\t<td>
  3926. \t\t\t\t\t\t\t\t\t\t\t\t\t\t<h5>{{ key|replace({'_': ' '})|title }}</h5>
  3927. \t\t\t\t\t\t\t\t\t\t\t\t\t</td>
  3928. \t\t\t\t\t\t\t\t\t\t\t\t\t<td>
  3929. \t\t\t\t\t\t\t\t\t\t\t\t\t\t<h5>{{ value }}</h5>
  3930. \t\t\t\t\t\t\t\t\t\t\t\t\t</td>
  3931. \t\t\t\t\t\t\t\t\t\t\t\t</tr>
  3932. \t\t\t\t\t\t\t\t\t\t\t{% endfor %}
  3933. \t\t\t\t\t\t\t\t\t\t{% endif %}
  3934. \t\t\t\t\t\t\t\t\t</tbody>
  3935. \t\t\t\t\t\t\t\t</table>
  3936. \t\t\t\t\t\t\t</div>
  3937. \t\t\t\t\t\t{% else %}
  3938. \t\t\t\t\t\t\t<p class=\"text-muted\">Aucune spécification disponible pour ce produit.</p>
  3939. \t\t\t\t\t\t{% endif %}
  3940. \t\t\t\t\t</div>
  3941. \t\t\t\t</div>
  3942. \t\t\t\t<div class=\"tab-pane fade\" id=\"contact\" role=\"tabpanel\" aria-labelledby=\"contact-tab\">
  3943. \t\t\t\t\t<div class=\"comment-wrapper\">
  3944. \t\t\t\t\t\t<div class=\"alert alert-info\">
  3945. \t\t\t\t\t\t\t<i class=\"lnr lnr-info-circle\"></i>
  3946. \t\t\t\t\t\t\tLa fonctionnalité de commentaires sera bientôt disponible. Vous pourrez laisser des commentaires et poser des questions sur ce produit.
  3947. \t\t\t\t\t\t</div>
  3948. \t\t\t\t\t\t{% if app.user %}
  3949. \t\t\t\t\t\t\t<div class=\"review_box mt-4\">
  3950. \t\t\t\t\t\t\t\t<h4>Poser une question</h4>
  3951. \t\t\t\t\t\t\t\t<form class=\"row contact_form\" method=\"post\" novalidate=\"novalidate\">
  3952. \t\t\t\t\t\t\t\t\t<div class=\"col-md-12\">
  3953. \t\t\t\t\t\t\t\t\t\t<div class=\"form-group\">
  3954. \t\t\t\t\t\t\t\t\t\t\t<textarea class=\"form-control\" name=\"comment\" id=\"comment\" rows=\"3\" placeholder=\"Votre question ou commentaire...\" required></textarea>
  3955. \t\t\t\t\t\t\t\t\t\t</div>
  3956. \t\t\t\t\t\t\t\t\t</div>
  3957. \t\t\t\t\t\t\t\t\t<div class=\"col-md-12 text-right\">
  3958. \t\t\t\t\t\t\t\t\t\t<button type=\"submit\" class=\"btn primary-btn\">Envoyer</button>
  3959. \t\t\t\t\t\t\t\t\t</div>
  3960. \t\t\t\t\t\t\t\t</form>
  3961. \t\t\t\t\t\t\t</div>
  3962. \t\t\t\t\t\t{% else %}
  3963. \t\t\t\t\t\t\t<p class=\"text-center\">
  3964. \t\t\t\t\t\t\t\t<a href=\"{{ path('ui_app_login') }}\" class=\"primary-btn\">Connectez-vous</a>
  3965. \t\t\t\t\t\t\t\tpour poser une question ou laisser un commentaire.
  3966. \t\t\t\t\t\t\t</p>
  3967. \t\t\t\t\t\t{% endif %}
  3968. \t\t\t\t\t</div>
  3969. \t\t\t\t</div>
  3970. \t\t\t\t<div class=\"tab-pane fade\" id=\"review\" role=\"tabpanel\" aria-labelledby=\"review-tab\">
  3971. \t\t\t\t\t<div class=\"review-wrapper\">
  3972. \t\t\t\t\t\t<div class=\"row\">
  3973. \t\t\t\t\t\t\t<div class=\"col-lg-6\">
  3974. \t\t\t\t\t\t\t\t<div class=\"row total_rate\">
  3975. \t\t\t\t\t\t\t\t\t<div class=\"col-6\">
  3976. \t\t\t\t\t\t\t\t\t\t<div class=\"box_total\">
  3977. \t\t\t\t\t\t\t\t\t\t\t<h5>Note globale</h5>
  3978. \t\t\t\t\t\t\t\t\t\t\t<h4>{{ product.averageRating ? product.averageRating|number_format(1, '.', ',') : '0.0' }}</h4>
  3979. \t\t\t\t\t\t\t\t\t\t\t<h6>({{ product.reviewCount ?? 0 }}
  3980. \t\t\t\t\t\t\t\t\t\t\t\tAvis)</h6>
  3981. \t\t\t\t\t\t\t\t\t\t</div>
  3982. \t\t\t\t\t\t\t\t\t</div>
  3983. \t\t\t\t\t\t\t\t\t<div class=\"col-6\">
  3984. \t\t\t\t\t\t\t\t\t\t<div class=\"rating_list\">
  3985. \t\t\t\t\t\t\t\t\t\t\t<h3>Basé sur
  3986. \t\t\t\t\t\t\t\t\t\t\t\t{{ product.reviewCount ?? 0 }}
  3987. \t\t\t\t\t\t\t\t\t\t\t\tavis</h3>
  3988. \t\t\t\t\t\t\t\t\t\t\t<div class=\"rating-summary\">
  3989. \t\t\t\t\t\t\t\t\t\t\t\t{% if product.averageRating %}
  3990. \t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rating-display mb-3\">
  3991. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% for i in 1..5 %}
  3992. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<i class=\"fa fa-star{% if i <= product.averageRating %}{% else %}-o{% endif %}\" style=\"color: #ffa200; font-size: 1.2rem;\"></i>
  3993. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% endfor %}
  3994. \t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"ms-2\" style=\"font-size: 1.1rem; font-weight: bold;\">{{ product.averageRating|number_format(1, '.', ',') }}
  3995. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/ 5.0</span>
  3996. \t\t\t\t\t\t\t\t\t\t\t\t\t</div>
  3997. \t\t\t\t\t\t\t\t\t\t\t\t{% else %}
  3998. \t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"text-muted\">Aucune note disponible</p>
  3999. \t\t\t\t\t\t\t\t\t\t\t\t{% endif %}
  4000. \t\t\t\t\t\t\t\t\t\t\t</div>
  4001. \t\t\t\t\t\t\t\t\t\t</div>
  4002. \t\t\t\t\t\t\t\t\t</div>
  4003. \t\t\t\t\t\t\t\t</div>
  4004. \t\t\t\t\t\t\t\t<div class=\"review_list\">
  4005. \t\t\t\t\t\t\t\t\t{% if product.reviewCount and product.reviewCount > 0 %}
  4006. \t\t\t\t\t\t\t\t\t\t<div class=\"alert alert-info\">
  4007. \t\t\t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-info-circle\"></i>
  4008. \t\t\t\t\t\t\t\t\t\t\tLe système d'affichage détaillé des avis sera disponible prochainement. 
  4009. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNote actuelle :
  4010. \t\t\t\t\t\t\t\t\t\t\t{{ product.averageRating|number_format(1, '.', ',') }}
  4011. \t\t\t\t\t\t\t\t\t\t\t/ 5.0 basée sur
  4012. \t\t\t\t\t\t\t\t\t\t\t{{ product.reviewCount }}
  4013. \t\t\t\t\t\t\t\t\t\t\tavis.
  4014. \t\t\t\t\t\t\t\t\t\t</div>
  4015. \t\t\t\t\t\t\t\t\t{% else %}
  4016. \t\t\t\t\t\t\t\t\t\t<div class=\"alert alert-info\">
  4017. \t\t\t\t\t\t\t\t\t\t\t<i class=\"lnr lnr-info-circle\"></i>
  4018. \t\t\t\t\t\t\t\t\t\t\tAucun avis pour ce produit pour le moment. Soyez le premier à laisser un avis après votre achat !
  4019. \t\t\t\t\t\t\t\t\t\t</div>
  4020. \t\t\t\t\t\t\t\t\t{% endif %}
  4021. \t\t\t\t\t\t\t\t</div>
  4022. \t\t\t\t\t\t\t</div>
  4023. \t\t\t\t\t\t\t<div class=\"col-lg-6\">
  4024. \t\t\t\t\t\t\t\t<div class=\"review_box\">
  4025. \t\t\t\t\t\t\t\t\t<h4>Laisser un avis</h4>
  4026. \t\t\t\t\t\t\t\t\t{% if app.user %}
  4027. \t\t\t\t\t\t\t\t\t\t<p class=\"text-muted\">Vous pouvez laisser un avis après avoir acheté ce produit.</p>
  4028. \t\t\t\t\t\t\t\t\t\t<form class=\"row contact_form\" method=\"post\" novalidate=\"novalidate\">
  4029. \t\t\t\t\t\t\t\t\t\t\t<div class=\"col-md-12\">
  4030. \t\t\t\t\t\t\t\t\t\t\t\t<div class=\"form-group\">
  4031. \t\t\t\t\t\t\t\t\t\t\t\t\t<label>Votre note</label>
  4032. \t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rating-input mb-3\" style=\"display: flex; gap: 5px; flex-direction: row-reverse; justify-content: flex-end;\">
  4033. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% for i in 5..1 %}
  4034. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"rating\" id=\"rating{{ i }}\" value=\"{{ i }}\" required style=\"display: none;\">
  4035. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<label for=\"rating{{ i }}\" class=\"star-label\" style=\"cursor: pointer;\">
  4036. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<i class=\"fa fa-star-o\" style=\"color: #ccc; font-size: 1.5rem;\"></i>
  4037. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</label>
  4038. \t\t\t\t\t\t\t\t\t\t\t\t\t\t{% endfor %}
  4039. \t\t\t\t\t\t\t\t\t\t\t\t\t</div>
  4040. \t\t\t\t\t\t\t\t\t\t\t\t</div>
  4041. \t\t\t\t\t\t\t\t\t\t\t</div>
  4042. \t\t\t\t\t\t\t\t\t\t\t<div class=\"col-md-12\">
  4043. \t\t\t\t\t\t\t\t\t\t\t\t<div class=\"form-group\">
  4044. \t\t\t\t\t\t\t\t\t\t\t\t\t<textarea class=\"form-control\" name=\"review\" id=\"review\" rows=\"5\" placeholder=\"Votre avis...\" required onfocus=\"this.placeholder = ''\" onblur=\"this.placeholder = 'Votre avis...'\"></textarea>
  4045. \t\t\t\t\t\t\t\t\t\t\t\t</div>
  4046. \t\t\t\t\t\t\t\t\t\t\t</div>
  4047. \t\t\t\t\t\t\t\t\t\t\t<div class=\"col-md-12 text-right\">
  4048. \t\t\t\t\t\t\t\t\t\t\t\t<button type=\"submit\" class=\"btn primary-btn\">Publier l'avis</button>
  4049. \t\t\t\t\t\t\t\t\t\t\t</div>
  4050. \t\t\t\t\t\t\t\t\t\t</form>
  4051. \t\t\t\t\t\t\t\t\t{% else %}
  4052. \t\t\t\t\t\t\t\t\t\t<p class=\"text-center\">
  4053. \t\t\t\t\t\t\t\t\t\t\t<a href=\"{{ path('ui_app_login') }}\" class=\"primary-btn\">Connectez-vous</a>
  4054. \t\t\t\t\t\t\t\t\t\t\tpour laisser un avis sur ce produit.
  4055. \t\t\t\t\t\t\t\t\t\t</p>
  4056. \t\t\t\t\t\t\t\t\t{% endif %}
  4057. \t\t\t\t\t\t\t\t</div>
  4058. \t\t\t\t\t\t\t</div>
  4059. \t\t\t\t\t\t</div>
  4060. \t\t\t\t\t</div>
  4061. \t\t\t\t</div>
  4062. \t\t\t</div>
  4063. \t\t</div>
  4064. \t</section>
  4065. \t<!--================End Product Description Area =================-->
  4066. \t<script>
  4067. \t\t// Initialiser les onglets Bootstrap
  4068. document.addEventListener('DOMContentLoaded', function () { // Activer les onglets Bootstrap 5
  4069. const triggerTabList = document.querySelectorAll('#myTab button[data-bs-toggle=\"tab\"]');
  4070. triggerTabList.forEach(triggerEl => {
  4071. const tabTrigger = new bootstrap.Tab(triggerEl);
  4072. triggerEl.addEventListener('click', event => {
  4073. event.preventDefault();
  4074. tabTrigger.show();
  4075. });
  4076. });
  4077. // Gestion des étoiles pour les avis
  4078. const ratingInputs = document.querySelectorAll('.rating-input input[type=\"radio\"]');
  4079. const starLabels = document.querySelectorAll('.rating-input .star-label');
  4080. ratingInputs.forEach((input, index) => {
  4081. input.addEventListener('change', function () {
  4082. const rating = parseInt(this.value);
  4083. starLabels.forEach((label) => { // Trouver l'input associé à ce label
  4084. const labelInput = label.previousElementSibling;
  4085. if (labelInput && labelInput.type === 'radio') {
  4086. const starValue = parseInt(labelInput.value);
  4087. const star = label.querySelector('i');
  4088. if (starValue <= rating) {
  4089. star.style.color = '#ffa200';
  4090. star.classList.remove('fa-star-o');
  4091. star.classList.add('fa-star');
  4092. } else {
  4093. star.style.color = '#ccc';
  4094. star.classList.remove('fa-star');
  4095. star.classList.add('fa-star-o');
  4096. }
  4097. }
  4098. });
  4099. });
  4100. });
  4101. // Pré-remplir les étoiles au survol
  4102. starLabels.forEach((label) => {
  4103. label.addEventListener('mouseenter', function () { // Trouver l'input associé à ce label
  4104. const input = label.previousElementSibling;
  4105. if (input && input.type === 'radio') {
  4106. const rating = parseInt(input.value);
  4107. starLabels.forEach((l) => {
  4108. const lInput = l.previousElementSibling;
  4109. if (lInput && lInput.type === 'radio') {
  4110. const starValue = parseInt(lInput.value);
  4111. const star = l.querySelector('i');
  4112. if (starValue <= rating) {
  4113. star.style.color = '#ffa200';
  4114. star.classList.remove('fa-star-o');
  4115. star.classList.add('fa-star');
  4116. } else {
  4117. star.style.color = '#ccc';
  4118. star.classList.remove('fa-star');
  4119. star.classList.add('fa-star-o');
  4120. }
  4121. }
  4122. });
  4123. }
  4124. });
  4125. });
  4126. const ratingContainer = document.querySelector('.rating-input');
  4127. if (ratingContainer) {
  4128. ratingContainer.addEventListener('mouseleave', function () {
  4129. const checkedInput = document.querySelector('.rating-input input[type=\"radio\"]:checked');
  4130. if (checkedInput) {
  4131. checkedInput.dispatchEvent(new Event('change'));
  4132. } else {
  4133. starLabels.forEach(label => {
  4134. const star = label.querySelector('i');
  4135. star.style.color = '#ccc';
  4136. });
  4137. }
  4138. });
  4139. }
  4140. });
  4141. \t</script>
  4142. \t{% if youMightAlsoLike|length > 0 %}
  4143. \t\t<section class=\"related-product-area section_gap_bottom py-5\" style=\"background: linear-gradient(to bottom, #f8f9fa 0%, #ffffff 100%);\">
  4144. \t\t\t<div class=\"container\">
  4145. \t\t\t\t<div class=\"row justify-content-center\">
  4146. \t\t\t\t\t<div class=\"col-lg-8 text-center\">
  4147. \t\t\t\t\t\t<div class=\"section-title\">
  4148. \t\t\t\t\t\t\t<h2 class=\"fw-bold mb-3\" style=\"font-size: 2.5rem; color: #2c3e50;\">Vous pourriez aussi aimer</h2>
  4149. \t\t\t\t\t\t\t<p class=\"text-muted\" style=\"font-size: 1.1rem;\">Produits sélectionnés pour vous en fonction de vos préférences</p>
  4150. \t\t\t\t\t\t</div>
  4151. \t\t\t\t\t</div>
  4152. \t\t\t\t</div>
  4153. \t\t\t\t<div class=\"row g-4\">
  4154. \t\t\t\t\t{% for relatedProduct in youMightAlsoLike %}
  4155. \t\t\t\t\t\t<div class=\"col-xl-3 col-lg-4 col-md-6 col-sm-6 mb-4 d-flex align-items-stretch\">
  4156. \t\t\t\t\t\t\t{% include 'home/_single_product_card.html.twig' with {'product': relatedProduct} %}
  4157. \t\t\t\t\t\t</div>
  4158. \t\t\t\t\t{% endfor %}
  4159. \t\t\t\t</div>
  4160. \t\t\t</div>
  4161. \t\t</section>
  4162. \t\t<style>
  4163. \t\t\t.modern-product-card {
  4164. \t\t\t\tbackground: #ffffff;
  4165. \t\t\t\tborder-radius: 16px;
  4166. \t\t\t\toverflow: hidden;
  4167. \t\t\t\tbox-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  4168. \t\t\t\ttransition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  4169. \t\t\t\theight: 100%;
  4170. \t\t\t\tdisplay: flex;
  4171. \t\t\t\tflex-direction: column;
  4172. \t\t\t\tposition: relative;
  4173. \t\t\t}
  4174. \t\t\t.modern-product-card:hover {
  4175. \t\t\t\ttransform: translateY(-8px);
  4176. \t\t\t\tbox-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
  4177. \t\t\t}
  4178. \t\t\t.product-image-wrapper {
  4179. \t\t\t\tposition: relative;
  4180. \t\t\t\twidth: 100%;
  4181. \t\t\t\theight: 250px;
  4182. \t\t\t\toverflow: hidden;
  4183. \t\t\t\tbackground: #f8f9fa;
  4184. \t\t\t}
  4185. \t\t\t.product-image {
  4186. \t\t\t\twidth: 100%;
  4187. \t\t\t\theight: 100%;
  4188. \t\t\t\tobject-fit: cover;
  4189. \t\t\t\ttransition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  4190. \t\t\t}
  4191. \t\t\t.modern-product-card:hover .product-image {
  4192. \t\t\t\ttransform: scale(1.1);
  4193. \t\t\t}
  4194. \t\t\t.product-overlay {
  4195. \t\t\t\tposition: absolute;
  4196. \t\t\t\ttop: 0;
  4197. \t\t\t\tleft: 0;
  4198. \t\t\t\tright: 0;
  4199. \t\t\t\tbottom: 0;
  4200. \t\t\t\tbackground: rgba(0, 0, 0, 0.5);
  4201. \t\t\t\tdisplay: flex;
  4202. \t\t\t\talign-items: center;
  4203. \t\t\t\tjustify-content: center;
  4204. \t\t\t\topacity: 0;
  4205. \t\t\t\ttransition: opacity 0.3s ease;
  4206. \t\t\t}
  4207. \t\t\t.modern-product-card:hover .product-overlay {
  4208. \t\t\t\topacity: 1;
  4209. \t\t\t}
  4210. \t\t\t.view-product-btn {
  4211. \t\t\t\tcolor: white;
  4212. \t\t\t\tpadding: 12px 24px;
  4213. \t\t\t\tbackground: rgba(255, 255, 255, 0.2);
  4214. \t\t\t\tbackdrop-filter: blur(10px);
  4215. \t\t\t\tborder-radius: 8px;
  4216. \t\t\t\tfont-weight: 600;
  4217. \t\t\t\tdisplay: inline-flex;
  4218. \t\t\t\talign-items: center;
  4219. \t\t\t\tgap: 8px;
  4220. \t\t\t\ttransition: all 0.3s ease;
  4221. \t\t\t}
  4222. \t\t\t.view-product-btn:hover {
  4223. \t\t\t\tbackground: rgba(255, 255, 255, 0.3);
  4224. \t\t\t\ttransform: scale(1.05);
  4225. \t\t\t}
  4226. \t\t\t.product-badge {
  4227. \t\t\t\tposition: absolute;
  4228. \t\t\t\ttop: 12px;
  4229. \t\t\t\tright: 12px;
  4230. \t\t\t\tz-index: 2;
  4231. \t\t\t}
  4232. \t\t\t.badge-discount {
  4233. \t\t\t\tbackground: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
  4234. \t\t\t\tcolor: white;
  4235. \t\t\t\tpadding: 6px 12px;
  4236. \t\t\t\tborder-radius: 20px;
  4237. \t\t\t\tfont-size: 0.75rem;
  4238. \t\t\t\tfont-weight: 700;
  4239. \t\t\t\tbox-shadow: 0 2px 8px rgba(238, 90, 111, 0.4);
  4240. \t\t\t}
  4241. \t\t\t.product-info {
  4242. \t\t\t\tpadding: 20px;
  4243. \t\t\t\tflex-grow: 1;
  4244. \t\t\t\tdisplay: flex;
  4245. \t\t\t\tflex-direction: column;
  4246. \t\t\t}
  4247. \t\t\t.product-title {
  4248. \t\t\t\tmargin: 0 0 8px 0;
  4249. \t\t\t\tfont-size: 1rem;
  4250. \t\t\t\tfont-weight: 600;
  4251. \t\t\t\tline-height: 1.4;
  4252. \t\t\t\theight: 2.8em;
  4253. \t\t\t\toverflow: hidden;
  4254. \t\t\t\tdisplay: -webkit-box;
  4255. \t\t\t\t-webkit-line-clamp: 2;
  4256. \t\t\t\t-webkit-box-orient: vertical;
  4257. \t\t\t}
  4258. \t\t\t.product-title a {
  4259. \t\t\t\tcolor: #2c3e50;
  4260. \t\t\t\ttext-decoration: none;
  4261. \t\t\t\ttransition: color 0.3s ease;
  4262. \t\t\t}
  4263. \t\t\t.product-title a:hover {
  4264. \t\t\t\tcolor: #ffa200;
  4265. \t\t\t}
  4266. \t\t\t.product-shop {
  4267. \t\t\t\tfont-size: 0.85rem;
  4268. \t\t\t\tcolor: #6c757d;
  4269. \t\t\t\tmargin-bottom: 12px;
  4270. \t\t\t\tdisplay: flex;
  4271. \t\t\t\talign-items: center;
  4272. \t\t\t\tgap: 6px;
  4273. \t\t\t}
  4274. \t\t\t.product-shop i {
  4275. \t\t\t\tfont-size: 0.9rem;
  4276. \t\t\t}
  4277. \t\t\t.shop-link {
  4278. \t\t\t\tcolor: #6c757d;
  4279. \t\t\t\ttext-decoration: none;
  4280. \t\t\t\ttransition: color 0.3s ease;
  4281. \t\t\t}
  4282. \t\t\t.shop-link:hover {
  4283. \t\t\t\tcolor: #ffa200;
  4284. \t\t\t}
  4285. \t\t\t.product-price-wrapper {
  4286. \t\t\t\tmargin-bottom: 16px;
  4287. \t\t\t\tdisplay: flex;
  4288. \t\t\t\talign-items: center;
  4289. \t\t\t\tgap: 8px;
  4290. \t\t\t\tflex-wrap: wrap;
  4291. \t\t\t}
  4292. \t\t\t.product-price {
  4293. \t\t\t\tfont-size: 1.25rem;
  4294. \t\t\t\tfont-weight: 700;
  4295. \t\t\t\tcolor: #ffa200;
  4296. \t\t\t}
  4297. \t\t\t.product-compare-price {
  4298. \t\t\t\tfont-size: 0.9rem;
  4299. \t\t\t\tcolor: #adb5bd;
  4300. \t\t\t\ttext-decoration: line-through;
  4301. \t\t\t}
  4302. \t\t\t.product-actions {
  4303. \t\t\t\tmargin-top: auto;
  4304. \t\t\t}
  4305. \t\t\t/* Styles pour les boutons de la section \"Vous pourriez aussi aimer\" uniquement */
  4306. \t\t\t.modern-product-card .btn-add-to-cart {
  4307. \t\t\t\twidth: 100%;
  4308. \t\t\t\tpadding: 12px;
  4309. \t\t\t\tbackground: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
  4310. \t\t\t\tcolor: white;
  4311. \t\t\t\tborder: none;
  4312. \t\t\t\tborder-radius: 10px;
  4313. \t\t\t\tfont-weight: 600;
  4314. \t\t\t\tdisplay: flex;
  4315. \t\t\t\talign-items: center;
  4316. \t\t\t\tjustify-content: center;
  4317. \t\t\t\tgap: 8px;
  4318. \t\t\t\ttext-decoration: none;
  4319. \t\t\t\ttransition: all 0.3s ease;
  4320. \t\t\t\tcursor: pointer;
  4321. \t\t\t}
  4322. \t\t\t.modern-product-card .btn-add-to-cart:hover {
  4323. \t\t\t\tbackground: linear-gradient(135deg, #e8910a 0%, #d6820a 100%);
  4324. \t\t\t\ttransform: translateY(-2px);
  4325. \t\t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.4);
  4326. \t\t\t\tcolor: white;
  4327. \t\t\t}
  4328. \t\t\t/* Le bouton principal garde son design original */
  4329. \t\t\t.card_area .btn-add-to-cart {
  4330. \t\t\t\twidth: auto;
  4331. \t\t\t\tdisplay: inline-flex;
  4332. \t\t\t}
  4333. \t\t\t.modern-product-card .btn-add-to-cart:active {
  4334. \t\t\t\ttransform: translateY(0);
  4335. \t\t\t}
  4336. \t\t\t.modern-product-card .btn-add-to-cart i {
  4337. \t\t\t\tfont-size: 1.1rem;
  4338. \t\t\t}
  4339. \t\t\t/* Responsive adjustments */
  4340. \t\t\t@media (max-width: 768px) {
  4341. \t\t\t\t.product-image-wrapper {
  4342. \t\t\t\t\theight: 200px;
  4343. \t\t\t\t}
  4344. \t\t\t\t.product-info {
  4345. \t\t\t\t\tpadding: 16px;
  4346. \t\t\t\t}
  4347. \t\t\t\t.product-title {
  4348. \t\t\t\t\tfont-size: 0.95rem;
  4349. \t\t\t\t}
  4350. \t\t\t\t.product-price {
  4351. \t\t\t\t\tfont-size: 1.1rem;
  4352. \t\t\t\t}
  4353. \t\t\t\t.section-title h2 {
  4354. \t\t\t\t\tfont-size: 2rem !important;
  4355. \t\t\t\t}
  4356. \t\t\t}
  4357. \t\t\t@media (max-width: 576px) {
  4358. \t\t\t\t.product-image-wrapper {
  4359. \t\t\t\t\theight: 190px;
  4360. \t\t\t\t}
  4361. \t\t\t\t.product-info {
  4362. \t\t\t\t\tpadding: 14px;
  4363. \t\t\t\t}
  4364. \t\t\t\t.btn-add-to-cart {
  4365. \t\t\t\t\tpadding: 10px;
  4366. \t\t\t\t\tfont-size: 0.9rem;
  4367. \t\t\t\t}
  4368. \t\t\t}
  4369. \t\t\t/* Ensure fixed height for cards */
  4370. \t\t\t.col-6.col-md-4.col-lg-3 {
  4371. \t\t\t\tdisplay: flex;
  4372. \t\t\t}
  4373. \t\t\t.modern-product-card {
  4374. \t\t\t\twidth: 100%;
  4375. \t\t\t}
  4376. \t\t\t/* Loading state for add to cart button */
  4377. \t\t\t.btn-add-to-cart.loading {
  4378. \t\t\t\topacity: 0.7;
  4379. \t\t\t\tpointer-events: none;
  4380. \t\t\t}
  4381. \t\t\t.btn-add-to-cart.loading i {
  4382. \t\t\t\tanimation: spin 1s linear infinite;
  4383. \t\t\t}
  4384. \t\t\t@keyframes spin {
  4385. \t\t\t\tfrom { transform: rotate(0deg); }
  4386. \t\t\t\tto { transform: rotate(360deg); }
  4387. \t\t\t}
  4388. \t\t\t/* Success state - vert pour tous les boutons */
  4389. \t\t\t.btn-add-to-cart.success {
  4390. \t\t\t\tbackground: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important;
  4391. \t\t\t}
  4392. \t\t\t/* Le bouton principal garde son style original même en success */
  4393. \t\t\t.card_area .btn-add-to-cart.success {
  4394. \t\t\t\tbackground: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important;
  4395. \t\t\t}
  4396. \t\t</style>
  4397. \t\t<script>
  4398. \t\t\t// Gestion des boutons d'ajout au panier (principal et section \"Vous pourriez aussi aimer\")
  4399. \t\t\tdocument.addEventListener('DOMContentLoaded', function() {
  4400. \t\t\t\tconst addToCartButtons = document.querySelectorAll('.btn-add-to-cart');
  4401. \t\t\t\t
  4402. \t\t\t\taddToCartButtons.forEach(button => {
  4403. \t\t\t\t\tbutton.addEventListener('click', function(e) {
  4404. \t\t\t\t\t\te.preventDefault();
  4405. \t\t\t\t\t\te.stopPropagation();
  4406. \t\t\t\t\t\t
  4407. \t\t\t\t\t\tconst productId = this.getAttribute('data-product-id');
  4408. \t\t\t\t\t\t
  4409. \t\t\t\t\t\t// Pour le bouton principal, récupérer la quantité depuis l'input sst si disponible
  4410. \t\t\t\t\t\tlet qty = this.getAttribute('data-qty') || '1';
  4411. \t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  4412. \t\t\t\t\t\t\tconst qtyInput = document.getElementById('sst');
  4413. \t\t\t\t\t\t\tif (qtyInput) {
  4414. \t\t\t\t\t\t\t\tqty = parseInt(qtyInput.value) || 1;
  4415. \t\t\t\t\t\t\t}
  4416. \t\t\t\t\t\t}
  4417. \t\t\t\t\t\t
  4418. \t\t\t\t\t\tif (!productId) {
  4419. \t\t\t\t\t\t\tconsole.error('Product ID manquant');
  4420. \t\t\t\t\t\t\treturn false;
  4421. \t\t\t\t\t\t}
  4422. \t\t\t\t\t\t// Sauvegarder le contenu original
  4423. \t\t\t\t\t\tconst originalContent = this.innerHTML;
  4424. \t\t\t\t\t\t
  4425. \t\t\t\t\t\t// Désactiver le bouton et afficher le loading
  4426. \t\t\t\t\t\tthis.classList.add('loading');
  4427. \t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  4428. \t\t\t\t\t\t\tthis.style.pointerEvents = 'none';
  4429. \t\t\t\t\t\t\tthis.innerHTML = '<i class=\"lnr lnr-spinner\"></i> Ajout en cours...';
  4430. \t\t\t\t\t\t} else {
  4431. \t\t\t\t\t\t\tthis.innerHTML = '<i class=\"ti ti-loader-2\"></i> <span>Ajout...</span>';
  4432. \t\t\t\t\t\t}
  4433. \t\t\t\t\t\t
  4434. \t\t\t\t\t\t// Construire le body de la requête
  4435. \t\t\t\t\t\tlet body = 'productId=' + encodeURIComponent(productId) + '&qty=' + encodeURIComponent(qty);
  4436. \t\t\t\t\t\t
  4437. \t\t\t\t\t\t// Pour le bouton principal, vérifier aussi le prix unitaire pour les prix de gros
  4438. \t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  4439. \t\t\t\t\t\t\tconst unitPriceInput = document.getElementById('unit-price-input');
  4440. \t\t\t\t\t\t\tconst unitPrice = unitPriceInput ? unitPriceInput.value : '';
  4441. \t\t\t\t\t\t\tif (unitPrice) {
  4442. \t\t\t\t\t\t\t\tbody += '&unitPrice=' + encodeURIComponent(unitPrice);
  4443. \t\t\t\t\t\t\t}
  4444. \t\t\t\t\t\t}
  4445. \t\t\t\t\t\t
  4446. \t\t\t\t\t\t// Faire la requête AJAX
  4447. \t\t\t\t\t\tfetch('{{ path(\"ui_cart_add\") }}', {
  4448. \t\t\t\t\t\t\tmethod: 'POST',
  4449. \t\t\t\t\t\t\theaders: {
  4450. \t\t\t\t\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',
  4451. \t\t\t\t\t\t\t\t'X-Requested-With': 'XMLHttpRequest'
  4452. \t\t\t\t\t\t\t},
  4453. \t\t\t\t\t\t\tbody: body
  4454. \t\t\t\t\t\t})
  4455. \t\t\t\t\t\t.then(response => {
  4456. \t\t\t\t\t\t\tif (!response.ok) {
  4457. \t\t\t\t\t\t\t\tthrow new Error('Erreur réseau: ' + response.status);
  4458. \t\t\t\t\t\t\t}
  4459. \t\t\t\t\t\t\treturn response.json();
  4460. \t\t\t\t\t\t})
  4461. \t\t\t\t\t\t.then(data => {
  4462. \t\t\t\t\t\t\tif (data.ok || data.success) {
  4463. \t\t\t\t\t\t\t\t// Succès - Afficher l'animation de succès
  4464. \t\t\t\t\t\t\t\tthis.classList.remove('loading');
  4465. \t\t\t\t\t\t\t\tthis.classList.add('success');
  4466. \t\t\t\t\t\t\t\t
  4467. \t\t\t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  4468. \t\t\t\t\t\t\t\t\tthis.style.background = 'linear-gradient(135deg, #28a745 0%, #20c997 100%)';
  4469. \t\t\t\t\t\t\t\t\tthis.innerHTML = '<i class=\"lnr lnr-checkmark-circle\"></i> Ajouté !';
  4470. \t\t\t\t\t\t\t\t\t
  4471. \t\t\t\t\t\t\t\t\t// Afficher le message de confirmation si présent
  4472. \t\t\t\t\t\t\t\t\tconst cartMessage = document.getElementById('cart-message');
  4473. \t\t\t\t\t\t\t\t\tif (cartMessage) {
  4474. \t\t\t\t\t\t\t\t\t\tcartMessage.style.display = 'block';
  4475. \t\t\t\t\t\t\t\t\t\tsetTimeout(() => {
  4476. \t\t\t\t\t\t\t\t\t\t\tcartMessage.style.display = 'none';
  4477. \t\t\t\t\t\t\t\t\t\t}, 3000);
  4478. \t\t\t\t\t\t\t\t\t}
  4479. \t\t\t\t\t\t\t\t} else {
  4480. \t\t\t\t\t\t\t\t\tthis.innerHTML = '<i class=\"ti ti-check\"></i> <span>Ajouté !</span>';
  4481. \t\t\t\t\t\t\t\t}
  4482. \t\t\t\t\t\t\t\t
  4483. \t\t\t\t\t\t\t\t// Mettre à jour le compteur du panier si présent
  4484. \t\t\t\t\t\t\t\tconst cartBadge = document.querySelector('.cart-badge');
  4485. \t\t\t\t\t\t\t\tif (cartBadge) {
  4486. \t\t\t\t\t\t\t\t\tcartBadge.textContent = data.totalQty || data.quantity || 0;
  4487. \t\t\t\t\t\t\t\t\t// Animation du badge
  4488. \t\t\t\t\t\t\t\t\tcartBadge.style.transform = 'scale(1.2)';
  4489. \t\t\t\t\t\t\t\t\tsetTimeout(() => {
  4490. \t\t\t\t\t\t\t\t\t\tcartBadge.style.transform = 'scale(1)';
  4491. \t\t\t\t\t\t\t\t\t}, 300);
  4492. \t\t\t\t\t\t\t\t}
  4493. \t\t\t\t\t\t\t\t
  4494. \t\t\t\t\t\t\t\t// Afficher une notification si la fonction existe
  4495. \t\t\t\t\t\t\t\tif (typeof showCustomAlert === 'function') {
  4496. \t\t\t\t\t\t\t\t\tshowCustomAlert('Produit ajouté au panier avec succès !', 'success');
  4497. \t\t\t\t\t\t\t\t}
  4498. \t\t\t\t\t\t\t\t
  4499. \t\t\t\t\t\t\t\t// Restaurer le bouton après 2 secondes
  4500. \t\t\t\t\t\t\t\tsetTimeout(() => {
  4501. \t\t\t\t\t\t\t\t\tthis.classList.remove('success');
  4502. \t\t\t\t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  4503. \t\t\t\t\t\t\t\t\t\tthis.style.background = '';
  4504. \t\t\t\t\t\t\t\t\t\tthis.style.pointerEvents = 'auto';
  4505. \t\t\t\t\t\t\t\t\t}
  4506. \t\t\t\t\t\t\t\t\tthis.innerHTML = originalContent;
  4507. \t\t\t\t\t\t\t\t}, 2000);
  4508. \t\t\t\t\t\t\t} else {
  4509. \t\t\t\t\t\t\t\t// Erreur
  4510. \t\t\t\t\t\t\t\tthis.classList.remove('loading');
  4511. \t\t\t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  4512. \t\t\t\t\t\t\t\t\tthis.style.pointerEvents = 'auto';
  4513. \t\t\t\t\t\t\t\t}
  4514. \t\t\t\t\t\t\t\tthis.innerHTML = originalContent;
  4515. \t\t\t\t\t\t\t\t
  4516. \t\t\t\t\t\t\t\tconst message = data.message || 'Erreur lors de l\\'ajout au panier';
  4517. \t\t\t\t\t\t\t\tif (typeof showCustomAlert === 'function') {
  4518. \t\t\t\t\t\t\t\t\tshowCustomAlert(message, 'error');
  4519. \t\t\t\t\t\t\t\t} else {
  4520. \t\t\t\t\t\t\t\t\talert(message);
  4521. \t\t\t\t\t\t\t\t}
  4522. \t\t\t\t\t\t\t}
  4523. \t\t\t\t\t\t})
  4524. \t\t\t\t\t\t.catch(error => {
  4525. \t\t\t\t\t\t\tconsole.error('Erreur:', error);
  4526. \t\t\t\t\t\t\tthis.classList.remove('loading');
  4527. \t\t\t\t\t\t\tif (this.id === 'add-to-cart-btn') {
  4528. \t\t\t\t\t\t\t\tthis.style.pointerEvents = 'auto';
  4529. \t\t\t\t\t\t\t}
  4530. \t\t\t\t\t\t\tthis.innerHTML = originalContent;
  4531. \t\t\t\t\t\t\t
  4532. \t\t\t\t\t\t\tif (typeof showCustomAlert === 'function') {
  4533. \t\t\t\t\t\t\t\tshowCustomAlert('Erreur lors de l\\'ajout au panier. Veuillez réessayer.', 'error');
  4534. \t\t\t\t\t\t\t} else {
  4535. \t\t\t\t\t\t\t\talert('Erreur lors de l\\'ajout au panier. Veuillez réessayer.');
  4536. \t\t\t\t\t\t\t}
  4537. \t\t\t\t\t\t});
  4538. \t\t\t\t\t\t
  4539. \t\t\t\t\t\treturn false;
  4540. \t\t\t\t\t});
  4541. \t\t\t\t});
  4542. \t\t\t});
  4543. \t\t</script>
  4544. \t{% endif %}
  4545. \t<script src=\"js/vendor/jquery-2.2.4.min.js\"></script>
  4546. \t<script src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js\" integrity=\"sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4\" crossorigin=\"anonymous\"></script>
  4547. \t<script src=\"js/vendor/bootstrap.min.js\"></script>
  4548. \t<script src=\"js/jquery.ajaxchimp.min.js\"></script>
  4549. \t<script src=\"js/jquery.nice-select.min.js\"></script>
  4550. \t<script src=\"js/jquery.sticky.js\"></script>
  4551. \t<script src=\"js/nouislider.min.js\"></script>
  4552. \t<script src=\"js/jquery.magnific-popup.min.js\"></script>
  4553. \t<script src=\"js/owl.carousel.min.js\"></script>
  4554. \t<!--gmaps Js-->
  4555. \t<script src=\"https://maps.googleapis.com/maps/api/js?key=AIzaSyCjCGmQ0Uq4exrzdcL6rvxywDDOvfAu6eE\"></script>
  4556. \t<script src=\"js/gmaps.min.js\"></script>
  4557. \t<script src=\"js/main.js\"></script>
  4558. \t<script>
  4559. \t\t// Le gestionnaire d'événements pour les boutons d'ajout au panier est déjà défini
  4560. \t\t// dans la section \"Vous pourriez aussi aimer\" et gère maintenant aussi le bouton principal
  4561. // Gestion de la galerie d'images
  4562. document.addEventListener('DOMContentLoaded', function () {
  4563. const thumbnails = document.querySelectorAll('.thumbnail-item');
  4564. const mainImage = document.getElementById('main-product-image');
  4565. const currentIndex = document.getElementById('current-image-index');
  4566. const totalImages = document.getElementById('total-images');
  4567. // Fonction pour changer l'image principale
  4568. function changeMainImage(thumbnail, index) {
  4569. const imageUrl = thumbnail.getAttribute('data-image');
  4570. if (mainImage && imageUrl) {
  4571. mainImage.style.opacity = '0';
  4572. setTimeout(() => {
  4573. mainImage.src = imageUrl;
  4574. mainImage.style.opacity = '1';
  4575. // Reset zoom
  4576. mainImage.style.transform = 'scale(1)';
  4577. mainImage.style.transformOrigin = 'center center';
  4578. }, 150);
  4579. }
  4580. // Mettre à jour l'indicateur
  4581. if (currentIndex) {
  4582. currentIndex.textContent = index + 1;
  4583. }
  4584. }
  4585. // Ajouter le zoom sur l'image principale au survol
  4586. const mainImageContainer = document.querySelector('.product-main-image-wrapper');
  4587. if (mainImageContainer && mainImage) {
  4588. \tmainImageContainer.addEventListener('mousemove', function(e) {
  4589. \t\tconst rect = mainImageContainer.getBoundingClientRect();
  4590. \t\tconst x = e.clientX - rect.left;
  4591. \t\tconst y = e.clientY - rect.top;
  4592. \t\t
  4593. \t\tconst xPercent = (x / rect.width) * 100;
  4594. \t\tconst yPercent = (y / rect.height) * 100;
  4595. \t\t
  4596. \t\tmainImage.style.transformOrigin = `\${xPercent}% \${yPercent}%`;
  4597. \t\tmainImage.style.transform = 'scale(2.2)'; // Zoom Alibaba-style
  4598. \t});
  4599. \t
  4600. \tmainImageContainer.addEventListener('mouseleave', function() {
  4601. \t\tmainImage.style.transformOrigin = 'center center';
  4602. \t\tmainImage.style.transform = 'scale(1)';
  4603. \t});
  4604. }
  4605. // Gérer le survol sur les miniatures (sélection permanente au survol)
  4606. thumbnails.forEach((thumbnail, index) => {
  4607. thumbnail.addEventListener('mouseenter', function () { // Retirer toutes les sélections permanentes
  4608. thumbnails.forEach(t => {
  4609. t.classList.remove('active', 'permanently-active');
  4610. });
  4611. // Ajouter active et permanently-active à la miniature survolée
  4612. thumbnail.classList.add('active', 'permanently-active');
  4613. // Changer l'image principale
  4614. changeMainImage(thumbnail, index);
  4615. });
  4616. });
  4617. // Gérer le clic sur les miniatures (sélection permanente)
  4618. thumbnails.forEach((thumbnail, index) => {
  4619. thumbnail.addEventListener('click', function () { // Retirer la classe active et permanently-active de toutes les miniatures
  4620. thumbnails.forEach(t => {
  4621. t.classList.remove('active', 'permanently-active');
  4622. });
  4623. // Ajouter les classes active et permanently-active à la miniature cliquée
  4624. thumbnail.classList.add('active', 'permanently-active');
  4625. // Changer l'image principale
  4626. changeMainImage(thumbnail, index);
  4627. });
  4628. });
  4629. // Gestion du carrousel de miniatures avec taille fixe carrée
  4630. const navUpBtn = document.getElementById('thumbnail-nav-up');
  4631. const navDownBtn = document.getElementById('thumbnail-nav-down');
  4632. const thumbnailsContainer = document.getElementById('product-thumbnails');
  4633. const thumbnailsWrapper = document.getElementById('thumbnails-wrapper');
  4634. const thumbnailItems = Array.from(document.querySelectorAll('.thumbnail-item'));
  4635. let currentThumbnailOffset = 0;
  4636. const thumbnailHeight = 90;
  4637. // 80px (hauteur) + 10px (gap)
  4638. // Calculer la hauteur disponible pour les miniatures dynamiquement
  4639. const thumbnailsEl = document.getElementById('product-thumbnails');
  4640. let containerHeight = thumbnailsEl ? thumbnailsEl.clientHeight : 520; // Hauteur par défaut si non disponible
  4641. let visibleThumbnails = Math.floor(containerHeight / thumbnailHeight);
  4642. // Recalculer après le chargement pour avoir la vraie hauteur
  4643. setTimeout(() => {
  4644. if (thumbnailsEl) {
  4645. containerHeight = thumbnailsEl.clientHeight;
  4646. visibleThumbnails = Math.floor(containerHeight / thumbnailHeight);
  4647. updateThumbnailsPosition();
  4648. }
  4649. }, 100);
  4650. function getCurrentIndex() {
  4651. const current = document.querySelector('.thumbnail-item.permanently-active') || document.querySelector('.thumbnail-item.active');
  4652. if (! current) 
  4653. return 0;
  4654. const idxAttr = current.getAttribute('data-index');
  4655. return idxAttr ? parseInt(idxAttr, 10) : Math.max(0, thumbnailItems.indexOf(current));
  4656. }
  4657. function selectThumbnailByIndex(newIndex) {
  4658. if (! thumbnailItems.length) 
  4659. return;
  4660. const total = thumbnailItems.length;
  4661. if (newIndex < 0) 
  4662. newIndex = 0;
  4663. if (newIndex > total - 1) 
  4664. newIndex = total - 1;
  4665. // Reset classes
  4666. thumbnailItems.forEach(t => t.classList.remove('active', 'permanently-active'));
  4667. const target = thumbnailItems[newIndex];
  4668. if (! target) 
  4669. return;
  4670. target.classList.add('active', 'permanently-active');
  4671. changeMainImage(target, newIndex);
  4672. // Ajuster le carrousel pour voir la miniature sélectionnée
  4673. ensureThumbnailVisible(newIndex);
  4674. }
  4675. function ensureThumbnailVisible(index) {
  4676. if (! thumbnailsWrapper || ! thumbnailItems.length) 
  4677. return;
  4678. const targetTop = index * thumbnailHeight;
  4679. const visibleTop = currentThumbnailOffset * thumbnailHeight;
  4680. const visibleBottom = visibleTop + containerHeight;
  4681. // Si la miniature est au-dessus de la zone visible
  4682. if (targetTop < visibleTop) {
  4683. currentThumbnailOffset = index;
  4684. updateThumbnailsPosition();
  4685. }
  4686. // Si la miniature est en-dessous de la zone visible 
  4687. else if (targetTop + thumbnailHeight > visibleBottom) {
  4688. currentThumbnailOffset = Math.max(0, index - visibleThumbnails + 1);
  4689. updateThumbnailsPosition();
  4690. }
  4691. }
  4692. function updateThumbnailsPosition() {
  4693. if (!thumbnailsWrapper) 
  4694. return;
  4695. const maxOffset = Math.max(0, thumbnailItems.length - visibleThumbnails);
  4696. currentThumbnailOffset = Math.max(0, Math.min(currentThumbnailOffset, maxOffset));
  4697. thumbnailsWrapper.style.transform = `translateY(-\${
  4698. currentThumbnailOffset * thumbnailHeight
  4699. }px)`;
  4700. updateNavButtons();
  4701. }
  4702. function updateNavButtons() {
  4703. if (!thumbnailItems.length) 
  4704. return;
  4705. const maxOffset = Math.max(0, thumbnailItems.length - visibleThumbnails);
  4706. const canScrollUp = currentThumbnailOffset > 0;
  4707. const canScrollDown = currentThumbnailOffset < maxOffset;
  4708. if (navUpBtn) {
  4709. navUpBtn.classList.toggle('disabled', ! canScrollUp);
  4710. }
  4711. if (navDownBtn) {
  4712. navDownBtn.classList.toggle('disabled', ! canScrollDown);
  4713. }
  4714. }
  4715. // Navigation avec les boutons
  4716. if(navUpBtn) {
  4717. navUpBtn.addEventListener('click', function (e) {
  4718. e.preventDefault();
  4719. if (currentThumbnailOffset > 0) {
  4720. currentThumbnailOffset --;
  4721. updateThumbnailsPosition();
  4722. }
  4723. });
  4724. }
  4725. if(navDownBtn) {
  4726. navDownBtn.addEventListener('click', function (e) {
  4727. e.preventDefault();
  4728. const maxOffset = Math.max(0, thumbnailItems.length - visibleThumbnails);
  4729. if (currentThumbnailOffset < maxOffset) {
  4730. currentThumbnailOffset ++;
  4731. updateThumbnailsPosition();
  4732. }
  4733. });
  4734. }
  4735. // Initialiser le carrousel
  4736. if(thumbnailsWrapper && thumbnailItems.length > 0) {
  4737. updateThumbnailsPosition();
  4738. // S'assurer que la première miniature est visible
  4739. ensureThumbnailVisible(0);
  4740. }
  4741. // Navigation sur l'image principale
  4742. const mainPrev = document.getElementById('main-image-prev');
  4743. const mainNext = document.getElementById('main-image-next');
  4744. if(mainPrev) {
  4745. mainPrev.addEventListener('click', function (e) {
  4746. e.preventDefault();
  4747. const idx = getCurrentIndex();
  4748. selectThumbnailByIndex(idx - 1);
  4749. });
  4750. }
  4751. if(mainNext) {
  4752. mainNext.addEventListener('click', function (e) {
  4753. e.preventDefault();
  4754. const idx = getCurrentIndex();
  4755. selectThumbnailByIndex(idx + 1);
  4756. });
  4757. }
  4758. }
  4759. );
  4760. // --- Prix de gros : calcul du prix unitaire dynamique ---
  4761. (function () {
  4762. const bulk = document.getElementById('bulk-pricing');
  4763. const qtyInputEl = document.getElementById('sst');
  4764. const unitPriceSpan = document.getElementById('unit-price-value');
  4765. const mainUnitPriceSpan = document.getElementById('main-unit-price');
  4766. const totalPriceSpan = document.getElementById('total-price-value');
  4767. const savingsAmountSpan = document.getElementById('savings-amount');
  4768. const savingsPercentSpan = document.getElementById('savings-percent');
  4769. const unitPriceInput = document.getElementById('unit-price-input');
  4770. const tierRowsTbody = document.getElementById('bulk-tier-rows');
  4771. if (! bulk || ! qtyInputEl || ! unitPriceSpan || ! mainUnitPriceSpan || ! unitPriceInput) 
  4772. return;
  4773. let tiers = [];
  4774. try {
  4775. tiers = JSON.parse(bulk.getAttribute('data-tiers') || '[]');
  4776. } catch (e) {
  4777. tiers = [];
  4778. }
  4779. if (!Array.isArray(tiers) || tiers.length === 0) 
  4780. return;
  4781. tiers.sort(function (a, b) {
  4782. return(a.min || 0) - (b.min || 0);
  4783. });
  4784. const basePrice = (function () {
  4785. const one = tiers.find(t => (t.min || 0) <= 1);
  4786. return one ? parseFloat(one.price) : parseFloat(tiers[0].price);
  4787. })();
  4788. function getUnitPriceForQty(qty) {
  4789. let candidate = tiers[0] || null;
  4790. for (let i = 0; i < tiers.length; i++) {
  4791. if (qty >= (tiers[i].min || 1)) {
  4792. candidate = tiers[i];
  4793. }
  4794. }
  4795. return candidate ? parseFloat(candidate.price) : (tiers[0] ? parseFloat(tiers[0].price) : basePrice);
  4796. }
  4797. function formatPrice(val) {
  4798. return(parseFloat(val) || 0).toFixed(2);
  4799. }
  4800. function highlightActiveTier(qty) {
  4801. if (! tierRowsTbody) 
  4802. return;
  4803. const rows = tierRowsTbody.querySelectorAll('tr');
  4804. rows.forEach(r => r.classList.remove('table-warning'));
  4805. let activeRow = null;
  4806. rows.forEach(r => {
  4807. const m = parseInt(r.getAttribute('data-min'), 10) || 1;
  4808. if (qty >= m) 
  4809. activeRow = r;
  4810. });
  4811. if (activeRow) 
  4812. activeRow.classList.add('table-warning');
  4813. }
  4814. function refreshPrices() {
  4815. const qty = Math.max(1, parseInt(qtyInputEl.value, 10) || 1);
  4816. const unit = getUnitPriceForQty(qty);
  4817. const total = unit * qty;
  4818. const saveAmount = Math.max(0, (basePrice - unit) * qty);
  4819. const savePercent = Math.max(0, 100 * (basePrice - unit) / basePrice);
  4820. unitPriceSpan.textContent = formatPrice(unit) + ' HTG';
  4821. mainUnitPriceSpan.textContent = formatPrice(unit);
  4822. if (totalPriceSpan) 
  4823. totalPriceSpan.textContent = formatPrice(total);
  4824. if (savingsAmountSpan) 
  4825. savingsAmountSpan.textContent = formatPrice(saveAmount);
  4826. if (savingsPercentSpan) 
  4827. savingsPercentSpan.textContent = Math.round(savePercent);
  4828. unitPriceInput.value = formatPrice(unit);
  4829. highlightActiveTier(qty);
  4830. }
  4831. qtyInputEl.addEventListener('input', refreshPrices);
  4832. qtyInputEl.addEventListener('change', refreshPrices);
  4833. setTimeout(refreshPrices, 50);
  4834. setInterval(refreshPrices, 350);
  4835. })();
  4836. // --- /Prix de gros ---
  4837. // Injection du prix unitaire dans l'ajout au panier
  4838. (function () {
  4839. const addToCartBtn = document.getElementById('add-to-cart-btn');
  4840. if (! addToCartBtn) 
  4841. return;
  4842. addToCartBtn.addEventListener('click', function () {
  4843. const unitPriceInput = document.getElementById('unit-price-input');
  4844. if (unitPriceInput) {
  4845. addToCartBtn.setAttribute('data-unit-price', unitPriceInput.value);
  4846. }
  4847. });
  4848. })();
  4849. // Fonction pour ouvrir le modal de zoom avec loader
  4850. function openImageZoom () {
  4851. const mainImage = document.getElementById('main-product-image');
  4852. const zoomModal = document.getElementById('image-zoom-modal');
  4853. const zoomImage = document.getElementById('zoom-modal-image');
  4854. const zoomLoader = document.getElementById('zoom-loader');
  4855. if (mainImage && zoomModal && zoomImage) { // Afficher le loader
  4856. if (zoomLoader) {
  4857. zoomLoader.style.display = 'flex';
  4858. }
  4859. // Masquer l'image et réinitialiser
  4860. zoomImage.classList.remove('loaded');
  4861. zoomImage.style.opacity = '0';
  4862. // Ouvrir le modal avec animation
  4863. zoomModal.classList.add('active');
  4864. document.body.style.overflow = 'hidden';
  4865. // Charger l'image
  4866. const imageUrl = mainImage.src;
  4867. const tempImage = new Image();
  4868. tempImage.onload = function () {
  4869. zoomImage.src = imageUrl;
  4870. // Masquer le loader et afficher l'image avec transition
  4871. setTimeout(function () {
  4872. if (zoomLoader) {
  4873. zoomLoader.style.display = 'none';
  4874. }
  4875. zoomImage.classList.add('loaded');
  4876. zoomImage.style.opacity = '1';
  4877. }, 200);
  4878. };
  4879. tempImage.onerror = function () {
  4880. if (zoomLoader) {
  4881. zoomLoader.style.display = 'none';
  4882. }
  4883. zoomImage.src = imageUrl;
  4884. zoomImage.classList.add('loaded');
  4885. };
  4886. tempImage.src = imageUrl;
  4887. }
  4888. }
  4889. // Fonction pour fermer le modal de zoom avec animation
  4890. function closeImageZoom () {
  4891. const zoomModal = document.getElementById('image-zoom-modal');
  4892. const zoomImage = document.getElementById('zoom-modal-image');
  4893. const zoomLoader = document.getElementById('zoom-loader');
  4894. if (zoomModal) { // Animation de fermeture
  4895. if (zoomImage) {
  4896. zoomImage.style.opacity = '0';
  4897. zoomImage.classList.remove('loaded');
  4898. }
  4899. setTimeout(function () {
  4900. zoomModal.classList.remove('active');
  4901. document.body.style.overflow = 'auto';
  4902. // Réinitialiser le loader pour la prochaine ouverture
  4903. if (zoomLoader) {
  4904. zoomLoader.style.display = 'none';
  4905. }
  4906. }, 200);
  4907. }
  4908. }
  4909. // Fermer le modal en cliquant en dehors
  4910. document.addEventListener('DOMContentLoaded', function () {
  4911. const zoomModal = document.getElementById('image-zoom-modal');
  4912. if (zoomModal) {
  4913. zoomModal.addEventListener('click', function (e) {
  4914. if (e.target === zoomModal) {
  4915. closeImageZoom();
  4916. }
  4917. });
  4918. }
  4919. });
  4920. \t</script>
  4921. \t<!-- Modal de zoom d'image -->
  4922. \t<div id=\"image-zoom-modal\" class=\"image-zoom-modal\">
  4923. \t\t<span class=\"close-zoom\" onclick=\"closeImageZoom()\" title=\"Fermer\">&times;</span>
  4924. \t\t<div
  4925. \t\t\tclass=\"zoom-modal-content\">
  4926. \t\t\t<!-- Loader -->
  4927. \t\t\t<div class=\"zoom-loader\" id=\"zoom-loader\" style=\"display: none;\">
  4928. \t\t\t\t<div class=\"zoom-loader-spinner\"></div>
  4929. \t\t\t\t<div class=\"zoom-loader-text\">Chargement...</div>
  4930. \t\t\t</div>
  4931. \t\t\t<!-- Image -->
  4932. \t\t\t<img id=\"zoom-modal-image\" src=\"\" alt=\"{{ product.name }}\">
  4933. \t\t</div>
  4934. \t</div>
  4935. \t{% if dropshipReferral %}
  4936. \t\t<!-- Modal d'affiliation -->
  4937. \t\t<div id=\"dropship-modal\" class=\"dropship-referral-modal\" style=\"display: none;\">
  4938. \t\t\t<div class=\"dropship-modal-overlay\"></div>
  4939. \t\t\t<div class=\"dropship-modal-content\">
  4940. \t\t\t\t<button type=\"button\" class=\"dropship-modal-close\" onclick=\"closeDropshipModal()\" aria-label=\"Fermer\">
  4941. \t\t\t\t\t&times;
  4942. \t\t\t\t</button>
  4943. \t\t\t\t<div class=\"dropship-modal-header\">
  4944. \t\t\t\t\t<div class=\"dropship-icon\">
  4945. \t\t\t\t\t\t<i class=\"fas fa-gift\"></i>
  4946. \t\t\t\t\t</div>
  4947. \t\t\t\t\t<h3>Produit recommandé par
  4948. \t\t\t\t\t\t{{ dropshipReferral.affiliateName }}</h3>
  4949. \t\t\t\t\t<p class=\"dropship-subtitle\">Vous avez été dirigé vers ce produit par un de nos partenaires</p>
  4950. \t\t\t\t</div>
  4951. \t\t\t\t<div class=\"dropship-modal-body\">
  4952. \t\t\t\t\t<div class=\"dropship-product-info\">
  4953. \t\t\t\t\t\t{% if product.images|length > 0 %}
  4954. \t\t\t\t\t\t\t<img src=\"{{ asset(product.images[0]) }}\" alt=\"{{ product.name }}\" class=\"dropship-product-image\" onerror=\"this.style.display='none'; this.nextElementSibling.style.display='flex';\">
  4955. \t\t\t\t\t\t\t<div class=\"dropship-product-image-placeholder\" style=\"display: none; width: 100px; height: 100px; background: #f0f0f0; border-radius: 8px; align-items: center; justify-content: center; color: #999;\">
  4956. \t\t\t\t\t\t\t\t<i class=\"fas fa-image\" style=\"font-size: 32px;\"></i>
  4957. \t\t\t\t\t\t\t</div>
  4958. \t\t\t\t\t\t{% else %}
  4959. \t\t\t\t\t\t\t<div class=\"dropship-product-image-placeholder\" style=\"width: 100px; height: 100px; background: #f0f0f0; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #999;\">
  4960. \t\t\t\t\t\t\t\t<i class=\"fas fa-image\" style=\"font-size: 32px;\"></i>
  4961. \t\t\t\t\t\t\t</div>
  4962. \t\t\t\t\t\t{% endif %}
  4963. \t\t\t\t\t\t<div class=\"dropship-product-details\">
  4964. \t\t\t\t\t\t\t<h4>{{ product.name }}</h4>
  4965. \t\t\t\t\t\t\t<div class=\"dropship-product-price\">
  4966. \t\t\t\t\t\t\t\t<span class=\"price\">{{ product.price|number_format(2, ',', ' ') }}
  4967. \t\t\t\t\t\t\t\t\tHTG</span>
  4968. \t\t\t\t\t\t\t</div>
  4969. \t\t\t\t\t\t</div>
  4970. \t\t\t\t\t</div>
  4971. \t\t\t\t\t<div class=\"dropship-message\">
  4972. \t\t\t\t\t\t<p>
  4973. \t\t\t\t\t\t\t<strong>{{ dropshipReferral.affiliateName }}</strong>
  4974. \t\t\t\t\t\t\tvous recommande ce produit. Souhaitez-vous l'ajouter à votre panier ?</p>
  4975. \t\t\t\t\t</div>
  4976. \t\t\t\t</div>
  4977. \t\t\t\t<div class=\"dropship-modal-footer\">
  4978. \t\t\t\t\t<button type=\"button\" class=\"btn btn-secondary\" onclick=\"closeDropshipModal()\">
  4979. \t\t\t\t\t\tParcourir d'abord
  4980. \t\t\t\t\t</button>
  4981. \t\t\t\t\t<button type=\"button\" class=\"btn btn-primary dropship-add-to-cart\" onclick=\"addToCartFromDropship()\">
  4982. \t\t\t\t\t\t<i class=\"fas fa-shopping-cart\"></i>
  4983. \t\t\t\t\t\tAjouter au panier
  4984. \t\t\t\t\t</button>
  4985. \t\t\t\t</div>
  4986. \t\t\t</div>
  4987. \t\t</div>
  4988. \t\t<style>
  4989. \t\t\t.dropship-referral-modal {
  4990. \t\t\t\tposition: fixed;
  4991. \t\t\t\ttop: 0;
  4992. \t\t\t\tleft: 0;
  4993. \t\t\t\twidth: 100%;
  4994. \t\t\t\theight: 100%;
  4995. \t\t\t\tz-index: 10000;
  4996. \t\t\t\tdisplay: flex;
  4997. \t\t\t\talign-items: center;
  4998. \t\t\t\tjustify-content: center;
  4999. \t\t\t}
  5000. \t\t\t.dropship-modal-overlay {
  5001. \t\t\t\tposition: absolute;
  5002. \t\t\t\ttop: 0;
  5003. \t\t\t\tleft: 0;
  5004. \t\t\t\twidth: 100%;
  5005. \t\t\t\theight: 100%;
  5006. \t\t\t\tbackground: rgba(0, 0, 0, 0.6);
  5007. \t\t\t\tbackdrop-filter: blur(4px);
  5008. \t\t\t}
  5009. \t\t\t.dropship-modal-content {
  5010. \t\t\t\tposition: relative;
  5011. \t\t\t\tbackground: white;
  5012. \t\t\t\tborder-radius: 16px;
  5013. \t\t\t\tbox-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
  5014. \t\t\t\tmax-width: 500px;
  5015. \t\t\t\twidth: 90%;
  5016. \t\t\t\tmax-height: 90vh;
  5017. \t\t\t\toverflow-y: auto;
  5018. \t\t\t\tz-index: 10001;
  5019. \t\t\t\tanimation: dropshipModalSlideIn 0.3s ease-out;
  5020. \t\t\t}
  5021. \t\t\t@keyframes dropshipModalSlideIn {
  5022. \t\t\t\tfrom {
  5023. \t\t\t\t\topacity: 0;
  5024. \t\t\t\t\ttransform: translateY(-30px) scale(0.95);
  5025. \t\t\t\t}
  5026. \t\t\t\tto {
  5027. \t\t\t\t\topacity: 1;
  5028. \t\t\t\t\ttransform: translateY(0) scale(1);
  5029. \t\t\t\t}
  5030. \t\t\t}
  5031. \t\t\t.dropship-modal-close {
  5032. \t\t\t\tposition: absolute;
  5033. \t\t\t\ttop: 15px;
  5034. \t\t\t\tright: 15px;
  5035. \t\t\t\tbackground: none;
  5036. \t\t\t\tborder: none;
  5037. \t\t\t\tfont-size: 28px;
  5038. \t\t\t\tcolor: #666;
  5039. \t\t\t\tcursor: pointer;
  5040. \t\t\t\twidth: 35px;
  5041. \t\t\t\theight: 35px;
  5042. \t\t\t\tdisplay: flex;
  5043. \t\t\t\talign-items: center;
  5044. \t\t\t\tjustify-content: center;
  5045. \t\t\t\tborder-radius: 50%;
  5046. \t\t\t\ttransition: all 0.2s;
  5047. \t\t\t\tz-index: 10;
  5048. \t\t\t}
  5049. \t\t\t.dropship-modal-close:hover {
  5050. \t\t\t\tbackground: #f0f0f0;
  5051. \t\t\t\tcolor: #333;
  5052. \t\t\t}
  5053. \t\t\t.dropship-modal-header {
  5054. \t\t\t\tpadding: 30px 30px 20px;
  5055. \t\t\t\ttext-align: center;
  5056. \t\t\t\tborder-bottom: 1px solid #f0f0f0;
  5057. \t\t\t}
  5058. \t\t\t.dropship-icon {
  5059. \t\t\t\twidth: 70px;
  5060. \t\t\t\theight: 70px;
  5061. \t\t\t\tmargin: 0 auto 15px;
  5062. \t\t\t\tbackground: linear-gradient(135deg, #ffa200 0%, #ff8c00 100%);
  5063. \t\t\t\tborder-radius: 50%;
  5064. \t\t\t\tdisplay: flex;
  5065. \t\t\t\talign-items: center;
  5066. \t\t\t\tjustify-content: center;
  5067. \t\t\t\tfont-size: 32px;
  5068. \t\t\t\tcolor: white;
  5069. \t\t\t\tbox-shadow: 0 4px 15px rgba(255, 162, 0, 0.3);
  5070. \t\t\t}
  5071. \t\t\t.dropship-modal-header h3 {
  5072. \t\t\t\tmargin: 0 0 8px;
  5073. \t\t\t\tfont-size: 22px;
  5074. \t\t\t\tfont-weight: 600;
  5075. \t\t\t\tcolor: #333;
  5076. \t\t\t}
  5077. \t\t\t.dropship-subtitle {
  5078. \t\t\t\tmargin: 0;
  5079. \t\t\t\tcolor: #666;
  5080. \t\t\t\tfont-size: 14px;
  5081. \t\t\t}
  5082. \t\t\t.dropship-modal-body {
  5083. \t\t\t\tpadding: 25px 30px;
  5084. \t\t\t}
  5085. \t\t\t.dropship-product-info {
  5086. \t\t\t\tdisplay: flex;
  5087. \t\t\t\tgap: 15px;
  5088. \t\t\t\tmargin-bottom: 20px;
  5089. \t\t\t\tpadding: 15px;
  5090. \t\t\t\tbackground: #f8f9fa;
  5091. \t\t\t\tborder-radius: 10px;
  5092. \t\t\t}
  5093. \t\t\t.dropship-product-image {
  5094. \t\t\t\twidth: 80px;
  5095. \t\t\t\theight: 80px;
  5096. \t\t\t\tobject-fit: cover;
  5097. \t\t\t\tborder-radius: 8px;
  5098. \t\t\t\tflex-shrink: 0;
  5099. \t\t\t}
  5100. \t\t\t.dropship-product-details {
  5101. \t\t\t\tflex: 1;
  5102. \t\t\t}
  5103. \t\t\t.dropship-product-details h4 {
  5104. \t\t\t\tmargin: 0 0 8px;
  5105. \t\t\t\tfont-size: 16px;
  5106. \t\t\t\tfont-weight: 600;
  5107. \t\t\t\tcolor: #333;
  5108. \t\t\t}
  5109. \t\t\t.dropship-product-price {
  5110. \t\t\t\tmargin-top: 5px;
  5111. \t\t\t}
  5112. \t\t\t.dropship-product-price .price {
  5113. \t\t\t\tfont-size: 20px;
  5114. \t\t\t\tfont-weight: 700;
  5115. \t\t\t\tcolor: #ffa200;
  5116. \t\t\t}
  5117. \t\t\t.dropship-message {
  5118. \t\t\t\tpadding: 15px;
  5119. \t\t\t\tbackground: #fff9e6;
  5120. \t\t\t\tborder-left: 4px solid #ffa200;
  5121. \t\t\t\tborder-radius: 6px;
  5122. \t\t\t}
  5123. \t\t\t.dropship-message p {
  5124. \t\t\t\tmargin: 0;
  5125. \t\t\t\tcolor: #666;
  5126. \t\t\t\tline-height: 1.6;
  5127. \t\t\t}
  5128. \t\t\t.dropship-modal-footer {
  5129. \t\t\t\tpadding: 20px 30px 30px;
  5130. \t\t\t\tdisplay: flex;
  5131. \t\t\t\tgap: 12px;
  5132. \t\t\t\tjustify-content: flex-end;
  5133. \t\t\t\tborder-top: 1px solid #f0f0f0;
  5134. \t\t\t}
  5135. \t\t\t.dropship-modal-footer .btn {
  5136. \t\t\t\tpadding: 12px 24px;
  5137. \t\t\t\tborder-radius: 8px;
  5138. \t\t\t\tfont-weight: 500;
  5139. \t\t\t\tborder: none;
  5140. \t\t\t\tcursor: pointer;
  5141. \t\t\t\ttransition: all 0.2s;
  5142. \t\t\t\tfont-size: 15px;
  5143. \t\t\t}
  5144. \t\t\t.dropship-modal-footer .btn-secondary {
  5145. \t\t\t\tbackground: #f0f0f0;
  5146. \t\t\t\tcolor: #666;
  5147. \t\t\t}
  5148. \t\t\t.dropship-modal-footer .btn-secondary:hover {
  5149. \t\t\t\tbackground: #e0e0e0;
  5150. \t\t\t}
  5151. \t\t\t.dropship-modal-footer .btn-primary {
  5152. \t\t\t\tbackground: #ffa200;
  5153. \t\t\t\tcolor: white;
  5154. \t\t\t}
  5155. \t\t\t.dropship-modal-footer .btn-primary:hover {
  5156. \t\t\t\tbackground: #e8910a;
  5157. \t\t\t\ttransform: translateY(-2px);
  5158. \t\t\t\tbox-shadow: 0 4px 12px rgba(255, 162, 0, 0.3);
  5159. \t\t\t}
  5160. \t\t\t.dropship-modal-footer .btn-primary:disabled {
  5161. \t\t\t\topacity: 0.6;
  5162. \t\t\t\tcursor: not-allowed;
  5163. \t\t\t\ttransform: none;
  5164. \t\t\t}
  5165. \t\t</style>
  5166. \t\t<script>
  5167. \t\t\t// Afficher le modal automatiquement au chargement de la page
  5168. document.addEventListener('DOMContentLoaded', function () {
  5169. const dropshipModal = document.getElementById('dropship-modal');
  5170. if (dropshipModal) {
  5171. setTimeout(function () {
  5172. dropshipModal.style.display = 'flex';
  5173. document.body.style.overflow = 'hidden';
  5174. }, 500); // Délai de 500ms pour une meilleure UX
  5175. }
  5176. });
  5177. function closeDropshipModal () {
  5178. const dropshipModal = document.getElementById('dropship-modal');
  5179. if (dropshipModal) {
  5180. dropshipModal.style.display = 'none';
  5181. document.body.style.overflow = 'auto';
  5182. }
  5183. }
  5184. function addToCartFromDropship () {
  5185. const addBtn = document.querySelector('.dropship-add-to-cart');
  5186. if (! addBtn) 
  5187. return;
  5188. // Désactiver le bouton pendant le traitement
  5189. addBtn.disabled = true;
  5190. addBtn.innerHTML = '<i class=\"fas fa-spinner fa-spin\"></i> Ajout en cours...';
  5191. const productId = {{ product.id }};
  5192. const qty = parseInt(document.getElementById('sst') ?. value || '1');
  5193. // Utiliser fetch pour ajouter au panier
  5194. fetch('{{ path(\"cart_add\") }}', {
  5195. method: 'POST',
  5196. headers: {
  5197. 'Content-Type': 'application/x-www-form-urlencoded',
  5198. 'X-Requested-With': 'XMLHttpRequest'
  5199. },
  5200. body: new URLSearchParams(
  5201. {productId: productId, qty: qty}
  5202. )
  5203. }).then(response => response.json()).then(data => {
  5204. if (data.ok) { // Afficher un message de succès
  5205. const modalBody = document.querySelector('.dropship-modal-body');
  5206. if (modalBody) {
  5207. modalBody.innerHTML = `
  5208. \t\t\t\t\t\t\t\t<div style=\"text-align: center; padding: 30px 20px;\">
  5209. \t\t\t\t\t\t\t\t\t<div style=\"width: 80px; height: 80px; margin: 0 auto 20px; background: #d4edda; border-radius: 50%; display: flex; align-items: center; justify-content: center;\">
  5210. \t\t\t\t\t\t\t\t\t\t<i class=\"fas fa-check\" style=\"font-size: 40px; color: #28a745;\"></i>
  5211. \t\t\t\t\t\t\t\t\t</div>
  5212. \t\t\t\t\t\t\t\t\t<h3 style=\"color: #28a745; margin-bottom: 10px;\">Produit ajouté avec succès !</h3>
  5213. \t\t\t\t\t\t\t\t\t<p style=\"color: #666; margin-bottom: 20px;\">Merci d'avoir utilisé le lien de recommandation de <strong>{{ dropshipReferral.affiliateName }}</strong></p>
  5214. \t\t\t\t\t\t\t\t\t<p style=\"color: #666; font-size: 14px;\">Vous pouvez continuer vos achats ou aller directement au panier.</p>
  5215. \t\t\t\t\t\t\t\t</div>
  5216. \t\t\t\t\t\t\t`;
  5217. }
  5218. const modalFooter = document.querySelector('.dropship-modal-footer');
  5219. if (modalFooter) {
  5220. modalFooter.innerHTML = `
  5221. \t\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-secondary\" onclick=\"closeDropshipModal()\">
  5222. \t\t\t\t\t\t\t\t\tContinuer les achats
  5223. \t\t\t\t\t\t\t\t</button>
  5224. \t\t\t\t\t\t\t\t<a href=\"{{ path('cart') }}\" class=\"btn btn-primary\" style=\"text-decoration: none; display: inline-block;\">
  5225. \t\t\t\t\t\t\t\t\t<i class=\"fas fa-shopping-cart\"></i> Voir le panier
  5226. \t\t\t\t\t\t\t\t</a>
  5227. \t\t\t\t\t\t\t`;
  5228. }
  5229. // Mettre à jour le compteur du panier si présent
  5230. const cartCount = document.querySelector('.cart-count, .cart_count, [data-cart-count]');
  5231. if (cartCount) {
  5232. cartCount.textContent = data.totalQty || 0;
  5233. }
  5234. } else {
  5235. showCustomAlert('Erreur lors de l\\'ajout au panier. Veuillez réessayer.', 'error');
  5236. addBtn.disabled = false;
  5237. addBtn.innerHTML = '<i class=\"fas fa-shopping-cart\"></i> Ajouter au panier';
  5238. }
  5239. }).catch(error => {
  5240. console.error('Erreur:', error);
  5241. showCustomAlert('Une erreur est survenue. Veuillez réessayer.', 'error');
  5242. addBtn.disabled = false;
  5243. addBtn.innerHTML = '<i class=\"fas fa-shopping-cart\"></i> Ajouter au panier';
  5244. });
  5245. }
  5246. // Fermer le modal en cliquant sur l'overlay
  5247. document.addEventListener('DOMContentLoaded', function () {
  5248. const dropshipModal = document.getElementById('dropship-modal');
  5249. if (dropshipModal) {
  5250. const overlay = dropshipModal.querySelector('.dropship-modal-overlay');
  5251. if (overlay) {
  5252. overlay.addEventListener('click', function () {
  5253. closeDropshipModal();
  5254. });
  5255. }
  5256. }
  5257. });
  5258. \t\t</script>
  5259. \t{% endif %}
  5260. \t<!-- Le Modal personnalisé et showCustomAlert ont été déplacés dans base_home.html.twig pour une portée globale -->
  5261. \t<script>
  5262. \t\t// Remplacer window.alert par showCustomAlert
  5263. \t\twindow.alert = window.showCustomAlert || function(message) { console.log(\"Alert:\", message); };
  5264. \t</script>
  5265. {% endblock %}
  5266. ""home/single-product.html.twig""/home/u540977899/domains/maketou-ht.com/public_html/templates/home/single-product.html.twig");
  5267.     }
  5268. }