491 lines
19 KiB
PHP
491 lines
19 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../includes/init_client.php';
|
|
|
|
// Support recherche ville en russe + latin
|
|
// Essaie d'abord /public/includes/, sinon /includes/ (selon ton projet)
|
|
$__paths = [
|
|
__DIR__ . '/../includes/search_helpers.php',
|
|
dirname(__DIR__, 2) . '/includes/search_helpers.php',
|
|
];
|
|
foreach ($__paths as $__p) { if (is_file($__p)) { require_once $__p; break; } }
|
|
unset($__paths, $__p);
|
|
|
|
/* =========================
|
|
Paramètres / Recherche
|
|
========================= */
|
|
$perPage = 12;
|
|
$page = max(1, (int)($_GET['page'] ?? 1));
|
|
$qRaw = trim((string)($_GET['q'] ?? ''));
|
|
$wRaw = trim((string)($_GET['where'] ?? ''));
|
|
$q = mb_strtolower($qRaw);
|
|
$w = mb_strtolower($wRaw);
|
|
$isSearch = ($q !== '' || $w !== '');
|
|
|
|
/* =========================
|
|
Helpers
|
|
========================= */
|
|
function video_thumb_info(?string $url): ?array {
|
|
if (!$url || !trim($url)) return null;
|
|
$u = trim($url);
|
|
|
|
// YouTube
|
|
if (preg_match('~(?:youtube\\.com/(?:watch\\?v=|embed/|shorts/)|youtu\\.be/)([A-Za-z0-9_-]{6,})~i', $u, $m)) {
|
|
$id = $m[1];
|
|
return ['provider'=>'youtube','id'=>$id, 'thumb'=>"https://i.ytimg.com/vi/$id/hqdefault.jpg"];
|
|
}
|
|
// Dailymotion
|
|
if (preg_match('~dailymotion\\.com/video/([A-Za-z0-9]+)|dai\\.ly/([A-Za-z0-9]+)~i', $u, $m)) {
|
|
$id = $m[1] ?: $m[2];
|
|
return ['provider'=>'dailymotion','id'=>$id,'thumb'=>"https://www.dailymotion.com/thumbnail/video/$id"];
|
|
}
|
|
// Fichiers directs
|
|
$path = parse_url($u, PHP_URL_PATH) ?? '';
|
|
if (preg_match('~\\.(mp4|webm|ogg)$~i', $path)) return ['provider'=>'file','id'=>null,'thumb'=>null];
|
|
if (strpos(strtolower($u), 'vimeo.com') !== false) return ['provider'=>'vimeo','id'=>null,'thumb'=>null];
|
|
return ['provider'=>'other','id'=>null,'thumb'=>null];
|
|
}
|
|
|
|
function fullName(array $p): string {
|
|
return trim(($p['prenom'] ?? '').' '.($p['nom'] ?? ''));
|
|
}
|
|
|
|
function excerpt_words(string $html, int $words = 60): string {
|
|
$plain = trim(preg_replace('/\\s+/', ' ', strip_tags($html)));
|
|
if ($plain === '') return '';
|
|
$parts = preg_split('/\\s+/', $plain);
|
|
if (count($parts) <= $words) return $plain;
|
|
return implode(' ', array_slice($parts, 0, $words)).'…';
|
|
}
|
|
|
|
/* =========================
|
|
Requêtes (COUNT + LIST)
|
|
========================= */
|
|
$whereSql = [];
|
|
$params = [];
|
|
|
|
/*==========================BLOCK WHERE AVEC ABONNEMENT VALIDE ==========================*/
|
|
$whereSql[] = "(date_expiration IS NOT NULL AND date_expiration >= DATE_SUB(NOW(), INTERVAL 14 DAY))";
|
|
/*=======================================================================================*/
|
|
|
|
if ($q !== '') {
|
|
$words = preg_split('/\s+/', $q, -1, PREG_SPLIT_NO_EMPTY);
|
|
|
|
$and = [];
|
|
foreach ($words as $i => $word) {
|
|
$k1 = ":q_name_$i";
|
|
$k2 = ":q_spec_$i";
|
|
|
|
$and[] = "(
|
|
LOWER(CONCAT(COALESCE(prenom,''),' ',COALESCE(nom,''))) LIKE $k1
|
|
OR LOWER(COALESCE(specialite,'')) LIKE $k2
|
|
)";
|
|
|
|
$params[$k1] = '%'.$word.'%';
|
|
$params[$k2] = '%'.$word.'%';
|
|
}
|
|
|
|
$whereSql[] = '(' . implode(' AND ', $and) . ')';
|
|
}
|
|
|
|
|
|
if ($w !== '') {
|
|
// Candidats latins à partir d'une saisie RU (alias + translittération)
|
|
$villeCands = city_query_candidates($w);
|
|
// Ajoute aussi la saisie brute (utile si déjà en latin)
|
|
$villeCands[] = mb_strtolower($w, 'UTF-8');
|
|
$villeCands = array_values(array_unique($villeCands));
|
|
|
|
$ors = [];
|
|
|
|
// Ville (LOWER(...)) : on cherche par préfixe
|
|
foreach ($villeCands as $i => $cand) {
|
|
$key = ":wv$i";
|
|
$ors[] = "LOWER(COALESCE(ville,'')) LIKE $key";
|
|
$params[$key] = $cand . '%'; // ← passe en '%'.$cand.'%' si tu veux "contient"
|
|
}
|
|
|
|
// Code postal : on garde un LIKE simple sur la saisie brute
|
|
$ors[] = "COALESCE(code_postal,'') LIKE :wcp";
|
|
$params[':wcp'] = '%' . $w . '%';
|
|
|
|
$whereSql[] = '(' . implode(' OR ', $ors) . ')';
|
|
}
|
|
$whereClause = $whereSql ? (' WHERE '.implode(' AND ', $whereSql)) : '';
|
|
|
|
/* Total pour pagination (utile seulement si !isSearch, mais on calcule une fois) */
|
|
$sqlCount = "SELECT COUNT(*) FROM praticien".$whereClause;
|
|
$stmt = $pdo->prepare($sqlCount);
|
|
$stmt->execute($params);
|
|
$total = (int)$stmt->fetchColumn();
|
|
$totalPages = max(1, (int)ceil($total / $perPage));
|
|
|
|
/* Liste (paginée si pas de recherche, sinon tout) */
|
|
$sqlList = "
|
|
SELECT id, titre, nom, prenom, photo, specialite, ville, code_postal, presentation, video
|
|
FROM praticien
|
|
$whereClause
|
|
ORDER BY specialite ASC, nom ASC
|
|
";
|
|
if (!$isSearch) {
|
|
$sqlList .= " LIMIT :limit OFFSET :offset";
|
|
}
|
|
$stmt = $pdo->prepare($sqlList);
|
|
foreach ($params as $k=>$v) $stmt->bindValue($k, $v, PDO::PARAM_STR);
|
|
if (!$isSearch) {
|
|
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
|
|
$stmt->bindValue(':offset', ($page-1)*$perPage, PDO::PARAM_INT);
|
|
}
|
|
$stmt->execute();
|
|
$praticiens = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
/* =========================
|
|
Rendu partiel AJAX (#results)
|
|
========================= */
|
|
function render_results(array $praticiens, array $translations, bool $isSearch, int $page, int $totalPages): void {
|
|
?>
|
|
<?php if ($isSearch): ?>
|
|
<h1 class="results-title"><?= htmlspecialchars($translations['res_poisk'] ?? 'Résultats', ENT_QUOTES, 'UTF-8') ?></h1>
|
|
|
|
<div id="resultCount" class="result-count">
|
|
<?php
|
|
$count = count($praticiens);
|
|
echo $count === 0
|
|
? htmlspecialchars($translations['ne_naideno'] ?? 'Aucun résultat', ENT_QUOTES, 'UTF-8')
|
|
: htmlspecialchars(($translations['naideno'] ?? 'Résultats')." : ".$count, ENT_QUOTES, 'UTF-8');
|
|
?>
|
|
</div>
|
|
<?php endif; ?>
|
|
<div class="pro-list" id="list">
|
|
<?php if (empty($praticiens)): ?>
|
|
|
|
<?php else: ?>
|
|
<?php foreach ($praticiens as $p):
|
|
$name = fullName($p);
|
|
$spec = $p['specialite'] ?? '';
|
|
$ville = $p['ville'] ?? '';
|
|
$cp = $p['code_postal'] ?? '';
|
|
$present= excerpt_words((string)($p['presentation'] ?? ''), 35);
|
|
$vinfo = !empty($p['video']) ? video_thumb_info($p['video']) : null;
|
|
$thumb = $vinfo['thumb'] ?? null;
|
|
$altVid = htmlspecialchars($translations['video_alt'] ?? 'Vidéo', ENT_QUOTES, 'UTF-8');
|
|
$href = !empty($p['video']) ? htmlspecialchars($p['video'], ENT_QUOTES, 'UTF-8') : '';
|
|
?>
|
|
<article class="pro-card praticien"
|
|
data-name="<?= htmlspecialchars(mb_strtolower($name), ENT_QUOTES, 'UTF-8') ?>"
|
|
data-spec="<?= htmlspecialchars(mb_strtolower($spec), ENT_QUOTES, 'UTF-8') ?>"
|
|
data-ville="<?= htmlspecialchars(mb_strtolower($ville), ENT_QUOTES, 'UTF-8') ?>"
|
|
data-cp="<?= htmlspecialchars(mb_strtolower($cp), ENT_QUOTES, 'UTF-8') ?>">
|
|
|
|
<!-- Photo -->
|
|
<div class="pro-photo">
|
|
<a href="/public/client/detail_praticien.php?id=<?= (int)$p['id'] ?>"><img
|
|
src="<?= !empty($p['photo']) ? '/public/uploads/'.htmlspecialchars($p['photo'], ENT_QUOTES, 'UTF-8') : '/public/uploads/img/user_placeholder.png' ?>"
|
|
alt="<?= htmlspecialchars($translations['alt_photo'] ?? 'Photo du praticien', ENT_QUOTES, 'UTF-8') ?>"
|
|
loading="lazy"></a>
|
|
</div>
|
|
|
|
<!-- Infos -->
|
|
<div class="pro-info">
|
|
<h2 class="pro-name"><a href="/public/client/detail_praticien.php?id=<?= (int)$p['id'] ?>"><?= htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?></a></h2>
|
|
<?php if ($spec): ?><div class="pro-spec"><?= htmlspecialchars($spec, ENT_QUOTES, 'UTF-8') ?></div><?php endif; ?>
|
|
<?php if ($present): ?>
|
|
<p class="pro-desc"><?= nl2br(htmlspecialchars($present, ENT_QUOTES, 'UTF-8')) ?></p>
|
|
<?php endif; ?>
|
|
|
|
<a href="#" class="favori_lien" data-id="<?= (int)$p['id'] ?>" data-loading="0">
|
|
<?= htmlspecialchars($translations['plus_favorits'] ?? 'Ajouter aux favoris', ENT_QUOTES, 'UTF-8') ?>
|
|
</a>
|
|
<a href="/public/client/detail_praticien.php?id=<?= (int)$p['id'] ?>">
|
|
<?= htmlspecialchars($translations['plus_details'] ?? 'Détails', ENT_QUOTES, 'UTF-8') ?>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Vidéo -->
|
|
<?php if(!empty($p['video'])): ?>
|
|
<div class="pro-video-col">
|
|
<a class="pro-video"
|
|
href="#"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#videoModal"
|
|
data-yturl="<?= $href ?>"
|
|
aria-label="<?= $altVid ?>">
|
|
<?php if ($thumb): ?>
|
|
<img src="<?= htmlspecialchars($thumb, ENT_QUOTES, 'UTF-8') ?>" alt="<?= $altVid ?>" loading="lazy">
|
|
<?php else: ?>
|
|
<img src="/public/uploads/img/video_placeholder.jpg" alt="<?= $altVid ?>" loading="lazy">
|
|
<?php endif; ?>
|
|
</a>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- CTA -->
|
|
<div class="pro-cta-col">
|
|
<a class="pro-cta" href="/public/client/reserver_rdv.php?praticien_id=<?= (int)$p['id'] ?>">
|
|
<?= htmlspecialchars($translations['den'] ?? 'Prendre RDV', ENT_QUOTES, 'UTF-8') ?>
|
|
</a>
|
|
</div>
|
|
</article>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php if (!$isSearch && $totalPages > 1): ?>
|
|
<nav class="pager" aria-label="Pagination">
|
|
<a class="pager-arrow <?= $page<=1?'is-disabled':'' ?>"
|
|
href="?page=<?= max(1,$page-1) ?>" aria-label="Page précédente">««</a>
|
|
|
|
<?php
|
|
$start = max(1, $page-2);
|
|
$end = min($totalPages, $page+2);
|
|
for ($p=$start; $p<=$end; $p++):
|
|
?>
|
|
<a class="pager-page <?= $p===$page?'is-active':'' ?>" href="?page=<?= $p ?>"><?= $p ?></a>
|
|
<?php endfor; ?>
|
|
|
|
<a class="pager-arrow <?= $page>=$totalPages?'is-disabled':'' ?>"
|
|
href="?page=<?= min($totalPages,$page+1) ?>" aria-label="Page suivante">»»</a>
|
|
</nav>
|
|
<?php endif;
|
|
}
|
|
|
|
/* Si appel AJAX, ne renvoyer que le fragment #results */
|
|
if (isset($_GET['ajax']) && $_GET['ajax'] === '1') {
|
|
header('Content-Type: text/html; charset=utf-8');
|
|
ob_start();
|
|
render_results($praticiens, $translations, $isSearch, $page, $totalPages);
|
|
echo ob_get_clean();
|
|
exit;
|
|
}
|
|
require_once '../includes/header.php';
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title><?= htmlspecialchars($translations['menu_liste_specialistes'] ?? 'Liste des praticiens', ENT_QUOTES, 'UTF-8') ?></title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<!-- Design -->
|
|
<link rel="stylesheet" href="../../includes/custom_style.css">
|
|
<link rel="stylesheet" href="../../includes/mobil_style.css">
|
|
<style>
|
|
/* Pagination pills (<< >> ovale + ronds numérotés) */
|
|
.pager{display:flex;justify-content:center;align-items:center;gap:10px;margin:22px 0;}
|
|
.pager a{text-decoration:none;font-weight:700}
|
|
.pager-arrow{
|
|
padding:10px 18px;border-radius:999px;border:1px solid #E0E3EB;background:#fff;color:#38378B;
|
|
box-shadow:0 6px 14px rgba(0,0,0,.06);transition:background .2s, transform .1s, opacity .2s;
|
|
}
|
|
.pager-arrow:hover{background:#f8f9ff;transform:translateY(-1px)}
|
|
.pager-arrow.is-disabled{opacity:.4;pointer-events:none}
|
|
.pager-page{
|
|
width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;border-radius:50%;
|
|
border:1px solid #E0E3EB;background:#fff;color:#38378B;box-shadow:0 6px 14px rgba(0,0,0,.06);
|
|
transition:background .2s, transform .1s, color .2s, border-color .2s;
|
|
}
|
|
.pager-page:hover{background:#f8f9ff;transform:translateY(-1px)}
|
|
.pager-page.is-active{background:#E03C3F;color:#fff;border-color:#E03C3F;transform:none}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- HERO + BARRE DE RECHERCHE -->
|
|
<section class="hero">
|
|
<br>
|
|
<div class="searchbar" role="search">
|
|
<span class="search-icon" aria-hidden="true">🔎</span>
|
|
<div class="input-wrap">
|
|
<input id="q" type="text"
|
|
placeholder="<?= htmlspecialchars($translations['n_special'] ?? 'spécialité, nom…', ENT_QUOTES, 'UTF-8') ?>"
|
|
aria-label="spécialité, nom" value="<?= htmlspecialchars($qRaw, ENT_QUOTES, 'UTF-8') ?>">
|
|
</div>
|
|
<div class="divider" aria-hidden="true"></div>
|
|
<div class="input-wrap">
|
|
<input id="where" type="text"
|
|
placeholder="<?= htmlspecialchars($translations['gde'] ?? 'ville ou code postal', ENT_QUOTES, 'UTF-8') ?>"
|
|
aria-label="ville ou code postal" value="<?= htmlspecialchars($wRaw, ENT_QUOTES, 'UTF-8') ?>">
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- LISTE + PAGINATION (wrapper remplaçable en AJAX) -->
|
|
<div class="container">
|
|
<div id="results">
|
|
<?php render_results($praticiens, $translations, $isSearch, $page, $totalPages); ?>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="site-footer">
|
|
<div class="container">
|
|
<p><?= $translations['copyr'] ?></p>
|
|
</div>
|
|
</footer>
|
|
|
|
<!-- Modal vidéo -->
|
|
<div class="modal fade" id="videoModal" tabindex="-1" aria-labelledby="videoModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-xl">
|
|
<div class="modal-content border-0 shadow-lg rounded-4">
|
|
<div class="modal-header border-0">
|
|
<h5 class="modal-title" id="videoModalLabel"><?= htmlspecialchars($translations['video_titre'] ?? 'Vidéo') ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="<?= htmlspecialchars($translations['fermer'] ?? 'Fermer') ?>"></button>
|
|
</div>
|
|
<div class="modal-body pt-0">
|
|
<div class="ratio ratio-16x9 rounded-3 overflow-hidden">
|
|
<iframe id="ytPlayer"
|
|
src=""
|
|
title="YouTube video player"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
allowfullscreen
|
|
referrerpolicy="strict-origin-when-cross-origin"></iframe>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer border-0">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
|
<?= htmlspecialchars($translations['fermer'] ?? 'Fermer') ?>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- JS -->
|
|
<script>
|
|
(function(){
|
|
// Convertit n'importe quelle URL YouTube en URL d'embed nocookie
|
|
function toEmbed(url) {
|
|
if (!url) return '';
|
|
try {
|
|
const u = new URL(url), host = u.hostname;
|
|
let id = '';
|
|
if (host.includes('youtu.be')) {
|
|
id = u.pathname.split('/').filter(Boolean)[0] || '';
|
|
} else if (u.searchParams.get('v')) {
|
|
id = u.searchParams.get('v');
|
|
} else if (u.pathname.includes('/embed/')) {
|
|
id = u.pathname.split('/embed/')[1].split('/')[0];
|
|
} else if (u.pathname.includes('/shorts/')) {
|
|
id = u.pathname.split('/shorts/')[1].split('/')[0];
|
|
}
|
|
id = (id || '').replace(/[^a-zA-Z0-9_-]/g, '');
|
|
return id ? `https://www.youtube-nocookie.com/embed/${id}` : '';
|
|
} catch { return ''; }
|
|
}
|
|
|
|
const modal = document.getElementById('videoModal');
|
|
const iframe = document.getElementById('ytPlayer');
|
|
|
|
document.addEventListener('click', function(e) {
|
|
const a = e.target.closest('a.pro-video[data-yturl]');
|
|
if (!a) return;
|
|
e.preventDefault();
|
|
const embed = toEmbed(a.getAttribute('data-yturl'));
|
|
if (embed) iframe.src = embed + '?autoplay=1&rel=0&modestbranding=1';
|
|
}, false);
|
|
|
|
modal.addEventListener('hidden.bs.modal', function () { iframe.src = ''; });
|
|
|
|
// Recherche AJAX sur toute la table
|
|
const q = document.getElementById('q');
|
|
const where = document.getElementById('where');
|
|
const results = document.getElementById('results');
|
|
|
|
let t;
|
|
function reload(page=1) {
|
|
const qs = new URLSearchParams({
|
|
ajax: '1',
|
|
q: q.value.trim(),
|
|
where: where.value.trim(),
|
|
page: page
|
|
});
|
|
fetch(location.pathname + '?' + qs.toString(), { headers:{'X-Requested-With':'XMLHttpRequest'} })
|
|
.then(r => r.text())
|
|
.then(html => { results.innerHTML = html; })
|
|
.catch(()=>{ /* silencieux */ });
|
|
}
|
|
|
|
function onInput(){ clearTimeout(t); t = setTimeout(() => reload(1), 220); }
|
|
q.addEventListener('input', onInput);
|
|
where.addEventListener('input', onInput);
|
|
|
|
// Pagination progressive enhancement (charge dans #results)
|
|
document.addEventListener('click', function(e){
|
|
const a = e.target.closest('.pager a[href]');
|
|
if (!a) return;
|
|
e.preventDefault();
|
|
const url = new URL(a.getAttribute('href'), location.href);
|
|
// Conserve recherche si non vide
|
|
if (q.value.trim()) url.searchParams.set('q', q.value.trim());
|
|
if (where.value.trim()) url.searchParams.set('where', where.value.trim());
|
|
url.searchParams.set('ajax', '1');
|
|
fetch(url.toString(), { headers:{'X-Requested-With':'XMLHttpRequest'} })
|
|
.then(r=>r.text()).then(html=>{ results.innerHTML = html; });
|
|
}, false);
|
|
|
|
// Favoris (SweetAlert2)
|
|
function ajouterFavori(el, praticienId) {
|
|
if (el.dataset.loading === '1') return;
|
|
el.dataset.loading = '1';
|
|
fetch('/public/client/ajouter_favori.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
},
|
|
body: new URLSearchParams({ praticien_id: praticienId }).toString()
|
|
})
|
|
.then(async (r) => {
|
|
const text = await r.text();
|
|
if (!r.ok) throw new Error(text || 'Erreur réseau');
|
|
const deja = /déjà|already/i.test(text);
|
|
el.classList.add('ajoute'); el.setAttribute('aria-pressed', 'true'); el.title = text;
|
|
if (typeof Swal !== 'undefined') {
|
|
Swal.fire({
|
|
icon: deja ? 'info' : 'success',
|
|
title: <?= json_encode($translations['sw_favori_ajoute_title'] ?? 'Favori ajouté !', JSON_UNESCAPED_UNICODE) ?>,
|
|
text: text,
|
|
timer: 1800,
|
|
showConfirmButton: false
|
|
});
|
|
} else { alert(text); }
|
|
})
|
|
.catch((err) => {
|
|
if (typeof Swal !== 'undefined') {
|
|
Swal.fire({ icon:'error', title:'Oups', text: err.message || 'Erreur.' });
|
|
} else { console.error(err); alert('Erreur.'); }
|
|
})
|
|
.finally(() => { el.dataset.loading = '0'; });
|
|
}
|
|
|
|
document.addEventListener('click', (e)=>{
|
|
const a = e.target.closest('.favori_lien');
|
|
if (!a) return;
|
|
e.preventDefault();
|
|
const id = a.dataset.id;
|
|
|
|
const T = {
|
|
title: <?= json_encode($translations['confirmation'] ?? 'Confirmation') ?>,
|
|
text: <?= json_encode($translations['voulez_vous_ajouter_favori'] ?? 'Ajouter ce praticien à vos favoris ?') ?>,
|
|
ok: <?= json_encode($translations['btn_oui'] ?? 'Oui') ?>,
|
|
cancel:<?= json_encode($translations['btn_annuler'] ?? 'Annuler') ?>
|
|
};
|
|
|
|
if (typeof Swal !== 'undefined') {
|
|
Swal.fire({
|
|
icon: 'question',
|
|
title: T.title,
|
|
text: T.text,
|
|
showCancelButton: true,
|
|
confirmButtonText: T.ok,
|
|
cancelButtonText: T.cancel,
|
|
}).then(res => { if (res.isConfirmed) ajouterFavori(a, id); });
|
|
} else {
|
|
if (confirm(T.text)) ajouterFavori(a, id);
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
<script src="/js/sweetalert2@11.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" defer></script>
|
|
</body>
|
|
</html>
|