From b7ccfce43e6d7cbf3b58d3b6619114ffdd1819b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Wed, 8 Oct 2025 17:00:40 +0400 Subject: [PATCH] feat: add next live announcement with multi-timezone display --- .gitignore | 4 + README.adoc | 66 ++++++ css/styles.css | 421 +++++++++++++++++++++++++++++++++++- direct.php | 120 +++++++++- includes/config.default.php | 20 ++ index.php | 113 +++++++++- uploads/.gitkeep | 0 7 files changed, 715 insertions(+), 29 deletions(-) create mode 100644 uploads/.gitkeep diff --git a/.gitignore b/.gitignore index b7fce03..2a408f3 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,10 @@ temp/ # Fichiers de cache cache/ +# Dossier pour les images d'annonces (tout ignorer sauf .gitkeep) +uploads/* +!uploads/.gitkeep + # Fichiers de dépendances (si nécessaire) # vendor/ # node_modules/ diff --git a/README.adoc b/README.adoc index 17bce71..d825218 100644 --- a/README.adoc +++ b/README.adoc @@ -26,6 +26,7 @@ kaubuntu.re est une interface web responsive qui permet de consulter et recherch - 📰 *Intégration WordPress* : Affichage des articles depuis un site WordPress via REST API - 💝 *Système de dons* : Interface PayPal Me configurable pour collecter des dons - ⏰ *Système de countdown* : Page de lancement configurable avec compte à rebours multi-fuseaux +- 📺 *Annonce du prochain live* : Affichage dynamique avec multi-fuseaux horaires et image personnalisable == 🛠️ Technologies utilisées @@ -157,6 +158,71 @@ cp mentions-legales.php.sample mentions-legales.php Ces fichiers sont listés dans le `.gitignore` afin que vos modifications ne soient pas suivies par Git, ce qui vous permet de personnaliser votre instance sans affecter le code source principal. +== 📺 Annonce du prochain live + +kaubuntu.re intègre un système d'annonce configurable qui s'affiche automatiquement lorsqu'il n'y a pas de diffusion en direct en cours. + +=== ✨ Fonctionnalités + +- 📅 *Date et heure dynamiques* : Génération automatique à partir de `NEXT_LIVE_DATE` +- 🌍 *Multi-fuseaux horaires* : Affichage automatique pour 5 territoires (Ma'ohi Nui, Martinique/Guadeloupe, Guyane, France, Kanaky) +- ⏰ *Décalage UTC* : Affichage du fuseau horaire de référence (UTC+04:00 pour La Réunion) +- 📅 *Indicateurs de jour* : Affichage des décalages de jour (+1j/-1j) si nécessaire +- 🖼️ *Image personnalisable* : Support des formats Instagram (portrait 4:5, carré 1:1) et paysage (16:9) +- 📱 *Responsive complet* : Layouts adaptés pour desktop (50/50), tablette (vertical), et mobile +- 🔄 *Flexbox intelligent* : Réorganisation automatique des fuseaux horaires selon la largeur d'écran + +=== ⚙️ Configuration + +Pour configurer l'annonce du prochain live, ajoutez dans votre `config.local.php` : + +[source,php] +---- +// Activer l'annonce du prochain live +define('NEXT_LIVE_ENABLED', true); + +// Titre (la date sera ajoutée automatiquement) +define('NEXT_LIVE_TITLE', 'Prochain live'); + +// Description (l'heure sera ajoutée automatiquement) +define('NEXT_LIVE_DESCRIPTION', 'Constitution du futur état réunionnais & Hommage à Thomas Sankara.'); + +// Date du prochain live au format Y-m-d H:i:s +define('NEXT_LIVE_DATE', '2025-10-11 10:00:00'); + +// Chemin vers l'image d'annonce (optionnel) +define('NEXT_LIVE_IMAGE', 'uploads/next-live.jpg'); +---- + +=== 📁 Gestion des images + +. Placez vos images dans le dossier `uploads/` (non tracké par Git) +. Formats recommandés : + * Portrait 4:5 : 1080×1350px ou 1280×1600px - *Idéal* + * Carré 1:1 : 1080×1080px - *Parfait* + * Paysage 16:9 : 1920×1080px +. Optimisez vos images (< 500 Ko recommandé) + +=== 🎯 Affichage + +*Desktop (≥1700px)* : Layout 50/50 (image à gauche, informations à droite) + +*Tablette (1025-1699px)* : Hero seul sur une ligne, Mastodon et WordPress côte à côte en dessous + +*Mobile (<769px)* : Stack vertical avec scroll si nécessaire + +=== 🌍 Fuseaux horaires affichés + +L'annonce calcule automatiquement les heures locales pour : + +- *Ma'ohi Nui* (Polynésie française) - UTC-10:00 +- *Martinique / Guadeloupe* - UTC-04:00 +- *Guyane* - UTC-03:00 +- *France* - UTC+02:00 +- *Kanaky* (Nouvelle-Calédonie) - UTC+11:00 + +L'heure de référence (La Réunion, UTC+04:00) est affichée dans le badge principal. + == 💝 Système de dons kaubuntu.re intègre un système de dons configurable qui permet de collecter des contributions via PayPal Me. diff --git a/css/styles.css b/css/styles.css index 4506b7e..9ba754d 100644 --- a/css/styles.css +++ b/css/styles.css @@ -584,21 +584,32 @@ img { align-items: start; } -/* Responsive breakpoint intermédiaire pour les 3 éléments */ -@media (max-width: 1200px) and (min-width: 1025px) { +/* Responsive breakpoint pour écrans entre 1025px et 1699px */ +@media (max-width: 1699px) and (min-width: 1025px) { .hero-mastodon-wrapper { - grid-template-columns: 1fr; - grid-template-rows: auto auto; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20px; } - + + .hero { + grid-column: 1 / -1; + width: 100%; + } + .timeline-wordpress-container { grid-column: 1 / -1; - grid-template-columns: 1fr; - gap: 15px; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; } - - .timeline-wordpress-container .wordpress-section { - width: 100%; + + #mt-container { + grid-column: 1 / 2; + } + + .wordpress-section { + grid-column: 2 / 3; } } @@ -729,6 +740,116 @@ img { opacity: 0.9; } +/* Annonce du prochain live dans le hero (page d'accueil) - Split gauche/droite */ +.hero-next-live { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + flex-direction: row; + overflow: hidden; +} + +.hero-next-live-image-container { + flex: 0 0 50%; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--bg-dark); + padding: 15px; +} + +.hero-next-live-image { + width: 100%; + height: 100%; + object-fit: contain; + object-position: center; +} + +.hero-next-live-content { + flex: 0 0 50%; + padding: 20px 15px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0.95) 100%); + color: white; +} + +.hero-next-live-content i.fa-calendar-alt { + font-size: 35px; + margin-bottom: 8px; + color: var(--primary-red); +} + +.hero-next-live-content h2 { + font-size: 20px; + font-weight: 700; + margin-bottom: 8px; + color: white; + line-height: 1.2; +} + +.hero-next-live-content p { + font-size: 14px; + margin: 0 auto 12px; + color: rgba(255, 255, 255, 0.95); + line-height: 1.3; +} + +.hero-next-live-date { + font-size: 15px !important; + font-weight: 600; + color: white !important; + background: var(--primary-red); + padding: 7px 14px; + border-radius: 8px; + display: inline-block; + margin-bottom: 12px !important; + box-shadow: 0 4px 12px rgba(255, 0, 0, 0.3); +} + +.hero-next-live-date i { + margin-right: 8px; +} + +.hero-next-live-datetime { + width: 100%; +} + +.hero-next-live-timezones { + display: flex; + flex-wrap: wrap; + gap: 8px; + justify-content: center; + margin-top: 12px; + padding: 12px 16px; + background-color: rgba(255, 255, 255, 0.15); + border-radius: 8px; +} + +.hero-timezone-item { + font-size: 13px; + color: rgba(255, 255, 255, 0.95); + white-space: nowrap; + line-height: 1.5; + background-color: rgba(0, 0, 0, 0.3); + padding: 5px 10px; + border-radius: 5px; + border: 1px solid rgba(255, 255, 255, 0.15); + flex: 0 1 auto; + min-width: fit-content; +} + +.hero-timezone-item strong { + color: white; + font-weight: 700; +} + #mt-container { width: 100%; height: 400px; @@ -1982,6 +2103,16 @@ img { width: 100%; height: 300px; } + + /* Augmenter la hauteur du hero quand il y a une annonce */ + .hero:has(.hero-next-live) { + height: 600px; + } + + /* Augmenter aussi la hauteur du hero pour "no-live" pour cohérence */ + .hero:has(.hero-no-live) { + height: 300px; + } .hero-mastodon-wrapper #mt-container { height: 600px; @@ -1997,7 +2128,7 @@ img { font-size: 12px !important; padding: 5px 10px !important; } - + .hero-logo { font-size: 40px; } @@ -2342,6 +2473,129 @@ i.icon-mastodon, margin: 0 auto 30px; } +/* Annonce du prochain live - Split gauche/droite */ +.next-live-announcement { + display: flex; + flex-direction: row; + border-radius: 8px; + overflow: hidden; + background-color: var(--card-bg); + min-height: 500px; +} + +.next-live-image-container { + flex: 0 0 50%; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--bg-dark); + padding: 20px; +} + +.next-live-image { + width: 100%; + height: 100%; + object-fit: contain; + object-position: center; + max-height: 600px; +} + +.next-live-content { + flex: 0 0 50%; + padding: 50px 40px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + background: linear-gradient(135deg, var(--card-bg) 0%, var(--bg-dark) 100%); +} + +.next-live-content i.fa-calendar-alt { + font-size: 60px; + margin-bottom: 20px; + color: var(--primary-red); +} + +.next-live-content h2 { + font-size: 32px; + font-weight: 700; + margin-bottom: 20px; + color: var(--text-color); + line-height: 1.3; +} + +.next-live-content p { + font-size: 20px; + margin: 0 auto 25px; + color: var(--text-muted); + line-height: 1.5; + max-width: 100%; +} + +.next-live-date { + font-size: 22px !important; + font-weight: 600; + color: white !important; + background: var(--primary-red); + padding: 12px 24px; + border-radius: 8px; + display: inline-block; + margin-bottom: 30px !important; + box-shadow: 0 4px 12px rgba(255, 0, 0, 0.3); +} + +.next-live-date i { + margin-right: 8px; +} + +.utc-offset { + font-size: 16px; + opacity: 0.85; + font-weight: 500; +} + +.next-live-datetime { + width: 100%; +} + +.next-live-timezones { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; + margin-top: 15px; + margin-bottom: 20px; + padding: 16px 20px; + background-color: rgba(0, 0, 0, 0.4); + border-radius: 8px; +} + +.timezone-item { + font-size: 16px; + color: rgba(255, 255, 255, 0.95); + white-space: nowrap; + line-height: 1.5; + background-color: rgba(0, 0, 0, 0.3); + padding: 6px 12px; + border-radius: 6px; + border: 1px solid rgba(255, 255, 255, 0.1); + flex: 0 1 auto; + min-width: fit-content; +} + +.timezone-item strong { + color: white; + font-weight: 700; +} + +.day-shift { + font-size: 12px; + color: var(--primary-red); + font-weight: 700; + margin-left: 4px; +} + .btn-primary { display: inline-block; padding: 12px 25px; @@ -2361,6 +2615,32 @@ i.icon-mastodon, box-shadow: 0 4px 8px rgba(0,0,0,0.1); } +/* Responsive tablette/desktop étroit - 769px à 1024px */ +@media (max-width: 1024px) and (min-width: 769px) { + .hero-mastodon-wrapper { + display: block; + } + + .hero { + width: 100%; + margin-bottom: 20px; + } + + .timeline-wordpress-container { + display: flex; + flex-direction: row; + gap: 20px; + } + + #mt-container { + flex: 1; + } + + .wordpress-section { + flex: 1; + } +} + /* Responsive pour la page de direct */ @media (max-width: 768px) { .live-title { @@ -2399,6 +2679,125 @@ i.icon-mastodon, .no-live-message p { font-size: 16px; } + + /* Responsive mobile - Stack vertical pour l'annonce */ + .next-live-announcement { + flex-direction: column; + min-height: auto; + } + + .next-live-image-container { + flex: 0 0 auto; + width: 100%; + max-height: 400px; + padding: 15px; + } + + .next-live-image { + max-height: 350px; + } + + .next-live-content { + flex: 0 0 auto; + width: 100%; + padding: 30px 20px; + overflow-y: auto; + max-height: 600px; + } + + .next-live-content i.fa-calendar-alt { + font-size: 45px; + } + + .next-live-content h2 { + font-size: 24px; + } + + .next-live-content p { + font-size: 16px; + } + + .next-live-date { + font-size: 17px !important; + padding: 10px 18px; + } + + .next-live-timezones { + gap: 8px; + padding: 14px 16px; + margin-top: 12px; + margin-bottom: 16px; + } + + .timezone-item { + font-size: 14px; + padding: 5px 10px; + } + + /* Hero next live responsive */ + .hero-next-live { + flex-direction: column; + } + + .hero-next-live-image-container { + flex: 0 0 300px; + width: 100%; + padding: 15px; + max-height: 300px; + } + + .hero-next-live-image { + max-height: 270px; + } + + .hero-next-live-content { + flex: 1; + width: 100%; + padding: 5px 15px 25px 15px; + overflow-y: auto; + display: flex; + flex-direction: column; + justify-content: flex-start; + } + + .hero-next-live-content i.fa-calendar-alt { + font-size: 35px; + margin-bottom: 10px; + margin-top: 0; + } + + .hero-next-live-content h2 { + font-size: 20px; + margin-bottom: 8px; + line-height: 1.2; + } + + .hero-next-live-content p { + font-size: 13px; + margin-bottom: 10px; + line-height: 1.4; + } + + .hero-next-live-date { + font-size: 13px !important; + padding: 6px 12px; + margin-bottom: 10px !important; + } + + .hero-next-live-timezones { + gap: 5px; + padding: 8px 10px; + margin-top: 8px; + } + + .hero-timezone-item { + font-size: 10px; + padding: 3px 6px; + } + + .utc-offset { + font-size: 11px; + } } /* Catégories */ diff --git a/direct.php b/direct.php index d094da8..42ee2be 100644 --- a/direct.php +++ b/direct.php @@ -150,17 +150,117 @@ $liveStream = getLiveStream(); - -
- -

