diff --git a/.gitignore b/.gitignore index b893a85..77a9516 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ data/utilisateurs.json data/consoles.json data/jeux.json data/jaquettes/* +data/consoles-photos/* data/cache/* !data/jaquettes/.gitkeep +!data/consoles-photos/.gitkeep !data/cache/.gitkeep diff --git a/Dockerfile b/Dockerfile index 2dfc14b..b560f65 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ COPY public /var/www/html/public COPY src /var/www/html/src COPY templates /var/www/html/templates -RUN mkdir -p /var/www/html/data/jaquettes /var/www/html/data/cache \ +RUN mkdir -p /var/www/html/data/jaquettes /var/www/html/data/consoles-photos /var/www/html/data/cache \ && chown -R www-data:www-data /var/www/html/data ENV APACHE_DOCUMENT_ROOT=/var/www/html/public diff --git a/data/consoles-photos/.gitkeep b/data/consoles-photos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/.htaccess b/public/.htaccess index caf5edf..dc4a0cc 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -1,7 +1,8 @@ RewriteEngine On -# Servir les jaquettes stockées hors de public/ -RewriteRule ^jaquettes/(.*)$ /servir-jaquette.php?fichier=$1 [L,QSA] +# Médias stockés hors de public/ +RewriteRule ^jaquettes/(.*)$ /servir-media.php?type=jaquettes&fichier=$1 [L,QSA] +RewriteRule ^consoles-photos/(.*)$ /servir-media.php?type=consoles-photos&fichier=$1 [L,QSA] # Fichiers / dossiers existants : ne pas réécrire RewriteCond %{REQUEST_FILENAME} !-f diff --git a/public/assets/css/style.css b/public/assets/css/style.css index d817298..d041e17 100644 --- a/public/assets/css/style.css +++ b/public/assets/css/style.css @@ -378,6 +378,79 @@ a:hover border: 1px solid transparent; } +.game-card-badges +{ + display: flex; + flex-wrap: wrap; + gap: 0.35rem; +} + +.badge-format +{ + display: inline-flex; + align-items: center; + padding: 0.15rem 0.55rem; + border-radius: 999px; + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.03em; + border: 1px solid var(--line); + color: var(--text); + background: rgba(0, 229, 255, 0.08); +} + +.format-physique +{ + border-color: rgba(124, 255, 107, 0.45); + color: var(--accent-hot); +} + +.format-dematerialise +{ + border-color: rgba(0, 229, 255, 0.45); + color: var(--accent); +} + +.format-rom +{ + border-color: rgba(255, 200, 87, 0.45); + color: var(--warning); +} + +.format-checks +{ + display: flex; + flex-wrap: wrap; + gap: 0.75rem; +} + +.format-check +{ + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.55rem 0.85rem; + border: 1px solid var(--line); + border-radius: 999px; + background: rgba(4, 12, 22, 0.55); + cursor: pointer; + font-weight: 600; + user-select: none; +} + +.format-check:has(input:checked) +{ + border-color: rgba(0, 229, 255, 0.55); + box-shadow: 0 0 0 1px rgba(0, 229, 255, 0.2); +} + +.format-check input +{ + accent-color: var(--accent); + width: 1.05rem; + height: 1.05rem; +} + .etat-possede { color: #041018; @@ -441,6 +514,77 @@ a:hover margin-bottom: 0.7rem; } +.console-photo +{ + width: 100%; + aspect-ratio: 16 / 10; + border-radius: 10px; + overflow: hidden; + border: 1px solid var(--line); + margin-bottom: 0.85rem; + background: var(--bg-elevated); +} + +.console-photo img, +.console-photo-preview img, +.console-cover img +{ + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.console-photo-preview, +.console-cover +{ + border-radius: var(--radius); + overflow: hidden; + border: 1px solid var(--line); + box-shadow: var(--shadow); + background: var(--bg-elevated); + aspect-ratio: 16 / 10; +} + +.console-detail-hero +{ + grid-template-columns: minmax(220px, 340px) 1fr; +} + +.photo-upload-bar +{ + padding: 1rem; + border: 1px dashed var(--line); + border-radius: var(--radius); + background: rgba(4, 12, 22, 0.45); +} + +.photo-upload-bar .form-label +{ + display: inline-flex; + align-items: center; + gap: 0.4rem; + color: var(--accent); +} + +.photo-upload-row +{ + display: flex; + gap: 0.65rem; + flex-wrap: wrap; + align-items: center; +} + +.photo-upload-row .form-control +{ + flex: 1 1 220px; +} + +.photo-upload-row .btn +{ + flex: 0 0 auto; +} + .console-card h2 { font-size: 1.25rem; @@ -760,15 +904,18 @@ a:hover { .auth-shell, .detail-hero, - .detail-grid + .detail-grid, + .console-detail-hero { grid-template-columns: 1fr; } .detail-cover, - .cover-preview + .cover-preview, + .console-cover, + .console-photo-preview { - max-width: 220px; + max-width: 340px; } } diff --git a/public/assets/img/console-manquante.svg b/public/assets/img/console-manquante.svg new file mode 100644 index 0000000..69c02eb --- /dev/null +++ b/public/assets/img/console-manquante.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + Pas de photo + diff --git a/public/servir-jaquette.php b/public/servir-media.php similarity index 68% rename from public/servir-jaquette.php rename to public/servir-media.php index bee21fc..8fbaf44 100644 --- a/public/servir-jaquette.php +++ b/public/servir-media.php @@ -6,15 +6,21 @@ require_once dirname(__DIR__) . "/src/Config.php"; Config::charger(); +$type = $_GET["type"] ?? "jaquettes"; $fichier = basename($_GET["fichier"] ?? ""); -if ($fichier === "" || str_contains($fichier, "..")) +$dossiers = [ + "jaquettes" => Config::get("jaquettesPath"), + "consoles-photos" => Config::get("consolesPhotosPath") +]; + +if ($fichier === "" || str_contains($fichier, "..") || !isset($dossiers[$type])) { http_response_code(404); exit; } -$chemin = Config::get("jaquettesPath") . "/" . $fichier; +$chemin = $dossiers[$type] . "/" . $fichier; if (!is_file($chemin)) { diff --git a/src/Config.php b/src/Config.php index 7232275..34cc27f 100644 --- a/src/Config.php +++ b/src/Config.php @@ -18,6 +18,7 @@ final class Config "sessionLifetime" => (int) self::env("SESSION_LIFETIME", "86400"), "dataPath" => dirname(__DIR__) . "/data", "jaquettesPath" => dirname(__DIR__) . "/data/jaquettes", + "consolesPhotosPath" => dirname(__DIR__) . "/data/consoles-photos", "cachePath" => dirname(__DIR__) . "/data/cache" ]; } diff --git a/src/Controllers/ConsoleController.php b/src/Controllers/ConsoleController.php index 093523b..49aaf44 100644 --- a/src/Controllers/ConsoleController.php +++ b/src/Controllers/ConsoleController.php @@ -62,11 +62,15 @@ final class ConsoleController try { - $console = $this->consoleService->creer($utilisateur["id"], $_POST); + $console = $this->consoleService->creer( + $utilisateur["id"], + $_POST, + $_FILES["photo"] ?? null + ); Flash::definir("success", "Console ajoutée."); Helpers::rediriger("/consoles/" . $console["id"]); } - catch (InvalidArgumentException $exception) + catch (InvalidArgumentException | RuntimeException $exception) { Flash::definir("danger", $exception->getMessage()); } @@ -97,11 +101,16 @@ final class ConsoleController try { - $console = $this->consoleService->modifier($id, $utilisateur["id"], $_POST); + $console = $this->consoleService->modifier( + $id, + $utilisateur["id"], + $_POST, + $_FILES["photo"] ?? null + ); Flash::definir("success", "Console mise à jour."); Helpers::rediriger("/consoles/" . $console["id"]); } - catch (InvalidArgumentException $exception) + catch (InvalidArgumentException | RuntimeException $exception) { Flash::definir("danger", $exception->getMessage()); } @@ -115,6 +124,54 @@ final class ConsoleController ]); } + public function envoyerPhoto(string $id): void + { + $utilisateur = $this->auth->exigerConnexion(); + Csrf::exiger(); + + $console = $this->consoleService->trouverPourUtilisateur($id, $utilisateur["id"]); + + if ($console === null) + { + Flash::definir("danger", "Console introuvable."); + Helpers::rediriger("/consoles"); + } + + try + { + $this->consoleService->mettreAJourPhoto( + $id, + $utilisateur["id"], + $_FILES["photo"] ?? [] + ); + Flash::definir("success", "Photo de la console enregistrée."); + } + catch (InvalidArgumentException | RuntimeException $exception) + { + Flash::definir("danger", $exception->getMessage()); + } + + Helpers::rediriger("/consoles/" . $id); + } + + public function supprimerPhoto(string $id): void + { + $utilisateur = $this->auth->exigerConnexion(); + Csrf::exiger(); + + try + { + $this->consoleService->supprimerPhoto($id, $utilisateur["id"]); + Flash::definir("success", "Photo supprimée."); + } + catch (RuntimeException $exception) + { + Flash::definir("danger", $exception->getMessage()); + } + + Helpers::rediriger("/consoles/" . $id); + } + public function supprimer(string $id): void { $utilisateur = $this->auth->exigerConnexion(); diff --git a/src/Controllers/JeuController.php b/src/Controllers/JeuController.php index d0e8fef..697e730 100644 --- a/src/Controllers/JeuController.php +++ b/src/Controllers/JeuController.php @@ -19,12 +19,14 @@ final class JeuController $recherche = Helpers::champ("q"); $consoleId = Helpers::champ("console"); $etat = Helpers::champ("etat"); + $format = Helpers::champ("format"); $jeux = $this->jeuService->listerPourUtilisateur( $utilisateur["id"], $recherche !== "" ? $recherche : null, $consoleId !== "" ? $consoleId : null, - $etat !== "" ? $etat : null + $etat !== "" ? $etat : null, + $format !== "" ? $format : null ); $consoles = $this->consoleService->listerPourUtilisateur($utilisateur["id"]); @@ -44,7 +46,8 @@ final class JeuController "filtres" => [ "q" => $recherche, "console" => $consoleId, - "etat" => $etat + "etat" => $etat, + "format" => $format ] ]); } @@ -269,6 +272,7 @@ final class JeuController $this->jeuService->modifier($id, $utilisateur["id"], array_merge($jeu, [ "jaquette" => $fichier, "genres" => $jeu["genres"] ?? [], + "formats" => $jeu["formats"] ?? [], "sourceEnrichissement" => ($jeu["sourceEnrichissement"] ?? "") === "manuel" ? "api" : ($jeu["sourceEnrichissement"] ?? "api") ])); @@ -309,6 +313,7 @@ final class JeuController "titre" => Helpers::champ("titre"), "consoleId" => Helpers::champ("consoleId"), "etat" => Helpers::champ("etat", "possede"), + "formats" => $_POST["formats"] ?? [], "jaquette" => Helpers::champ("jaquette"), "description" => Helpers::champ("description"), "dateSortie" => Helpers::champ("dateSortie"), @@ -328,6 +333,7 @@ final class JeuController "titre" => Helpers::champ("titre"), "consoleId" => Helpers::champ("consoleId"), "etat" => Helpers::champ("etat", "possede"), + "formats" => [], "jaquette" => Helpers::champ("jaquette"), "description" => Helpers::champ("description"), "dateSortie" => Helpers::champ("dateSortie"), diff --git a/src/Helpers.php b/src/Helpers.php index dec461f..fa46980 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -52,6 +52,48 @@ final class Helpers return self::etatsJeu()[$etat] ?? $etat; } + public static function formatsJeu(): array + { + return [ + "physique" => "Physique", + "dematerialise" => "Dématérialisé", + "rom" => "ROM" + ]; + } + + public static function libelleFormat(string $format): string + { + return self::formatsJeu()[$format] ?? $format; + } + + public static function normaliserFormats(mixed $formats): array + { + if (!is_array($formats)) + { + return []; + } + + $autorises = array_keys(self::formatsJeu()); + $resultat = []; + + foreach ($formats as $format) + { + $format = is_string($format) ? trim($format) : ""; + + if ($format !== "" && in_array($format, $autorises, true)) + { + $resultat[] = $format; + } + } + + return array_values(array_unique($resultat)); + } + + public static function champCoche(string $nom): bool + { + return isset($_POST[$nom]) || isset($_GET[$nom]); + } + public static function urlJaquette(?string $chemin): string { if (self::jaquetteAbsente($chemin)) @@ -84,6 +126,38 @@ final class Helpers return !is_file($fichier); } + public static function urlPhotoConsole(?string $chemin): string + { + if ($chemin === null || trim($chemin) === "") + { + return "/assets/img/console-manquante.svg"; + } + + if (str_starts_with($chemin, "http://") || str_starts_with($chemin, "https://")) + { + return $chemin; + } + + return "/consoles-photos/" . ltrim($chemin, "/"); + } + + public static function photoConsoleAbsente(?string $chemin): bool + { + if ($chemin === null || trim($chemin) === "") + { + return true; + } + + if (str_starts_with($chemin, "http://") || str_starts_with($chemin, "https://")) + { + return false; + } + + $fichier = Config::get("consolesPhotosPath") . "/" . basename($chemin); + + return !is_file($fichier); + } + public static function tronquer(string $texte, int $longueur = 140): string { if (mb_strlen($texte) <= $longueur) diff --git a/src/ImageUpload.php b/src/ImageUpload.php new file mode 100644 index 0000000..51e8897 --- /dev/null +++ b/src/ImageUpload.php @@ -0,0 +1,92 @@ + "jpg", + "image/png" => "png", + "image/webp" => "webp", + "image/gif" => "gif" + ]; + + public static function enregistrer(array $fichier, string $dossierCible, int $tailleMaxMo = 5): string + { + if (($fichier["error"] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) + { + throw new InvalidArgumentException("Aucun fichier sélectionné."); + } + + if (($fichier["error"] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) + { + throw new InvalidArgumentException("Échec de l'envoi du fichier."); + } + + $tailleMax = $tailleMaxMo * 1024 * 1024; + + if (($fichier["size"] ?? 0) <= 0 || ($fichier["size"] ?? 0) > $tailleMax) + { + throw new InvalidArgumentException("La photo doit faire moins de " . $tailleMaxMo . " Mo."); + } + + $tmp = (string) ($fichier["tmp_name"] ?? ""); + + if ($tmp === "" || !is_uploaded_file($tmp)) + { + throw new InvalidArgumentException("Fichier upload invalide."); + } + + $info = @getimagesize($tmp); + + if ($info === false || empty($info["mime"])) + { + throw new InvalidArgumentException("Le fichier doit être une image (JPG, PNG, WebP ou GIF)."); + } + + $mime = (string) $info["mime"]; + + if (!isset(self::EXTENSIONS[$mime])) + { + throw new InvalidArgumentException("Format d'image non supporté."); + } + + if (!is_dir($dossierCible)) + { + mkdir($dossierCible, 0775, true); + } + + $nomFichier = Helpers::genererId() . "." . self::EXTENSIONS[$mime]; + $destination = rtrim($dossierCible, "/\\") . "/" . $nomFichier; + + if (!move_uploaded_file($tmp, $destination)) + { + throw new RuntimeException("Impossible d'enregistrer la photo."); + } + + return $nomFichier; + } + + public static function supprimer(?string $nomFichier, string $dossierCible): void + { + if ($nomFichier === null || $nomFichier === "") + { + return; + } + + if (str_starts_with($nomFichier, "http://") || str_starts_with($nomFichier, "https://")) + { + return; + } + + $chemin = rtrim($dossierCible, "/\\") . "/" . basename($nomFichier); + + if (is_file($chemin)) + { + unlink($chemin); + } + } +} diff --git a/src/Services/ConsoleService.php b/src/Services/ConsoleService.php index 95f5bbc..69f13ac 100644 --- a/src/Services/ConsoleService.php +++ b/src/Services/ConsoleService.php @@ -37,7 +37,7 @@ final class ConsoleService return $console; } - public function creer(string $utilisateurId, array $donnees): array + public function creer(string $utilisateurId, array $donnees, ?array $fichierPhoto = null): array { $nom = trim((string) ($donnees["nom"] ?? "")); $constructeur = trim((string) ($donnees["constructeur"] ?? "")); @@ -47,7 +47,16 @@ final class ConsoleService throw new InvalidArgumentException("Le nom et le constructeur sont obligatoires."); } - $annee = $this->normaliserAnnee($donnees["annee"] ?? null); + $photo = null; + + if ($this->fichierPresent($fichierPhoto)) + { + $photo = ImageUpload::enregistrer( + $fichierPhoto, + Config::get("consolesPhotosPath") + ); + } + $maintenant = Helpers::maintenant(); $console = [ @@ -55,7 +64,8 @@ final class ConsoleService "utilisateurId" => $utilisateurId, "nom" => $nom, "constructeur" => $constructeur, - "annee" => $annee, + "annee" => $this->normaliserAnnee($donnees["annee"] ?? null), + "photo" => $photo, "notes" => trim((string) ($donnees["notes"] ?? "")), "createdAt" => $maintenant, "updatedAt" => $maintenant @@ -64,7 +74,7 @@ final class ConsoleService return $this->store->ajouter($console); } - public function modifier(string $id, string $utilisateurId, array $donnees): array + public function modifier(string $id, string $utilisateurId, array $donnees, ?array $fichierPhoto = null): array { $console = $this->trouverPourUtilisateur($id, $utilisateurId); @@ -81,13 +91,25 @@ final class ConsoleService throw new InvalidArgumentException("Le nom et le constructeur sont obligatoires."); } - $miseAJour = $this->store->mettreAJour($id, [ + $champs = [ "nom" => $nom, "constructeur" => $constructeur, "annee" => $this->normaliserAnnee($donnees["annee"] ?? null), "notes" => trim((string) ($donnees["notes"] ?? "")), "updatedAt" => Helpers::maintenant() - ]); + ]; + + if ($this->fichierPresent($fichierPhoto)) + { + $nouvellePhoto = ImageUpload::enregistrer( + $fichierPhoto, + Config::get("consolesPhotosPath") + ); + ImageUpload::supprimer($console["photo"] ?? null, Config::get("consolesPhotosPath")); + $champs["photo"] = $nouvellePhoto; + } + + $miseAJour = $this->store->mettreAJour($id, $champs); if ($miseAJour === null) { @@ -97,6 +119,59 @@ final class ConsoleService return $miseAJour; } + public function mettreAJourPhoto(string $id, string $utilisateurId, array $fichierPhoto): array + { + $console = $this->trouverPourUtilisateur($id, $utilisateurId); + + if ($console === null) + { + throw new RuntimeException("Console introuvable."); + } + + $nouvellePhoto = ImageUpload::enregistrer( + $fichierPhoto, + Config::get("consolesPhotosPath") + ); + + ImageUpload::supprimer($console["photo"] ?? null, Config::get("consolesPhotosPath")); + + $miseAJour = $this->store->mettreAJour($id, [ + "photo" => $nouvellePhoto, + "updatedAt" => Helpers::maintenant() + ]); + + if ($miseAJour === null) + { + throw new RuntimeException("Échec de la mise à jour de la photo."); + } + + return $miseAJour; + } + + public function supprimerPhoto(string $id, string $utilisateurId): array + { + $console = $this->trouverPourUtilisateur($id, $utilisateurId); + + if ($console === null) + { + throw new RuntimeException("Console introuvable."); + } + + ImageUpload::supprimer($console["photo"] ?? null, Config::get("consolesPhotosPath")); + + $miseAJour = $this->store->mettreAJour($id, [ + "photo" => null, + "updatedAt" => Helpers::maintenant() + ]); + + if ($miseAJour === null) + { + throw new RuntimeException("Échec de la suppression de la photo."); + } + + return $miseAJour; + } + public function supprimer(string $id, string $utilisateurId): bool { $console = $this->trouverPourUtilisateur($id, $utilisateurId); @@ -106,6 +181,8 @@ final class ConsoleService return false; } + ImageUpload::supprimer($console["photo"] ?? null, Config::get("consolesPhotosPath")); + return $this->store->supprimer($id); } @@ -114,6 +191,12 @@ final class ConsoleService return count($this->listerPourUtilisateur($utilisateurId)); } + private function fichierPresent(?array $fichier): bool + { + return is_array($fichier) + && (($fichier["error"] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE); + } + private function normaliserAnnee(mixed $annee): ?int { if ($annee === null || $annee === "") diff --git a/src/Services/JeuService.php b/src/Services/JeuService.php index ba00eb1..289c511 100644 --- a/src/Services/JeuService.php +++ b/src/Services/JeuService.php @@ -15,7 +15,8 @@ final class JeuService string $utilisateurId, ?string $recherche = null, ?string $consoleId = null, - ?string $etat = null + ?string $etat = null, + ?string $format = null ): array { $jeux = $this->store->filtrer( @@ -38,6 +39,19 @@ final class JeuService )); } + if ($format !== null && $format !== "") + { + $jeux = array_values(array_filter( + $jeux, + static function (array $jeu) use ($format): bool + { + $formats = $jeu["formats"] ?? []; + + return is_array($formats) && in_array($format, $formats, true); + } + )); + } + if ($recherche !== null && $recherche !== "") { $terme = mb_strtolower($recherche); @@ -50,7 +64,8 @@ final class JeuService $jeu["description"] ?? "", $jeu["editeur"] ?? "", $jeu["developpeur"] ?? "", - implode(" ", $jeu["genres"] ?? []) + implode(" ", $jeu["genres"] ?? []), + implode(" ", $jeu["formats"] ?? []) ]; return str_contains(mb_strtolower(implode(" ", $champs)), $terme); @@ -199,6 +214,7 @@ final class JeuService "titre" => $titre, "consoleId" => $consoleId, "etat" => $etat, + "formats" => Helpers::normaliserFormats($donnees["formats"] ?? []), "jaquette" => $jaquette, "description" => trim((string) ($donnees["description"] ?? "")), "dateSortie" => $this->normaliserDate($donnees["dateSortie"] ?? null), diff --git a/src/bootstrap.php b/src/bootstrap.php index 717bb5b..8e7edf4 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -7,6 +7,7 @@ require_once __DIR__ . "/JsonStore.php"; require_once __DIR__ . "/Csrf.php"; require_once __DIR__ . "/Flash.php"; require_once __DIR__ . "/Helpers.php"; +require_once __DIR__ . "/ImageUpload.php"; require_once __DIR__ . "/View.php"; require_once __DIR__ . "/Auth.php"; require_once __DIR__ . "/Router.php"; @@ -22,7 +23,7 @@ require_once __DIR__ . "/Controllers/AdminController.php"; Config::charger(); -foreach (["", "/jaquettes", "/cache"] as $sousDossier) +foreach (["", "/jaquettes", "/consoles-photos", "/cache"] as $sousDossier) { $chemin = Config::get("dataPath") . $sousDossier; @@ -63,6 +64,8 @@ $router->post("/consoles/creer", [$consoleController, "creer"]); $router->get("/consoles/{id}", [$consoleController, "detail"]); $router->get("/consoles/{id}/modifier", [$consoleController, "modifier"]); $router->post("/consoles/{id}/modifier", [$consoleController, "modifier"]); +$router->post("/consoles/{id}/photo", [$consoleController, "envoyerPhoto"]); +$router->post("/consoles/{id}/photo/supprimer", [$consoleController, "supprimerPhoto"]); $router->post("/consoles/{id}/supprimer", [$consoleController, "supprimer"]); $router->get("/jeux", [$jeuController, "liste"]); diff --git a/templates/accueil.php b/templates/accueil.php index f883ed6..67fca57 100644 --- a/templates/accueil.php +++ b/templates/accueil.php @@ -60,9 +60,16 @@

- "> - - +
+ "> + + + + + + + +
diff --git a/templates/consoles/detail.php b/templates/consoles/detail.php index e0f7310..a027b5b 100644 --- a/templates/consoles/detail.php +++ b/templates/consoles/detail.php @@ -1,18 +1,67 @@ - -
+ -
-
- - "> -
-
- - "> -
+
- - " value=""> +
+ " + alt="Photo de la console" + id="apercuPhotoConsole" + > +
+ + +
JPG, PNG, WebP ou GIF — 5 Mo max.
-
- - +
+
+
+ + "> +
+
+ + "> +
+
+ + " value=""> +
+
+ + +
+
diff --git a/templates/consoles/liste.php b/templates/consoles/liste.php index 73792c9..e08686e 100644 --- a/templates/consoles/liste.php +++ b/templates/consoles/liste.php @@ -22,8 +22,12 @@
" class="console-card-link"> -
- +
+ " + alt="Photo de " + loading="lazy" + >

·

diff --git a/templates/jeux/detail.php b/templates/jeux/detail.php index 04b136f..625f9c7 100644 --- a/templates/jeux/detail.php +++ b/templates/jeux/detail.php @@ -12,6 +12,11 @@ "> + + + + + @@ -72,6 +77,21 @@
+
+
Supports
+
+ + + — + + + + + + + +
+
Notes
diff --git a/templates/jeux/formulaire.php b/templates/jeux/formulaire.php index 68cf27f..aef491c 100644 --- a/templates/jeux/formulaire.php +++ b/templates/jeux/formulaire.php @@ -3,6 +3,7 @@ $estEdition = isset($jeu["id"]); $genresTexte = is_array($jeu["genres"] ?? null) ? implode(", ", $jeu["genres"]) : (string) ($jeu["genres"] ?? ""); +$formatsSelectionnes = Helpers::normaliserFormats($jeu["formats"] ?? []); $retourEnrichissement = $action; ?> @@ -70,6 +71,23 @@ $retourEnrichissement = $action;
+
+ Support(s) +
+ $libelle): ?> + + +
+
Cochez toutes les versions que vous possédez.
+
">
@@ -28,7 +28,7 @@
-