741 lines
26 KiB
PHP
741 lines
26 KiB
PHP
<?php
|
||
declare (strict_types = 1);
|
||
|
||
require_once __DIR__ . '/../includes/bootstrap_global.php';
|
||
require_once APP_HTDOCS . '/includes/avatar_initiales.php';
|
||
require_once __DIR__ . '/includes/search_helpers.php';
|
||
|
||
$currentPath = parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH) ?? '';
|
||
|
||
// Redirection de la recherche libre vers URL SEO
|
||
if (
|
||
($currentPath === '/recherche' || $currentPath === '/recherche/' || $currentPath === '/result_search.php')
|
||
&& empty($_GET['ajax'])
|
||
) {
|
||
$qsSpecialite = trim((string) ($_GET['specialite'] ?? ''));
|
||
$qsVille = trim((string) ($_GET['ville'] ?? ''));
|
||
|
||
if ($qsSpecialite !== '' || $qsVille !== '') {
|
||
$target = '/recherche';
|
||
|
||
$specSlug = '';
|
||
if ($qsSpecialite === '__OTHERS__') {
|
||
$specSlug = 'autres-specialistes';
|
||
} elseif ($qsSpecialite !== '') {
|
||
$specSlug = seo_spec_slug_from_input($qsSpecialite) ?? '';
|
||
}
|
||
|
||
$citySlug = '';
|
||
if ($qsVille !== '') {
|
||
$citySlug = seo_city_slug_from_input($qsVille);
|
||
}
|
||
|
||
if ($specSlug !== '') {
|
||
$target .= '/' . rawurlencode($specSlug);
|
||
}
|
||
|
||
if ($specSlug !== '') {
|
||
if ($citySlug !== '') {
|
||
$target .= '/' . rawurlencode($citySlug);
|
||
}
|
||
|
||
$target .= '/';
|
||
|
||
$extra = $_GET;
|
||
unset($extra['specialite'], $extra['ville'], $extra['slug']);
|
||
|
||
if (! empty($extra)) {
|
||
$target .= '?' . http_build_query($extra);
|
||
}
|
||
|
||
if (($_SERVER['REQUEST_URI'] ?? '') !== $target) {
|
||
header('Location: ' . $target, true, 301);
|
||
exit;
|
||
}
|
||
} elseif ($citySlug !== '') {
|
||
$target = '/recherche/ville/' . rawurlencode($citySlug) . '/';
|
||
|
||
$extra = $_GET;
|
||
unset($extra['specialite'], $extra['ville'], $extra['slug']);
|
||
|
||
if (! empty($extra)) {
|
||
$target .= '?' . http_build_query($extra);
|
||
}
|
||
|
||
if (($_SERVER['REQUEST_URI'] ?? '') !== $target) {
|
||
header('Location: ' . $target, true, 301);
|
||
exit;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
$SPEC_CANON = require __DIR__ . '/includes/spec_map.php';
|
||
$canon = $SPEC_CANON[$lang] ?? [];
|
||
//Ajout 1 pourpraticien_traduction
|
||
$currentLang = $lang ?? 'ru';
|
||
if (! in_array($currentLang, ['ru', 'fr', 'en'], true)) {
|
||
$currentLang = 'ru';
|
||
}
|
||
|
||
// Lecture et normalisation des filtres
|
||
$specialiteParam = trim($_GET['specialite'] ?? '');
|
||
$ville = trim($_GET['ville'] ?? '');
|
||
|
||
// 🔑 On garde le slug SEO ici
|
||
$resolvedSeoSpecSlug = null;
|
||
|
||
// Remap de la spécialité URL/texte vers la valeur de recherche
|
||
if ($specialiteParam !== '' && $specialiteParam !== '__OTHERS__') {
|
||
|
||
$seoSpecSlug = seo_spec_slug_from_input($specialiteParam);
|
||
|
||
if ($seoSpecSlug !== null) {
|
||
$resolvedSeoSpecSlug = $seoSpecSlug; // ⭐ IMPORTANT
|
||
|
||
$searchSpec = seo_spec_search_value_from_slug($seoSpecSlug, $lang);
|
||
if ($searchSpec !== null) {
|
||
$specialiteParam = $searchSpec;
|
||
}
|
||
}
|
||
}
|
||
|
||
$H1_LABELS = [
|
||
'ru' => [
|
||
'avocats' => 'Адвокаты',
|
||
'consultants' => 'Консультанты',
|
||
'vrachi' => 'Врачи',
|
||
'psychologues' => 'Психологи',
|
||
'repetitori' => 'Преподаватели',
|
||
'finance' => 'Бухгалтеры',
|
||
'perevod' => 'Переводчики',
|
||
'notarius' => 'Нотариусы',
|
||
'gid' => 'Гиды',
|
||
'rieltori' => 'Риелторы',
|
||
'drugie' => 'Специалисты',
|
||
],
|
||
'fr' => [
|
||
'avocats' => 'Avocats',
|
||
'consultants' => 'Consultants',
|
||
'vrachi' => 'Médecins',
|
||
'psychologues' => 'Psychologues',
|
||
'repetitori' => 'Professeurs',
|
||
'finance' => 'Comptables',
|
||
'perevod' => 'Traducteurs',
|
||
'notarius' => 'Notaires',
|
||
'gid' => 'Guides',
|
||
'rieltori' => 'Agents immobiliers',
|
||
'drugie' => 'Spécialistes',
|
||
],
|
||
];
|
||
|
||
// Helper detection video
|
||
function video_thumb_info(?string $url): ?array
|
||
{
|
||
if (! $url || ! trim($url)) {
|
||
return null;
|
||
}
|
||
|
||
$u = trim($url);
|
||
|
||
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"];
|
||
}
|
||
|
||
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"];
|
||
}
|
||
|
||
$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];
|
||
}
|
||
|
||
// inverse map: canon_value -> key
|
||
$canonToKey = [];
|
||
foreach (($SPEC_CANON[$lang] ?? []) as $k => $v) {
|
||
$canonToKey[mb_strtolower($v, 'UTF-8')] = $k;
|
||
}
|
||
|
||
$inFrance = ($lang === 'fr') ? ' en France' : ' во Франции';
|
||
$h1Default = $translations['res_poisk'] ?? 'Результаты поиска';
|
||
$h1 = $h1Default;
|
||
|
||
// Normalisation affichage terme
|
||
$sp = trim(str_replace(["\xC2\xA0"], ' ', $specialiteParam));
|
||
$sp = preg_replace('/\s+/u', ' ', $sp);
|
||
$sp_l = mb_strtolower($sp, 'UTF-8');
|
||
|
||
if ($specialiteParam === '__OTHERS__') {
|
||
$label = $H1_LABELS[$lang]['drugie'] ?? $h1Default;
|
||
$h1 = $label . $inFrance;
|
||
} elseif (! empty($resolvedSeoSpecSlug)) {
|
||
$slugToKey = [
|
||
'avocat' => 'avocats',
|
||
'consultant' => 'consultants',
|
||
'medecin' => 'vrachi',
|
||
'psychologue' => 'psychologues',
|
||
'professeur' => 'repetitori',
|
||
'comptable' => 'finance',
|
||
'traducteur' => 'perevod',
|
||
'notaire' => 'notarius',
|
||
'guide' => 'gid',
|
||
'agent-immobilier' => 'rieltori',
|
||
'autres-specialistes' => 'drugie',
|
||
];
|
||
|
||
$h1Key = $slugToKey[$resolvedSeoSpecSlug] ?? null;
|
||
if ($h1Key !== null) {
|
||
$label = $H1_LABELS[$lang][$h1Key] ?? $h1Default;
|
||
$h1 = $label . $inFrance;
|
||
}
|
||
} elseif ($sp !== '' && isset($canonToKey[$sp_l])) {
|
||
$key = $canonToKey[$sp_l];
|
||
$label = $H1_LABELS[$lang][$key] ?? ($translations[$key] ?? $sp);
|
||
$h1 = $label . $inFrance;
|
||
}
|
||
|
||
// Cas spécial : autres spécialistes
|
||
$onlyOthers = false;
|
||
$specialite = $specialiteParam;
|
||
|
||
if ($specialiteParam === '__OTHERS__') {
|
||
$onlyOthers = true;
|
||
$specialite = '';
|
||
}
|
||
|
||
// Pagination
|
||
$perPage = 12;
|
||
$page = max(1, (int) ($_GET['page'] ?? 1));
|
||
$offset = ($page - 1) * $perPage;
|
||
|
||
// Construction SQL
|
||
$where = [];
|
||
$params = [];
|
||
|
||
/*========================== BLOCK WHERE AVEC ABONNEMENT VALIDE ==========================*/
|
||
$where[] = "(p.date_expiration IS NOT NULL AND p.date_expiration >= DATE_SUB(NOW(), INTERVAL 14 DAY))";
|
||
/*========================================================================================*/
|
||
|
||
// Le champ "specialite" peut contenir une spécialité OU un nom/prénom
|
||
$term = trim((string) $specialite);
|
||
$term_l = mb_strtolower($term, 'UTF-8');
|
||
|
||
// 1) Cas "Другие"
|
||
if ($onlyOthers) {
|
||
$mainSpecs = array_values($canon);
|
||
if ($mainSpecs) {
|
||
$ors = [];
|
||
foreach ($mainSpecs as $i => $spc) {
|
||
$key = ":cat{$i}";
|
||
$ors[] = "LOWER(p.specialite) LIKE $key";
|
||
$params[$key] = mb_strtolower($spc, 'UTF-8') . '%';
|
||
}
|
||
$where[] = '(p.specialite IS NULL OR p.specialite = "" OR NOT (' . implode(' OR ', $ors) . '))';
|
||
}
|
||
}
|
||
// 2) Cas normal
|
||
elseif ($term !== '') {
|
||
$where[] =
|
||
"("
|
||
. "LOWER(COALESCE(pt.specialite, p.specialite)) LIKE :sp_like "
|
||
. "OR LOWER(COALESCE(pt.nom_affiche, p.nom)) LIKE :nm_prefix1 "
|
||
. "OR LOWER(COALESCE(pt.prenom_affiche, p.prenom)) LIKE :nm_prefix2 "
|
||
. "OR LOWER(CONCAT(COALESCE(pt.prenom_affiche, p.prenom),' ',COALESCE(pt.nom_affiche, p.nom))) LIKE :nm_like_full1 "
|
||
. "OR LOWER(CONCAT(COALESCE(pt.nom_affiche, p.nom),' ',COALESCE(pt.prenom_affiche, p.prenom))) LIKE :nm_like_full2 "
|
||
. ")";
|
||
$params[':sp_like'] = '%' . $term_l . '%';
|
||
$params[':nm_prefix1'] = $term_l . '%';
|
||
$params[':nm_prefix2'] = $term_l . '%';
|
||
$params[':nm_like_full1'] = '%' . $term_l . '%';
|
||
$params[':nm_like_full2'] = '%' . $term_l . '%';
|
||
}
|
||
|
||
// Filtre ville — support latin + cyrillique
|
||
if ($ville !== '') {
|
||
$villeCands = city_query_candidates($ville);
|
||
$villeCands[] = mb_strtolower($ville, 'UTF-8');
|
||
$villeCands = array_values(array_unique($villeCands));
|
||
|
||
$villeOrs = [];
|
||
foreach ($villeCands as $i => $cand) {
|
||
$key = ":ville{$i}";
|
||
$params[$key] = $cand . '%';
|
||
$villeOrs[] = "LOWER(COALESCE(pt.ville_affichee, p.ville)) LIKE {$key}";
|
||
}
|
||
if ($villeOrs) {
|
||
$where[] = '(' . implode(' OR ', $villeOrs) . ')';
|
||
}
|
||
}
|
||
|
||
$whereSql = $where ? ('WHERE ' . implode(' AND ', $where)) : '';
|
||
|
||
// total
|
||
$sqlCount = "
|
||
SELECT COUNT(*)
|
||
FROM praticien p
|
||
LEFT JOIN praticien_traduction pt
|
||
ON pt.praticien_id = p.id
|
||
AND pt.langue = :lang
|
||
$whereSql
|
||
";
|
||
|
||
$stmt = $pdo->prepare($sqlCount);
|
||
|
||
$paramsCount = $params;
|
||
$paramsCount[':lang'] = $currentLang;
|
||
|
||
$stmt->execute($paramsCount);
|
||
$total = (int) $stmt->fetchColumn();
|
||
$pages = max(1, (int) ceil($total / $perPage));
|
||
|
||
// data
|
||
$sql = "
|
||
SELECT
|
||
p.id,
|
||
p.nom,
|
||
p.prenom,
|
||
p.ville,
|
||
p.photo,
|
||
p.video,
|
||
COALESCE(pt.nom_affiche, p.nom) AS nom_affiche,
|
||
COALESCE(pt.prenom_affiche, p.prenom) AS prenom_affiche,
|
||
COALESCE(pt.specialite, p.specialite) AS specialite_affichee,
|
||
COALESCE(pt.ville_affichee, p.ville) AS ville_affichee,
|
||
COALESCE(pt.presentation, p.presentation) AS presentation_affichee
|
||
FROM praticien p
|
||
LEFT JOIN praticien_traduction pt
|
||
ON pt.praticien_id = p.id
|
||
AND pt.langue = :lang
|
||
$whereSql
|
||
";
|
||
|
||
if ($term !== '') {
|
||
$sql .= " ORDER BY
|
||
(LOWER(CONCAT(COALESCE(pt.prenom_affiche, p.prenom),' ',COALESCE(pt.nom_affiche, p.nom))) = :nm_exact1
|
||
OR LOWER(CONCAT(COALESCE(pt.nom_affiche, p.nom),' ',COALESCE(pt.prenom_affiche, p.prenom))) = :nm_exact2) DESC,
|
||
(LOWER(COALESCE(pt.nom_affiche, p.nom)) = :nm_exact3 OR LOWER(COALESCE(pt.prenom_affiche, p.prenom)) = :nm_exact4) DESC,
|
||
(LOWER(COALESCE(pt.nom_affiche, p.nom)) LIKE :nm_prefix3 OR LOWER(COALESCE(pt.prenom_affiche, p.prenom)) LIKE :nm_prefix4) DESC,
|
||
(LOWER(COALESCE(pt.specialite, p.specialite)) LIKE :sp_prefix1) DESC,
|
||
COALESCE(pt.nom_affiche, p.nom) ASC,
|
||
COALESCE(pt.prenom_affiche, p.prenom) ASC";
|
||
$params[':nm_exact1'] = $term_l;
|
||
$params[':nm_exact2'] = $term_l;
|
||
$params[':nm_exact3'] = $term_l;
|
||
$params[':nm_exact4'] = $term_l;
|
||
$params[':nm_prefix3'] = $term_l . '%';
|
||
$params[':nm_prefix4'] = $term_l . '%';
|
||
$params[':sp_prefix1'] = $term_l . '%';
|
||
} else {
|
||
$sql .= " ORDER BY p.updated_at DESC, p.id DESC";
|
||
}
|
||
|
||
$sql .= " LIMIT :offset, :limit";
|
||
|
||
$stmt = $pdo->prepare($sql);
|
||
|
||
$params[':lang'] = $currentLang;
|
||
|
||
foreach ($params as $k => $v) {
|
||
$stmt->bindValue($k, $v, PDO::PARAM_STR);
|
||
}
|
||
$stmt->bindValue(':offset', (int) $offset, PDO::PARAM_INT);
|
||
$stmt->bindValue(':limit', (int) $perPage, PDO::PARAM_INT);
|
||
$stmt->execute();
|
||
|
||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
// Si ce n'est pas une catégorie canonique et qu'on a des résultats
|
||
if (
|
||
$h1 === $h1Default
|
||
&& $specialiteParam !== '__OTHERS__'
|
||
&& $sp !== ''
|
||
&& empty($resolvedSeoSpecSlug)
|
||
&& ! isset($canonToKey[$sp_l])
|
||
) {
|
||
if (! empty($rows)) {
|
||
$h1 = '';
|
||
}
|
||
}
|
||
|
||
// util
|
||
function h($s): string
|
||
{
|
||
return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8');
|
||
}
|
||
|
||
function excerpt($text, $len = 160): string
|
||
{
|
||
$t = trim(strip_tags((string) $text));
|
||
if (mb_strlen($t, 'UTF-8') <= $len) {
|
||
return $t;
|
||
}
|
||
|
||
return mb_substr($t, 0, $len, 'UTF-8') . '…';
|
||
}
|
||
|
||
// pour liens de pagination
|
||
$qParams = [];
|
||
|
||
if ($onlyOthers) {
|
||
$qParams['specialite'] = '__OTHERS__';
|
||
} elseif ($specialite !== '') {
|
||
$qParams['specialite'] = $specialite;
|
||
}
|
||
|
||
if ($ville !== '') {
|
||
$qParams['ville'] = $ville;
|
||
}
|
||
|
||
$baseQuery = http_build_query($qParams);
|
||
|
||
// =========================
|
||
// SEO dynamique
|
||
// =========================
|
||
$seoSpecialite = trim((string) $specialiteParam);
|
||
$seoVille = trim((string) $ville);
|
||
|
||
$isCanonCategory = ($seoSpecialite !== '' && isset($canonToKey[mb_strtolower($seoSpecialite, 'UTF-8')]));
|
||
$isOthersCategory = ($seoSpecialite === '__OTHERS__');
|
||
|
||
$seoDisplaySpecialite = $seoSpecialite;
|
||
if ($isOthersCategory) {
|
||
$seoDisplaySpecialite = $translations['drugie'] ?? 'Spécialistes';
|
||
}
|
||
|
||
if ($seoDisplaySpecialite === '') {
|
||
$seoTitle = $translations['boostseo_search_title_default'] ?? '';
|
||
$seoDesc = $translations['boostseo_search_desc_default'] ?? '';
|
||
$seoIntro = $translations['boostseo_search_intro_default'] ?? '';
|
||
} elseif ($seoVille !== '') {
|
||
$seoTitle = strtr(($translations['boostseo_search_title_spec_city'] ?? ''), [
|
||
'%specialite%' => $seoDisplaySpecialite,
|
||
'%ville%' => $seoVille,
|
||
]);
|
||
$seoDesc = strtr(($translations['boostseo_search_desc_spec_city'] ?? ''), [
|
||
'%specialite%' => $seoDisplaySpecialite,
|
||
'%ville%' => $seoVille,
|
||
]);
|
||
$seoIntro = strtr(($translations['boostseo_search_intro_spec_city'] ?? ''), [
|
||
'%specialite%' => $seoDisplaySpecialite,
|
||
'%ville%' => $seoVille,
|
||
]);
|
||
} else {
|
||
$seoTitle = strtr(($translations['boostseo_search_title_spec'] ?? ''), [
|
||
'%specialite%' => $seoDisplaySpecialite,
|
||
]);
|
||
$seoDesc = strtr(($translations['boostseo_search_desc_spec'] ?? ''), [
|
||
'%specialite%' => $seoDisplaySpecialite,
|
||
]);
|
||
$seoIntro = strtr(($translations['boostseo_search_intro_spec'] ?? ''), [
|
||
'%specialite%' => $seoDisplaySpecialite,
|
||
]);
|
||
}
|
||
|
||
$canonicalUrl = 'https://uslugi.fr/recherche';
|
||
|
||
$canonicalSpecSlug = '';
|
||
if ($specialiteParam === '__OTHERS__') {
|
||
$canonicalSpecSlug = 'autres-specialistes';
|
||
} elseif ($specialiteParam !== '') {
|
||
foreach (seo_spec_map() as $slug => $cfg) {
|
||
if (($cfg['db'] ?? '') === $specialiteParam) {
|
||
$canonicalSpecSlug = $slug;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
$cityCanonicalSlug = '';
|
||
if ($ville !== '') {
|
||
$cityCanonicalSlug = seo_city_slug_from_input($ville);
|
||
}
|
||
|
||
if ($canonicalSpecSlug !== '') {
|
||
$canonicalUrl .= '/' . rawurlencode($canonicalSpecSlug);
|
||
if ($cityCanonicalSlug !== '') {
|
||
$canonicalUrl .= '/' . rawurlencode($cityCanonicalSlug);
|
||
}
|
||
} elseif ($cityCanonicalSlug !== '') {
|
||
$canonicalUrl .= '/ville/' . rawurlencode($cityCanonicalSlug);
|
||
}
|
||
|
||
$canonicalUrl .= '/';
|
||
|
||
if ($page > 1) {
|
||
$canonicalUrl .= '?page=' . $page;
|
||
}
|
||
|
||
?>
|
||
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title><?php echo h($seoTitle) ?></title>
|
||
<meta name="description" content="<?php echo h($seoDesc) ?>">
|
||
<meta name="author" content="Uslugi.fr">
|
||
<meta name="robots" content="index, follow">
|
||
<link rel="canonical" href="<?php echo h($canonicalUrl) ?>">
|
||
|
||
<link rel="alternate" hreflang="ru" href="<?php echo h($canonicalUrl . (str_contains($canonicalUrl, '?') ? '&lang=ru' : '?lang=ru')) ?>">
|
||
<link rel="alternate" hreflang="fr" href="<?php echo h($canonicalUrl . (str_contains($canonicalUrl, '?') ? '&lang=fr' : '?lang=fr')) ?>">
|
||
<link rel="alternate" hreflang="x-default" href="<?php echo h($canonicalUrl) ?>">
|
||
|
||
<meta property="og:title" content="<?php echo h($seoTitle) ?>">
|
||
<meta property="og:description" content="<?php echo h($seoDesc) ?>">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="<?php echo h($canonicalUrl) ?>">
|
||
<meta property="og:image" content="https://uslugi.fr/public/uploads/img/logo.png">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
|
||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||
|
||
<link rel="stylesheet" href="<?php echo asset('/includes/site_style.css') ?>">
|
||
<link rel="stylesheet" href="<?php echo asset('/includes/custom_style.css') ?>">
|
||
<link rel="stylesheet" href="<?php echo asset('/includes/mobil_style.css') ?>">
|
||
|
||
<?php
|
||
$trackingFile = __DIR__ . '/includes/tracking.php';
|
||
if (is_file($trackingFile)) {
|
||
include_once $trackingFile;
|
||
}
|
||
?>
|
||
</head>
|
||
|
||
<body class="layout">
|
||
<?php if (! empty($logged)):
|
||
include_header_clean(APP_HTDOCS . '/public/includes/header.php');
|
||
else: ?>
|
||
<header class="site-header">
|
||
<div class="container">
|
||
<div class="header-left">
|
||
<div class="brand">
|
||
<a href="/index.php"><img src="/public/uploads/img/logo_u.png" alt="uslugi.fr" class="logo-mobile"></a>
|
||
<a href="/index.php"><img src="/public/uploads/img/logo.png" alt="uslugi.fr" class="logo-desktop"></a>
|
||
</div>
|
||
<div class="lang-switch">
|
||
<?php require __DIR__ . '/includes/menu_langue.php'; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="header-actions">
|
||
<a class="btn-login" href="/connexion.php">
|
||
<span class="d-sm-none"><?php echo h($translations['btn_se_connecter'] ?? '') ?></span>
|
||
<span class="d-none d-sm-inline"><?php echo h($translations['ja_spec'] ?? '') ?></span>
|
||
</a>
|
||
|
||
<div class="dropdown">
|
||
<button class="btn btn-logo rounded-circle d-inline-flex align-items-center justify-content-center question-btn"
|
||
type="button" data-bs-toggle="dropdown" data-bs-auto-close="true"
|
||
aria-expanded="false" aria-label="Помощь">?</button>
|
||
<ul class="dropdown-menu dropdown-menu-end shadow help-menu menu-panel">
|
||
<li><a href="/how-it-work.php"><?php echo h($translations['kak_rabotaet'] ?? '') ?></a></li>
|
||
<li><a href="/about.php"><?php echo h($translations['pro_nas'] ?? '') ?></a></li>
|
||
<li><a href="/faq.php"><?php echo h($translations['faq'] ?? '') ?></a></li>
|
||
<li><a href="/contact.php"><?php echo h($translations['nam_pisat'] ?? '') ?></a></li>
|
||
</ul>
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
</header>
|
||
<?php endif; ?>
|
||
|
||
<div class="main1">
|
||
<div class="content">
|
||
<div class="container">
|
||
<h1 class="results-title"><?php echo h($h1) ?></h1>
|
||
<p class="visually-hidden"><?php echo h($seoIntro) ?></p>
|
||
</div>
|
||
|
||
<section class="cards-section">
|
||
<div class="container pro-list">
|
||
<?php if ($rows): ?>
|
||
<?php foreach ($rows as $p):
|
||
$img = ! empty($p['photo'])
|
||
? "/public/uploads/" . ltrim((string) $p['photo'], '/')
|
||
: "/public/uploads/img/user_placeholder.png";
|
||
?>
|
||
<article class="pro-card">
|
||
<div class="pro-photo">
|
||
<a href="/detail_public.php?id=<?php echo (int) $p['id'] ?>">
|
||
<img src="<?php echo h($img ?: '/public/uploads/img/user_placeholder.png') ?>" alt="<?php echo h(trim(($p['prenom_affiche'] ?? '') . ' ' . ($p['nom_affiche'] ?? '') . ' — ' . ($p['specialite_affichee'] ?? '') . (! empty($p['ville_affichee']) ? ' — ' . $p['ville_affichee'] : ''))) ?>">
|
||
</a>
|
||
</div>
|
||
|
||
<div class="pro-info">
|
||
<h3 class="pro-name">
|
||
<a href="/detail_public.php?id=<?php echo (int) $p['id'] ?>">
|
||
<?php echo h(($p['prenom_affiche'] ?? '') . ' ' . ($p['nom_affiche'] ?? '')) ?>
|
||
</a>
|
||
</h3>
|
||
|
||
<?php if (! empty($p['specialite_affichee'])): ?>
|
||
<div class="pro-spec"><?php echo h($p['specialite_affichee']) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (! empty($p['presentation_affichee'])): ?>
|
||
<p class="pro-desc"><?php echo h(excerpt($p['presentation_affichee'], 360)) ?></p>
|
||
<?php endif; ?>
|
||
|
||
<a href="/detail_public.php?id=<?php echo (int) $p['id'] ?>">
|
||
<?php echo h($translations['plus_details'] ?? 'Подробнее'); ?>
|
||
</a>
|
||
</div>
|
||
|
||
<?php if (! empty($p['video'])): ?>
|
||
<div class="pro-video-col">
|
||
<?php
|
||
$vinfo = video_thumb_info($p['video']);
|
||
$alt = htmlspecialchars($translations['video_alt'] ?? 'Vidéo', ENT_QUOTES, 'UTF-8');
|
||
$thumb = $vinfo['thumb'] ?? null;
|
||
$prov = $vinfo['provider'] ?? 'other';
|
||
$vid = $vinfo['id'] ?? null;
|
||
$raw = trim((string) ($p['video'] ?? ''));
|
||
?>
|
||
|
||
<?php if (in_array($prov, ['youtube', 'dailymotion'], true) && $vid): ?>
|
||
<a class="pro-video" href="#"
|
||
data-provider="<?php echo htmlspecialchars($prov, ENT_QUOTES, 'UTF-8') ?>"
|
||
data-id="<?php echo htmlspecialchars($vid, ENT_QUOTES, 'UTF-8') ?>"
|
||
aria-label="<?php echo $alt ?>">
|
||
<img src="<?php echo htmlspecialchars($thumb ?: '/public/uploads/img/video_placeholder.jpg', ENT_QUOTES, 'UTF-8') ?>"
|
||
alt="<?php echo $alt ?>">
|
||
</a>
|
||
<?php else: ?>
|
||
<a class="pro-video" href="<?php echo htmlspecialchars($raw, ENT_QUOTES, 'UTF-8') ?>" target="_blank" rel="noopener">
|
||
<img src="<?php echo htmlspecialchars($thumb ?: '/public/uploads/img/video_placeholder.jpg', ENT_QUOTES, 'UTF-8') ?>"
|
||
alt="<?php echo $alt ?>">
|
||
</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="pro-cta-col">
|
||
<a class="pro-cta"
|
||
href="/public/client/reserver_rdv.php?praticien_id=<?php echo (int) $p['id'] ?>">
|
||
<?php echo h($translations['den'] ?? 'Выберите день') ?>
|
||
</a>
|
||
</div>
|
||
</article>
|
||
<?php endforeach; ?>
|
||
<?php else: ?>
|
||
<div class="no-results"><?php echo h($translations['probuite'] ?? 'Нет результатов') ?></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php if ($pages > 1): ?>
|
||
<?php
|
||
$qBase = $baseQuery ? ($baseQuery . '&') : '';
|
||
$prevPage = max(1, $page - 1);
|
||
$nextPage = min($pages, $page + 1);
|
||
?>
|
||
<nav class="pager" aria-label="Pagination">
|
||
<a class="pager-arrow <?php echo $page <= 1 ? 'is-disabled' : '' ?>"
|
||
href="<?php echo h('/result_search.php?' . $qBase . 'page=' . $prevPage) ?>"
|
||
aria-label="Page précédente">««</a>
|
||
|
||
<?php
|
||
$start = max(1, $page - 2);
|
||
$end = min($pages, $page + 2);
|
||
for ($i = $start; $i <= $end; $i++):
|
||
$href = '/result_search.php?' . $qBase . 'page=' . $i;
|
||
?>
|
||
<a class="pager-page <?php echo $i === $page ? 'is-active' : '' ?>"
|
||
href="<?php echo h($href) ?>"><?php echo (int) $i ?></a>
|
||
<?php endfor; ?>
|
||
|
||
<a class="pager-arrow <?php echo $page >= $pages ? 'is-disabled' : '' ?>"
|
||
href="<?php echo h('/result_search.php?' . $qBase . 'page=' . $nextPage) ?>"
|
||
aria-label="Page suivante">»»</a>
|
||
</nav>
|
||
<?php endif; ?>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal vidéo -->
|
||
<div id="lightboxVideo" class="vmodal" aria-hidden="true">
|
||
<div class="vmodal__backdrop" data-close></div>
|
||
<div class="vmodal__dialog" role="dialog" aria-modal="true" aria-label="Vidéo">
|
||
<div class="vmodal__header">
|
||
<h5 class="vmodal__title">Vidéo</h5>
|
||
<button class="vmodal__close" type="button" title="Fermer" aria-label="Fermer" data-close>×</button>
|
||
</div>
|
||
|
||
<div class="vmodal__body">
|
||
<div class="vmodal__ratio">
|
||
<iframe id="vPlayer" src="" title="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="vmodal__footer">
|
||
<button class="btn btn-secondary-like" type="button" data-close>Fermer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<footer class="site-footer">
|
||
<div class="container">
|
||
<p><?php echo $translations['copyr'] ?></p>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
(function(){
|
||
const modal = document.getElementById('lightboxVideo');
|
||
const frame = document.getElementById('vPlayer');
|
||
|
||
function buildSrc(provider, id){
|
||
if (!provider || !id) return '';
|
||
if (provider === 'youtube') return 'https://www.youtube-nocookie.com/embed/' + id + '?autoplay=1&rel=0&modestbranding=1';
|
||
if (provider === 'dailymotion') return 'https://www.dailymotion.com/embed/video/' + id + '?autoplay=1&ui-start-screen-info=false';
|
||
return '';
|
||
}
|
||
|
||
function openModal(src){
|
||
if (!src) return;
|
||
frame.src = src;
|
||
modal.setAttribute('aria-hidden','false');
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
function closeModal(){
|
||
frame.src = '';
|
||
modal.setAttribute('aria-hidden','true');
|
||
document.body.style.overflow = '';
|
||
}
|
||
|
||
document.addEventListener('click', function(e){
|
||
const a = e.target.closest('a.pro-video');
|
||
if (!a) return;
|
||
|
||
const provider = a.getAttribute('data-provider');
|
||
const id = a.getAttribute('data-id');
|
||
|
||
if (provider && id){
|
||
e.preventDefault();
|
||
const src = buildSrc(provider, id);
|
||
openModal(src);
|
||
}
|
||
});
|
||
|
||
modal.addEventListener('click', (e)=>{ if (e.target.hasAttribute('data-close')) closeModal(); });
|
||
window.addEventListener('keydown', (e)=>{ if (e.key === 'Escape' && modal.getAttribute('aria-hidden') === 'false') closeModal(); });
|
||
})();
|
||
</script>
|
||
|
||
<script src="/js/bootstrap.bundle.min.js"></script>
|
||
</body>
|
||
</html>
|