Aucun direct en cours

-

Revenez plus tard pour découvrir nos prochaines diffusions en direct.

- Retour à l'accueil -
+
+ +
+ <?php echo htmlspecialchars(NEXT_LIVE_TITLE); ?> +
+ +
+ + format($liveDate); + $formattedDay = ucfirst($formattedDay); + $dynamicTitle = NEXT_LIVE_TITLE . ' - ' . $formattedDay; + } else { + $dynamicTitle = NEXT_LIVE_TITLE; + } + ?> +

+ format('H\hi'); + $dynamicDescription = 'Rejoignez-nous à ' . $liveHour . '. ' . NEXT_LIVE_DESCRIPTION; + } else { + $dynamicDescription = NEXT_LIVE_DESCRIPTION; + } + ?> +

+ +
+

+ + format($liveDate); + + $offset = $liveDate->format('P'); + echo ' (UTC' . $offset . ')'; + ?> +

+ + +
+ 'Pacific/Tahiti', + 'Martinique / Guadeloupe' => 'America/Martinique', + 'Guyane' => 'America/Cayenne', + 'France' => 'Europe/Paris', + 'Kanaky' => 'Pacific/Noumea' + ]; + + foreach($timezones as $name => $timezone): + $liveDateLocal = clone $liveDate; + $liveDateLocal->setTimezone(new DateTimeZone($timezone)); + + // Vérifier si c'est un jour différent + $dayDiff = $liveDateLocal->format('j') - $liveDate->format('j'); + + $dayIndicator = ''; + if ($dayDiff > 0) { + $dayIndicator = ' +1j'; + } elseif ($dayDiff < 0) { + $dayIndicator = ' -1j'; + } + ?> + + : format('H\hi'); ?> + + +
+
+ + Retour à l'accueil +
+
+ +
+ +

