🔒

Accès refusé

Cette page demande la clé d'administration.

← Retour au site

['Nom affiché', $GLOBALS['APP_NAME'], 'text', 'Par défaut, le nom de la boutique.'], 'v_hero_tagline' => ['Phrase d\'accroche (sous le nom)', 'Une sélection soignée, commandée en deux minutes.', 'textarea', 'Une phrase courte, sous le titre principal.'], 'v_cta_label' => ['Texte du gros bouton', '🛍️ Commander maintenant', 'text', 'Le bouton le plus visible de la page, envoie vers le menu.'], 'v_cta_sub' => ['Petit texte sous le bouton', 'Le menu complet, les prix, les quantités', 'text', ''], 'v_hook_badge' => ['Bloc mini-jeux — badge', '🎮 Mini-jeux du groupe', 'text', ''], 'v_hook_title' => ['Bloc mini-jeux — titre', '50 € offerts chaque semaine', 'text', ''], 'v_hook_text' => ['Bloc mini-jeux — texte', "Rejoins notre groupe de discussion Telegram : mini-jeux, tirages au sort et bons plans y sont annoncés chaque semaine.", 'textarea', 'Explique clairement où et comment participer.'], 'v_hook_btn' => ['Bloc mini-jeux — bouton', 'Rejoindre le groupe', 'text', 'Le lien utilisé est celui du "Groupe de discussion" (TG_GROUPE_URL), déjà réglé dans les réseaux de la mini-app.'], 'v_step1_title' => ['Étape 1 — titre', 'Parcours le menu', 'text', ''], 'v_step1_text' => ['Étape 1 — texte', 'Catégories, photos, prix : tout est présenté dans la mini-app, sans créer de compte.', 'textarea', ''], 'v_step2_title' => ['Étape 2 — titre', 'Choisis tes quantités', 'text', ''], 'v_step2_text' => ['Étape 2 — texte', 'Ajoute au panier, ajuste les quantités, le total se calcule tout seul.', 'textarea', ''], 'v_step3_title' => ['Étape 3 — titre', 'Valide en deux clics', 'text', ''], 'v_step3_text' => ['Étape 3 — texte', 'Un message est déjà prêt : envoie-le sur Telegram, WhatsApp ou Signal pour confirmer.', 'textarea', ''], 'v_footer_note' => ['Mention en pied de page', '', 'textarea', 'Optionnel : mention légale, âge minimum, etc. Laisse vide pour ne rien afficher.'], ]; } $msg = ''; $CAT_PHASE = 'actions'; require __DIR__ . '/admin_catalogue.php'; if (isset($_POST['save_site'])) { $n = 0; foreach (v_fields() as $fkey => $f) { if (!array_key_exists($fkey, $_POST)) continue; setting_set($mysqli, $fkey, trim((string)$_POST[$fkey])); $n++; } setting_set($mysqli, 'v_show_new', isset($_POST['v_show_new']) ? '1' : '0'); $msg = "💾 Réglages enregistrés ($n champs). Recharge le site pour voir le résultat."; } /* Fond du haut de page : deux images possibles (mobile / ordinateur), pour composer différemment selon l'écran — une photo cadrée serré pour le mobile peut laisser un grand vide sur un écran large, et inversement. */ $heroTargets = ['mobile' => 'v_hero_bg', 'desktop' => 'v_hero_bg_desktop']; if (isset($_POST['upload_hero'])) { $target = (string)($_POST['hero_target'] ?? 'mobile'); $settingKey = $heroTargets[$target] ?? 'v_hero_bg'; if (!empty($_FILES['hero_bg_file']['name']) && is_uploaded_file($_FILES['hero_bg_file']['tmp_name'])) { $allowed = ['jpg','jpeg','png','webp']; $ext = strtolower(pathinfo((string)$_FILES['hero_bg_file']['name'], PATHINFO_EXTENSION)); if (in_array($ext, $allowed, true) && (int)$_FILES['hero_bg_file']['size'] <= 8 * 1024 * 1024) { $uploadDir = MEDIA_ROOT . '/uploads'; if (!is_dir($uploadDir)) { @mkdir($uploadDir, 0775, true); } $file = 'hero_' . uniqid('', true) . '.' . $ext; if (@move_uploaded_file($_FILES['hero_bg_file']['tmp_name'], $uploadDir . '/' . $file)) { setting_set($mysqli, $settingKey, $file); $msg = $target === 'desktop' ? '🖼️ Image de fond (ordinateur) mise à jour.' : '🖼️ Image de fond (mobile) mise à jour.'; } else { $msg = "⚠️ L'envoi du fichier a échoué."; } } else { $msg = '⚠️ Fichier refusé : formats acceptés jpg, png, webp (8 Mo max).'; } } } if (isset($_POST['remove_hero'])) { $target = (string)($_POST['hero_target'] ?? 'mobile'); $settingKey = $heroTargets[$target] ?? 'v_hero_bg'; $old = setting_get($mysqli, $settingKey, ''); if ($old !== '' && is_file(MEDIA_ROOT . '/uploads/' . $old)) { @unlink(MEDIA_ROOT . '/uploads/' . $old); } setting_set($mysqli, $settingKey, ''); $msg = '🗑️ Image de fond (' . ($target === 'desktop' ? 'ordinateur' : 'mobile') . ') retirée.'; } if (isset($_POST['save_hero_opacity'])) { $opTarget = (string)($_POST['opacity_target'] ?? 'mobile'); $opKey = $opTarget === 'desktop' ? 'v_hero_bg_opacity_desktop' : 'v_hero_bg_opacity'; $opacity = (int)($_POST['opacity_value'] ?? 55); if ($opacity < 0) { $opacity = 0; } if ($opacity > 100) { $opacity = 100; } setting_set($mysqli, $opKey, (string)$opacity); $msg = '🎚️ Opacité (' . ($opTarget === 'desktop' ? 'ordinateur' : 'mobile') . ") réglée à {$opacity}%."; } /* ---------- Logo (fichier partagé avec la mini-app : MEDIA_ROOT/logo.png) ---------- */ if (isset($_POST['save_logo'])) { if (!empty($_FILES['logo']['tmp_name']) && is_uploaded_file($_FILES['logo']['tmp_name'])) { if (@getimagesize($_FILES['logo']['tmp_name'])) { if (@move_uploaded_file($_FILES['logo']['tmp_name'], MEDIA_ROOT . '/logo.png')) { foreach (glob(MEDIA_ROOT . '/cache_thumbs/*') ?: [] as $f) { if (is_file($f)) @unlink($f); } $msg = '🖼️ Logo mis à jour (visible sur le site et la mini-app).'; } else { $msg = "⚠️ Impossible d'écrire le fichier logo.png (droits du dossier ?)."; } } else { $msg = "⚠️ Le fichier envoyé n'est pas une image valide (JPG / PNG / WebP)."; } } else { $msg = '⚠️ Aucun fichier reçu.'; } } if (isset($_POST['save_logo_format'])) { $format = (string)($_POST['logo_format'] ?? '16:9'); if (!in_array($format, ['16:9', 'square'], true)) { $format = '16:9'; } setting_set($mysqli, 'logo_format', $format); $msg = '🖼️ Format du logo enregistré (mini-app).'; } $logoFormat = setting_get($mysqli, 'logo_format', '16:9'); if (!in_array($logoFormat, ['16:9', 'square'], true)) { $logoFormat = '16:9'; } $logoV = is_file(MEDIA_ROOT . '/logo.png') ? (string)filemtime(MEDIA_ROOT . '/logo.png') : (string)time(); /* ---------- Police du texte : preset Google Fonts, ou fichier perso ---------- */ if (isset($_POST['save_font_preset'])) { $presets = site_font_presets(); $key = (string)($_POST['v_font_preset'] ?? 'fraunces'); if (!isset($presets[$key])) { $key = 'fraunces'; } setting_set($mysqli, 'v_font_preset', $key); $msg = '🔤 Police enregistrée : ' . $presets[$key]['label'] . '.'; } if (isset($_POST['upload_font'])) { if (!empty($_FILES['font_file']['name']) && is_uploaded_file($_FILES['font_file']['tmp_name'])) { $ext = strtolower(pathinfo((string)$_FILES['font_file']['name'], PATHINFO_EXTENSION)); if (in_array($ext, SITE_FONT_UPLOAD_EXT, true) && (int)$_FILES['font_file']['size'] <= 5 * 1024 * 1024) { $fontDir = __DIR__ . '/assets/fonts'; if (!is_dir($fontDir)) { @mkdir($fontDir, 0775, true); } // Une ancienne police perso reste sur le disque tant qu'on ne la supprime pas explicitement. $old = setting_get($mysqli, 'v_font_custom', ''); $file = 'custom_' . uniqid('', true) . '.' . $ext; if (@move_uploaded_file($_FILES['font_file']['tmp_name'], $fontDir . '/' . $file)) { if ($old !== '' && is_file($fontDir . '/' . $old)) { @unlink($fontDir . '/' . $old); } setting_set($mysqli, 'v_font_custom', $file); $customName = trim((string)($_POST['font_name'] ?? '')); setting_set($mysqli, 'v_font_custom_name', $customName); $msg = '🔤 Police personnalisée envoyée — utilisée partout sur le site (télécharge-la où tu veux, y compris sur dafont.com).'; } else { $msg = "⚠️ L'envoi de la police a échoué."; } } else { $msg = '⚠️ Fichier refusé : formats acceptés .ttf, .otf, .woff, .woff2 (5 Mo max).'; } } else { $msg = '⚠️ Aucun fichier reçu.'; } } if (isset($_POST['remove_font'])) { $old = setting_get($mysqli, 'v_font_custom', ''); if ($old !== '' && is_file(__DIR__ . '/assets/fonts/' . $old)) { @unlink(__DIR__ . '/assets/fonts/' . $old); } setting_set($mysqli, 'v_font_custom', ''); setting_set($mysqli, 'v_font_custom_name', ''); $msg = '↩️ Police personnalisée retirée — retour au duo Google Fonts choisi ci-dessus.'; } $currentFontPreset = setting_get($mysqli, 'v_font_preset', 'fraunces'); $currentFontCustom = setting_get($mysqli, 'v_font_custom', ''); $currentFontCustomName = setting_get($mysqli, 'v_font_custom_name', ''); if (isset($_POST['reset_site'])) { foreach (v_fields() as $fkey => $f) { setting_set($mysqli, $fkey, ''); } setting_set($mysqli, 'v_show_new', '1'); $msg = '↩️ Réglages du site remis à leurs valeurs par défaut.'; } $currentHeroBgMobile = setting_get($mysqli, 'v_hero_bg', ''); $currentHeroBgDesktop = setting_get($mysqli, 'v_hero_bg_desktop', ''); $currentHeroOpacityMobile = (int)setting_get($mysqli, 'v_hero_bg_opacity', '55'); if ($currentHeroOpacityMobile < 0 || $currentHeroOpacityMobile > 100) { $currentHeroOpacityMobile = 55; } $currentHeroOpacityDesktop = (int)setting_get($mysqli, 'v_hero_bg_opacity_desktop', '55'); if ($currentHeroOpacityDesktop < 0 || $currentHeroOpacityDesktop > 100) { $currentHeroOpacityDesktop = 55; } $showNewChecked = setting_get($mysqli, 'v_show_new', '1') !== '0'; /* Post/Redirect/Get : évite qu'un rechargement rejoue l'action précédente. */ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !headers_sent()) { if ($msg !== '') { setcookie('sa_flash', rawurlencode($msg), ['expires' => time() + 60, 'path' => '/', 'samesite' => 'Lax']); } $back = (string)($_SERVER['REQUEST_URI'] ?? 'admin.php'); if ($back === '' || $back[0] !== '/' || str_starts_with($back, '//')) { $back = 'admin.php?key=' . urlencode($ADMIN_SECRET); } header('Location: ' . $back, true, 303); exit; } if (!empty($_COOKIE['sa_flash'])) { $msg = rawurldecode((string)$_COOKIE['sa_flash']); if (!headers_sent()) { setcookie('sa_flash', '', ['expires' => time() - 3600, 'path' => '/', 'samesite' => 'Lax']); } } $fields = v_fields(); ?> Admin du site — <?= h($APP_NAME) ?>
Admin du site
👁️

Contenu du site vitrine

Ces réglages pilotent la page d'accueil, le panier et les fiches produit du site — la mini-app de commande (autre projet) n'est pas concernée.

📐

Format d'affichage sur l'accueil de la mini-app

Le site affiche toujours le logo en médaillon rond ; ce réglage ne concerne que la grande bannière du catalogue (mini-app).

🔤

Police du texte

Choisis un duo prêt à l'emploi, ou envoie n'importe quelle police à toi — y compris une police téléchargée sur dafont.com : dafont ne propose pas de lien direct utilisable sur un site, donc télécharge le fichier depuis dafont puis envoie-le ici (.ttf, .otf, .woff ou .woff2).

$pf): $checked = $currentFontCustom === '' && $currentFontPreset === $pk; ?>
Une police personnalisée est active en ce moment (ci-dessous) — elle prend le dessus tant que tu ne la retires pas.
✍️ Police personnalisée (dafont ou autre)
Police active : — Aperçu : Waking Dream 69
🌅

Image de fond du haut de page

Optionnelles : sans image, la page utilise un fond en dégradé (plus léger, s'affiche instantanément). Tu peux envoyer une photo différente pour mobile et pour ordinateur — une image cadrée serré pour le mobile laisse souvent un grand vide sur un écran large.

📱 Mobile
Aucune image
— fond dégradé —
0 % = fond dégradé seul, 100 % = photo pleinement visible. Réglage propre au mobile.
🖥️ Ordinateur
Aucune image
— reprend celle du mobile —
0 % = fond dégradé seul, 100 % = photo pleinement visible. Réglage propre à l'ordinateur.
✨

En-tête & bouton principal

Ce que le client voit en premier en arrivant sur le site.

🎮

Bloc mini-jeux (groupe Telegram)

Ce bloc n'apparaît que si un lien de groupe est configuré (onglet Réseaux de l'admin mini-app).

🧭

Les 3 étapes « Comment commander »