617 lines
21 KiB
PHP
617 lines
21 KiB
PHP
<?php
|
||
$lang = $_SESSION['lang'] ?? 'fr';
|
||
|
||
$PAGE_TITLE = $PAGE_TITLE ?? ($translations['site_name'] ?? 'uslugi.fr');
|
||
$HEAD_EXTRA = $HEAD_EXTRA ?? '';
|
||
|
||
$initiales = $initiales ?? ($GLOBALS['initiales'] ?? 'U');
|
||
$avatarSvg = $avatar_svg ?? ($GLOBALS['avatar_svg'] ?? null);
|
||
$pushCfg = $pushCfg ?? ($GLOBALS['pushCfg'] ?? []);
|
||
$alerte_abonnement = $alerte_abonnement ?? ($GLOBALS['alerte_abonnement'] ?? null);
|
||
|
||
require_once __DIR__ . '/../contact_admin.php';
|
||
|
||
// =====================================================
|
||
// [RETOUR / REFERER SÉCURISÉ]
|
||
// - Autorise le retour seulement si le referer est sur le même host
|
||
// =====================================================
|
||
$ref = $_SERVER['HTTP_REFERER'] ?? '';
|
||
$host = $_SERVER['HTTP_HOST'] ?? '';
|
||
$safeRef = '';
|
||
if ($ref) {
|
||
$u = parse_url($ref);
|
||
if (!empty($u['host']) && $u['host'] === $host) {
|
||
$safeRef = $ref;
|
||
}
|
||
}
|
||
$fallbackBack = $safeRef ?: '/';
|
||
|
||
// =====================================================
|
||
// [DETECTION MOBILE]
|
||
// - Très simple détection UA pour le logo adaptatif
|
||
// =====================================================
|
||
$is_mobile = preg_match('/Mobile|Android|iPhone|iPad/i', $_SERVER['HTTP_USER_AGENT']);
|
||
?>
|
||
<!doctype html>
|
||
<html lang="fr">
|
||
<head>
|
||
<title><?= htmlspecialchars($PAGE_TITLE, ENT_QUOTES, 'UTF-8') ?></title>
|
||
<?= $HEAD_EXTRA ?>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||
|
||
<!-- Police Montserrat -->
|
||
|
||
|
||
<!-- Bootstrap -->
|
||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||
<script defer src="/js/bootstrap.bundle.min.js"></script>
|
||
|
||
|
||
<!-- Styles personnalisés -->
|
||
<link rel="stylesheet" href="<?= asset('/includes/site_style.css') ?>">
|
||
<link rel="stylesheet" href="<?= asset('/includes/custom_style.css') ?>"> <!-- ta home -->
|
||
<link rel="stylesheet" href="<?= asset('/includes/mobil_style.css') ?>">
|
||
</head>
|
||
<body>
|
||
<?php
|
||
// Pour n'afficher qu'une fois par compte (PWA)
|
||
$pushOnboardKey = 'uslugi_push_onboard_v1_' . (int)($_SESSION['compte_id'] ?? 0);
|
||
|
||
// helper traduction (si tu n'as pas déjà $t ici)
|
||
$t = function(string $k, string $fallback = '') use (&$translations): string {
|
||
$val = $translations[$k] ?? null;
|
||
return ($val === null || $val === '') ? $fallback : (string)$val;
|
||
};
|
||
?>
|
||
|
||
<div class="modal fade" id="pushOnboardModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-dialog-centered">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">
|
||
<?= htmlspecialchars($t('push_onboard_title', 'Activer les notifications ?'), ENT_QUOTES, 'UTF-8') ?>
|
||
</h5>
|
||
<button type="button"
|
||
class="btn-close"
|
||
data-bs-dismiss="modal"
|
||
aria-label="<?= htmlspecialchars($t('push_onboard_close_aria', 'Fermer'), ENT_QUOTES, 'UTF-8') ?>">
|
||
</button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<?= htmlspecialchars($t('push_onboard_body', 'Vous recevrez des alertes pour les nouveaux messages et rendez-vous.'), ENT_QUOTES, 'UTF-8') ?>
|
||
</div>
|
||
|
||
<div class="modal-footer">
|
||
<button type="button"
|
||
class="btn btn-outline-secondary"
|
||
data-bs-dismiss="modal"
|
||
id="pushLaterBtn">
|
||
<?= htmlspecialchars($t('push_onboard_later', 'Plus tard'), ENT_QUOTES, 'UTF-8') ?>
|
||
</button>
|
||
|
||
<button type="button"
|
||
class="btn btn-primary"
|
||
id="pushEnableBtn">
|
||
<?= htmlspecialchars($t('push_onboard_allow', 'Autoriser'), ENT_QUOTES, 'UTF-8') ?>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ================================================
|
||
[HEADER / BARRE DE NAVIGATION]
|
||
- Bouton retour sécurisé vers $fallbackBack
|
||
- Logo adaptatif mobile/desktop
|
||
- Icône messages avec badge non lus
|
||
- Menu principal + sous-menu Admin
|
||
- Menu "?" compact (aide)
|
||
================================================ -->
|
||
<header class="site-header py-2 border-bottom">
|
||
|
||
|
||
<?php if (!empty($_SESSION['admin_impersonate'])): ?>
|
||
<div class="alert alert-warning text-center mb-0 rounded-0">
|
||
Вы вошли как другой пользователь.
|
||
<a href="/admin/tools/retour_admin.php"
|
||
class="btn btn-sm btn-danger ms-2"
|
||
style="border-radius:999px;">
|
||
Вернуться в режим администратора
|
||
</a>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php $alerte_abonnement = $GLOBALS['alerte_abonnement'] ?? null; ?>
|
||
<?php if (!empty($alerte_abonnement)): ?>
|
||
<div class="alert alert-warning m-0 py-1 px-2 rounded"
|
||
role="alert"
|
||
style="background:#F1F1F1; color:#E03C3F; font-size:14px; text-align:center;">
|
||
<?= htmlspecialchars((string)$alerte_abonnement, ENT_QUOTES, 'UTF-8') ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="container d-flex justify-content-between align-items-center">
|
||
|
||
<!-- Marque + Bouton retour + Logo -->
|
||
<div class="header-left">
|
||
<div class="brand">
|
||
<!-- Bouton retour -->
|
||
<a
|
||
href="<?= htmlspecialchars($fallbackBack, ENT_QUOTES, 'UTF-8') ?>"
|
||
id="btnBack"
|
||
class="btn btn-light btn-circle me-2"
|
||
style="border-radius:5px; box-shadow:0 1px 2px rgba(0,0,0,.08);"
|
||
aria-label="<?= htmlspecialchars($translations['retour'] ?? 'Retour', ENT_QUOTES, 'UTF-8') ?>"
|
||
>
|
||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" aria-hidden="true">
|
||
<path
|
||
d="M15 6l-6 6 6 6"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
/>
|
||
</svg>
|
||
</a>
|
||
|
||
<!-- Logo -->
|
||
<a href="/index.php">
|
||
<img src="/public/uploads/img/logo_u.png"
|
||
alt="uslugi.fr"
|
||
class="logo-mobile">
|
||
|
||
<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>
|
||
<!-- Actions header (avatar, messages, menu, aide) -->
|
||
<nav class="header-actions d-flex align-items-center gap-2 position-relative">
|
||
|
||
|
||
<!-- RDV (+ badge non lus) -->
|
||
<a
|
||
id="btn-rdv"
|
||
href="<?= ($_SESSION['role'] ?? '') === 'praticien'
|
||
? '/public/praticien/rendez_vous.php'
|
||
: '/public/client/mes_rendez_vous.php' ?>"
|
||
class="nav-rdv position-relative"
|
||
>
|
||
<img src="/public/uploads/img/bell-on.png" width="22" alt="Rendez-vous">
|
||
<span
|
||
id="rdv-badge"
|
||
style="position:absolute; top:-6px; right:-8px; background:#e11d48; color:#fff;
|
||
font-size:12px; min-width:18px; height:18px; line-height:18px;
|
||
text-align:center; border-radius:999px; padding:0 5px; display:none;"
|
||
>0</span>
|
||
</a>
|
||
|
||
|
||
<!-- Messages (+ badge non lus) -->
|
||
<a href="/messages/" class="nav-msg position-relative">
|
||
<img src="/public/uploads/img/message.png" width="20" alt="Messages">
|
||
<span
|
||
id="msg-badge"
|
||
style="position:absolute; top:-6px; right:-8px; background:#e11d48; color:#fff;
|
||
font-size:12px; min-width:18px; height:18px; line-height:18px;
|
||
text-align:center; border-radius:999px; padding:0 5px; display:none;"
|
||
>0</span>
|
||
</a>
|
||
|
||
<!-- Menu principal -->
|
||
<div class="menu-wrap">
|
||
<button type="button" id="btnMenu">
|
||
<div class="avatar"><?= $initiales ?></div>
|
||
</button>
|
||
|
||
<!-- Menu principal (liste) -->
|
||
<ul class="dropdown-menu-custom menu-panel" id="menu">
|
||
<?php if ($_SESSION['role'] === 'client'): ?>
|
||
<li onclick="location.href='/public/client/mes_rendez_vous.php'">
|
||
<?= htmlspecialchars($translations['menu_mes_rendez_vous']) ?>
|
||
</li>
|
||
<li onclick="location.href='/public/client/list_praticien.php'">
|
||
<?= htmlspecialchars($translations['menu_liste_specialistes']) ?>
|
||
</li>
|
||
<li onclick="location.href='/public/client/mes_favori.php'">
|
||
<?= htmlspecialchars($translations['menu_mes_favoris']) ?>
|
||
</li>
|
||
<?php else: ?>
|
||
<li onclick="location.href='/public/praticien/rendez_vous.php'">
|
||
<?= htmlspecialchars($translations['menu_mes_rendez_vous']) ?>
|
||
</li>
|
||
<li onclick="location.href='/public/praticien/agenda.php'">
|
||
<?= htmlspecialchars($translations['menu_mon_agenda']) ?>
|
||
</li>
|
||
<li onclick="location.href='/public/praticien/disponibilites.php'">
|
||
<?= htmlspecialchars($translations['menu_mes_disponibilites']) ?>
|
||
</li>
|
||
<li onclick="location.href='/public/praticien/synchronisation.php'">
|
||
<?= htmlspecialchars($translations['menu_sync_agendas']) ?>
|
||
</li>
|
||
<!--<li onclick="location.href='/public/praticien/clients/index.php'">
|
||
<?= htmlspecialchars($translations['menu_mes_clients'] ?? 'Mes clients') ?>
|
||
</li>-->
|
||
|
||
<?php endif; ?>
|
||
|
||
<!-- Sous-menu Admin -->
|
||
<li class="has-submenu">
|
||
<button
|
||
type="button"
|
||
class="submenu-toggle"
|
||
aria-expanded="false"
|
||
aria-controls="submenu-admin"
|
||
>
|
||
<?= htmlspecialchars($translations['menu_admin']) ?>
|
||
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
|
||
<path d="M6 9l6 6 6-6" fill="none" stroke="currentColor" stroke-width="2"/>
|
||
</svg>
|
||
</button>
|
||
|
||
<ul id="submenu-admin" class="submenu" hidden>
|
||
<?php if ($_SESSION['role'] === 'praticien'): ?>
|
||
<li onclick="location.href='/public/praticien/resiliation.php'">
|
||
<?= htmlspecialchars($translations['menu_gerer_abonnement']) ?>
|
||
</li>
|
||
<li onclick="location.href='/public/praticien/factures.php'">
|
||
<?= htmlspecialchars($translations['menu_mes_factures']) ?>
|
||
</li>
|
||
<li onclick="location.href='/public/cabinet/invitations.php'">
|
||
<?= htmlspecialchars($translations['secretariat_inviter_secretaire']) ?>
|
||
</li>
|
||
<li onclick="location.href='/public/praticien/clients/index.php'">
|
||
<?= htmlspecialchars($translations['menu_mes_clients'] ?? 'Mes clients') ?>
|
||
</li>
|
||
<?php endif; ?>
|
||
|
||
<li onclick="location.href='/public/<?= $_SESSION['role'] ?>/informations.php'">
|
||
<?= htmlspecialchars($translations['menu_mes_informations']) ?>
|
||
</li>
|
||
|
||
<li onclick="location.href='/public/<?= $_SESSION['role'] ?>/compte.php'">
|
||
<?= htmlspecialchars($translations['menu_infos_connexion']) ?>
|
||
</li>
|
||
<li onclick="location.href='/supprimer_compte.php'">
|
||
<?= htmlspecialchars($translations['menu_supprimer_compte']) ?>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
|
||
|
||
|
||
<!-- Déconnexion -->
|
||
<li onclick="closeMenu(); openContactAdmin()">
|
||
<?= htmlspecialchars($translations['contact_admin']) ?>
|
||
</li>
|
||
<li onclick="location.href='/public/deconnexion.php'">
|
||
<?= htmlspecialchars($translations['menu_deconnexion']) ?>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ================================================
|
||
[SCRIPTS / LOGIQUE CLIENT]
|
||
- Badge messages non lus (polling)
|
||
- Ouverture/fermeture du menu principal
|
||
- Gestion du sous-menu Admin (ARIA)
|
||
- Bootstrap JS bundle
|
||
================================================ -->
|
||
<script>
|
||
|
||
(function(){
|
||
function setBadge(el, n){
|
||
if (!el) return;
|
||
n = Number(n || 0);
|
||
if (n > 0){
|
||
el.style.display = 'inline-block';
|
||
el.textContent = String(n > 99 ? '99+' : n);
|
||
} else {
|
||
el.style.display = 'none';
|
||
el.textContent = '0';
|
||
}
|
||
}
|
||
|
||
async function tickBadges(){
|
||
try {
|
||
const r = await fetch('/push/badge_count.php', { credentials:'same-origin', cache:'no-store' });
|
||
if (!r.ok) return;
|
||
const j = await r.json();
|
||
if (!j || !j.ok) return;
|
||
|
||
// UI header
|
||
setBadge(document.getElementById('msg-badge'), j.msg_unread);
|
||
setBadge(document.getElementById('rdv-badge'), j.rdv_unread);
|
||
|
||
// Badge PWA (si supporté)
|
||
const n = Number(j.app_badge || 0);
|
||
if ('setAppBadge' in navigator) {
|
||
try {
|
||
if (n > 0) await navigator.setAppBadge(n);
|
||
else if ('clearAppBadge' in navigator) await navigator.clearAppBadge();
|
||
} catch(_) {}
|
||
}
|
||
} catch(e){}
|
||
}
|
||
|
||
// 1) au chargement
|
||
tickBadges();
|
||
|
||
// 2) refresh (mets 10s au lieu de 5s pour soulager)
|
||
setInterval(tickBadges, 10000);
|
||
|
||
// 3) quand on revient sur l’onglet
|
||
document.addEventListener('visibilitychange', () => {
|
||
if (!document.hidden) tickBadges();
|
||
});
|
||
|
||
// optionnel : dispo ailleurs
|
||
window.refreshBadges = tickBadges;
|
||
})();
|
||
|
||
|
||
|
||
// -------------------------------------------------
|
||
// [MENU PRINCIPAL] — Ouverture / Fermeture
|
||
// -------------------------------------------------
|
||
function toggleMenu() {
|
||
const menu = document.getElementById('menu');
|
||
if (!menu) return;
|
||
menu.style.display = (menu.style.display === 'block') ? 'none' : 'block';
|
||
}
|
||
|
||
function closeMenu() {
|
||
const menu = document.getElementById('menu');
|
||
if (menu) menu.style.display = 'none';
|
||
closeSubmenus();
|
||
}
|
||
|
||
document.getElementById('btnMenu').addEventListener('click', toggleMenu);
|
||
|
||
// Ferme le menu en cliquant à l'extérieur
|
||
document.addEventListener('click', (e) => {
|
||
const menu = document.getElementById('menu');
|
||
if (!menu) return;
|
||
const toggle = document.getElementById('btnMenu');
|
||
if (!toggle.contains(e.target) && !menu.contains(e.target)) {
|
||
closeMenu();
|
||
}
|
||
});
|
||
|
||
// Ferme avec la touche Escape
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Escape') closeMenu();
|
||
});
|
||
|
||
// -------------------------------------------------
|
||
// [SOUS-MENU ADMIN] — Toggle + ARIA
|
||
// -------------------------------------------------
|
||
document.addEventListener('click', function(e) {
|
||
const btn = e.target.closest('.submenu-toggle');
|
||
if (!btn) return;
|
||
|
||
const submenuId = btn.getAttribute('aria-controls');
|
||
const submenu = document.getElementById(submenuId);
|
||
if (!submenu) return;
|
||
|
||
const isOpen = btn.getAttribute('aria-expanded') === 'true';
|
||
btn.setAttribute('aria-expanded', String(!isOpen));
|
||
submenu.hidden = isOpen;
|
||
e.stopPropagation();
|
||
});
|
||
|
||
function closeSubmenus() {
|
||
document
|
||
.querySelectorAll('.submenu-toggle[aria-expanded="true"]')
|
||
.forEach(btn => {
|
||
btn.setAttribute('aria-expanded', 'false');
|
||
const sub = document.getElementById(btn.getAttribute('aria-controls'));
|
||
if (sub) sub.hidden = true;
|
||
});
|
||
}
|
||
</script>
|
||
<script>
|
||
(async function registerSW(){
|
||
if (!('serviceWorker' in navigator)) return;
|
||
|
||
try {
|
||
// IMPORTANT : scope racine pour couvrir /push/, /messages/, /public/...
|
||
const reg = await navigator.serviceWorker.register('/sw.js', { scope: '/' });
|
||
|
||
// optionnel : log utile
|
||
// console.log('SW registered', reg.scope);
|
||
|
||
} catch (e) {
|
||
console.error('SW register failed', e);
|
||
}
|
||
})();
|
||
</script>
|
||
<script>
|
||
if ('serviceWorker' in navigator) {
|
||
navigator.serviceWorker.addEventListener('message', async (e) => {
|
||
const msg = e.data || {};
|
||
if (msg.type === 'SET_BADGE') {
|
||
const n = Number(msg.badgeCount ?? 0);
|
||
|
||
// iOS/Safari: parfois pas supporté => silence
|
||
if ('setAppBadge' in navigator) {
|
||
try {
|
||
if (n > 0) await navigator.setAppBadge(n);
|
||
else if ('clearAppBadge' in navigator) await navigator.clearAppBadge();
|
||
} catch (_) {}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
</script>
|
||
<script>
|
||
(async function pushOnboardingPWA(){
|
||
const isPWA =
|
||
window.matchMedia('(display-mode: standalone)').matches ||
|
||
window.navigator.standalone === true;
|
||
|
||
if (!isPWA) return;
|
||
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return;
|
||
if (!('Notification' in window)) return;
|
||
|
||
const VAPID_PUBLIC = "<?= htmlspecialchars($pushCfg['vapid_public'] ?? '', ENT_QUOTES) ?>";
|
||
if (!VAPID_PUBLIC) return;
|
||
|
||
const onboardKey = "<?= htmlspecialchars($pushOnboardKey, ENT_QUOTES) ?>";
|
||
|
||
function urlBase64ToUint8Array(base64String) {
|
||
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
|
||
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
|
||
const raw = atob(base64);
|
||
const arr = new Uint8Array(raw.length);
|
||
for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i);
|
||
return arr;
|
||
}
|
||
|
||
async function getSub(){
|
||
const reg = await navigator.serviceWorker.ready;
|
||
return await reg.pushManager.getSubscription();
|
||
}
|
||
|
||
async function syncToServer(sub){
|
||
const res = await fetch('/push/subscribe.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
credentials: 'same-origin',
|
||
cache: 'no-store',
|
||
body: JSON.stringify({
|
||
endpoint: sub.endpoint,
|
||
keys: sub.toJSON().keys,
|
||
userAgent: navigator.userAgent,
|
||
deviceLabel: 'PWA'
|
||
})
|
||
});
|
||
if (!res.ok) throw new Error('subscribe.php failed: ' + res.status);
|
||
return true;
|
||
}
|
||
|
||
async function subscribeNow(){
|
||
const reg = await navigator.serviceWorker.ready;
|
||
|
||
// 1) permission
|
||
const perm = await Notification.requestPermission();
|
||
if (perm !== 'granted') {
|
||
// on note qu'on a déjà proposé (évite spam)
|
||
try { localStorage.setItem(onboardKey, 'done'); } catch(_) {}
|
||
return false;
|
||
}
|
||
|
||
// 2) subscribe
|
||
const sub = await reg.pushManager.subscribe({
|
||
userVisibleOnly: true,
|
||
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC)
|
||
});
|
||
|
||
// 3) sync DB
|
||
await syncToServer(sub);
|
||
|
||
// 4) mark done
|
||
try { localStorage.setItem(onboardKey, 'done'); } catch(_) {}
|
||
return true;
|
||
}
|
||
|
||
// --- Step 1: si sub existe déjà => sync DB silencieux ---
|
||
try {
|
||
const existing = await getSub();
|
||
if (existing) {
|
||
await syncToServer(existing);
|
||
// on marque done pour ne pas reproposer
|
||
try { localStorage.setItem(onboardKey, 'done'); } catch(_) {}
|
||
return;
|
||
}
|
||
} catch(e) {
|
||
// silencieux (pas bloquant)
|
||
}
|
||
|
||
// --- Step 2: pas de sub => afficher modal une seule fois ---
|
||
let alreadyDone = false;
|
||
try { alreadyDone = localStorage.getItem(onboardKey) === 'done'; } catch(_) {}
|
||
|
||
// Si l'utilisateur a déjà refusé au niveau OS => pas de modal (ou tu peux afficher un toast)
|
||
if (Notification.permission === 'denied') {
|
||
try { localStorage.setItem(onboardKey, 'done'); } catch(_) {}
|
||
return;
|
||
}
|
||
|
||
// Si déjà proposé => stop
|
||
if (alreadyDone) return;
|
||
|
||
// Affiche modal (Bootstrap)
|
||
const modalEl = document.getElementById('pushOnboardModal');
|
||
if (!modalEl || typeof bootstrap === 'undefined') return;
|
||
|
||
const modal = new bootstrap.Modal(modalEl, { backdrop: 'static' });
|
||
|
||
const laterBtn = document.getElementById('pushLaterBtn');
|
||
const enableBtn = document.getElementById('pushEnableBtn');
|
||
|
||
if (laterBtn) {
|
||
laterBtn.addEventListener('click', () => {
|
||
try { localStorage.setItem(onboardKey, 'done'); } catch(_) {}
|
||
modal.hide();
|
||
}, { once:true });
|
||
}
|
||
|
||
if (enableBtn) {
|
||
enableBtn.addEventListener('click', async () => {
|
||
enableBtn.disabled = true;
|
||
try {
|
||
await subscribeNow();
|
||
} catch(e) {
|
||
// si échec (réseau / serveur), on ne spam pas l'utilisateur
|
||
try { localStorage.setItem(onboardKey, 'done'); } catch(_) {}
|
||
} finally {
|
||
modal.hide();
|
||
}
|
||
}, { once:true });
|
||
}
|
||
|
||
modal.show();
|
||
})();
|
||
</script>
|
||
<script>
|
||
window.applyBadges = function (data) {
|
||
const msgUnread = Number(data?.msg_unread || 0);
|
||
const rdvUnread = Number(data?.rdv_unread || 0);
|
||
const appBadge = Number(data?.app_badge || (msgUnread + rdvUnread));
|
||
|
||
const msgSpan = document.getElementById('msg-badge');
|
||
const rdvSpan = document.getElementById('rdv-badge');
|
||
|
||
if (msgSpan){
|
||
msgSpan.style.display = msgUnread > 0 ? 'inline-block' : 'none';
|
||
msgSpan.textContent = msgUnread > 99 ? '99+' : String(msgUnread);
|
||
}
|
||
if (rdvSpan){
|
||
rdvSpan.style.display = rdvUnread > 0 ? 'inline-block' : 'none';
|
||
rdvSpan.textContent = rdvUnread > 99 ? '99+' : String(rdvUnread);
|
||
}
|
||
|
||
if ('setAppBadge' in navigator) {
|
||
try {
|
||
if (appBadge > 0) navigator.setAppBadge(appBadge);
|
||
else navigator.clearAppBadge?.();
|
||
} catch(_) {}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
|
||
|
||
|