Aucun direct en cours

+

Revenez plus tard pour découvrir nos prochaines diffusions en direct.

+ Retour à l'accueil +
+ diff --git a/includes/config.default.php b/includes/config.default.php index 8ffd862..e1bb0e4 100644 --- a/includes/config.default.php +++ b/includes/config.default.php @@ -166,4 +166,24 @@ if (!defined('DONATION_AMOUNTS')) define('DONATION_AMOUNTS', [5, 10, 20, 50]); // Devise par défaut if (!defined('DONATION_CURRENCY')) define('DONATION_CURRENCY', 'EUR'); + +// ========================================= +// Annonce du prochain live +// ========================================= + +// Activer/désactiver l'annonce du prochain live par défaut +if (!defined('NEXT_LIVE_ENABLED')) define('NEXT_LIVE_ENABLED', false); + +// Titre de l'annonce du prochain live +if (!defined('NEXT_LIVE_TITLE')) define('NEXT_LIVE_TITLE', 'Prochain live'); + +// Description de l'annonce du prochain live +if (!defined('NEXT_LIVE_DESCRIPTION')) define('NEXT_LIVE_DESCRIPTION', 'Rejoignez-nous pour notre prochain live !'); + +// Date du prochain live (format: Y-m-d H:i:s) +if (!defined('NEXT_LIVE_DATE')) define('NEXT_LIVE_DATE', ''); + +// Chemin vers l'image d'annonce du prochain live (relatif à la racine du site) +// Exemple: 'uploads/next-live.jpg' +if (!defined('NEXT_LIVE_IMAGE')) define('NEXT_LIVE_IMAGE', 'uploads/next-live.jpg'); ?> diff --git a/index.php b/index.php index b7af55e..5bc7d8e 100644 --- a/index.php +++ b/index.php @@ -142,14 +142,111 @@ setSecurityHeaders(); -
- -

Aucun direct en cours

-

Revenez plus tard pour découvrir nos prochaines diffusions en direct.

-
- +
+ +
+ <?php echo htmlspecialchars(NEXT_LIVE_TITLE); ?> +
+ +
+ + format($liveDate); + $formattedDay = ucfirst($formattedDay); + $dynamicTitle = NEXT_LIVE_TITLE . ' - ' . $formattedDay; + } else { + $dynamicTitle = NEXT_LIVE_TITLE; + } + ?> +

+ format('H\hi'); + $dynamicDescription = 'Rejoignez-nous à ' . $liveHour . '. ' . NEXT_LIVE_DESCRIPTION; + } else { + $dynamicDescription = NEXT_LIVE_DESCRIPTION; + } + ?> +

+ +
+

+ + format($liveDate); + + $offset = $liveDate->format('P'); + echo ' (UTC' . $offset . ')'; + ?> +

+ + +
+ 'Pacific/Tahiti', + 'Martinique / Guadeloupe' => 'America/Martinique', + 'Guyane' => 'America/Cayenne', + 'France' => 'Europe/Paris', + 'Kanaky' => 'Pacific/Noumea' + ]; + + foreach($timezones as $name => $timezone): + $liveDateLocal = clone $liveDate; + $liveDateLocal->setTimezone(new DateTimeZone($timezone)); + $dayDiff = $liveDateLocal->format('j') - $liveDate->format('j'); + + $dayIndicator = ''; + if ($dayDiff > 0) { + $dayIndicator = ' +1j'; + } elseif ($dayDiff < 0) { + $dayIndicator = ' -1j'; + } + ?> + + : format('H\hi'); ?> + + +
+
+ +
+
+ +
+ +

Aucun direct en cours

+

Revenez plus tard pour découvrir nos prochaines diffusions en direct.

+
+ diff --git a/uploads/.gitkeep b/uploads/.gitkeep new file mode 100644 index 0000000..e69de29