refactor: update sidebar component to use ViewComponent and improve layout structure

This commit is contained in:
mirage
2026-03-26 11:10:48 +01:00
parent cceff9a02d
commit 95a5f99334
19 changed files with 523 additions and 6378 deletions

View File

@@ -0,0 +1,462 @@
using Webzine.EntitiesContext;
using Webzine.Entity;
using Bogus;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Webzine.Repository
{
/// <summary>
/// Classe de service pour peupler la base de données avec des données fictives (mock data) à des fins de développement et de test.
/// </summary>
public class SeedMockData
{
private readonly WebzineDbContext _context;
private readonly Random _random;
/// <summary>
/// Constructeur pour l'utilisation avec base de données
/// </summary>
public SeedMockData(WebzineDbContext context)
{
_context = context;
_random = new Random();
}
/// <summary>
/// Constructeur pour l'utilisation en mémoire (sans base de données)
/// </summary>
public SeedMockData()
{
_random = new Random();
}
/// <summary>
/// Peuple la base de données avec des données fictives
/// </summary>
public void SeedDB()
{
// Vérifie si la base contient déjà des données
if (_context.Artistes.Any()) return;
// Génère toutes les données
var styles = GenererStyles(18);
var artistes = GenererArtistes(100);
var titres = GenererTitres(artistes, 500, 550);
var commentaires = GenererCommentaires(titres, 0, 5);
// Associe les styles aux titres
AssocierStylesAuxTitres(titres, styles);
// Sauvegarde dans la base
_context.SaveChanges();
}
/// <summary>
/// Génère des données fictives en mémoire sans base de données
/// </summary>
/// <returns>Tuple contenant toutes les données générées</returns>
public (List<Style> Styles, List<Artiste> Artistes, List<Titre> Titres, List<Commentaire> Commentaires) SeedLocal()
{
// Génère toutes les données
var styles = GenererStyles(18);
var artistes = GenererArtistes(100);
var titres = GenererTitres(artistes, 500, 550);
var commentaires = GenererCommentaires(titres, 0, 5);
// Associe les styles aux titres
AssocierStylesAuxTitres(titres, styles);
// Retourne les données sans les sauvegarder
return (styles, artistes, titres, commentaires);
}
/// <summary>
/// Génère une liste de styles musicaux
/// </summary>
private List<Style> GenererStyles(int count)
{
var styleFaker = new Faker<Style>("fr")
.RuleFor(s => s.Libelle, f => GenererStyleMusical(f))
.RuleFor(s => s.Titres, () => new List<Titre>());
var styles = styleFaker.Generate(count);
// Styles réalistes en français
var stylesRealistes = new[]
{
"Rock", "Pop", "Jazz", "Classique", "Hip Hop", "Électronique",
"Blues", "Country", "Reggae", "Metal", "Funk", "Soul",
"R&B", "Punk", "Disco", "Folk", "Ambient", "Techno",
"House", "Grunge", "Indie", "Alternative", "Ska", "Gospel",
"Chanson Française", "Variété", "Rap Français", "Musique du Monde"
};
for (int i = 0; i < Math.Min(stylesRealistes.Length, count); i++)
{
styles[i].Libelle = stylesRealistes[i];
}
if (_context != null)
{
_context.Styles.AddRange(styles);
}
return styles;
}
/// <summary>
/// Génère un style musical réaliste
/// </summary>
private string GenererStyleMusical(Bogus.Faker f)
{
var styles = new[]
{
"Rock Alternatif", "Pop Rock", "Jazz Fusion", "Électro", "Hip Hop Old School",
"Blues Rock", "Metal Progressif", "Funk Soul", "Reggae Dub", "House Music"
};
return f.PickRandom(styles);
}
/// <summary>
/// Génère une liste d'artistes
/// </summary>
private List<Artiste> GenererArtistes(int count)
{
var artistFaker = new Faker<Artiste>("fr")
.RuleFor(a => a.Nom, f => GenererNomArtiste(f))
.RuleFor(a => a.Biographie, f => GenererBiographieRealiste(f))
.RuleFor(a => a.Titres, () => new List<Titre>());
var artistes = artistFaker.Generate(count);
// Ajoute des noms d'artistes célèbres
var artistesCelebres = new[]
{
"Les Beatles", "Queen", "Michael Jackson", "Madonna", "Bob Dylan",
"David Bowie", "Prince", "Nirvana", "Pink Floyd", "Led Zeppelin",
"Les Rolling Stones", "U2", "The Who", "The Doors", "AC/DC",
"Metallica", "Radiohead", "Coldplay", "Arctic Monkeys", "The Smiths",
"Daft Punk", "Justice", "Miles Davis", "Nina Simone", "Aretha Franklin",
"Johnny Hallyday", "Édith Piaf", "Charles Aznavour", "Jacques Brel",
"Serge Gainsbourg", "Joe Dassin", "Claude François", "Jean-Jacques Goldman"
};
for (int i = 0; i < Math.Min(artistesCelebres.Length, count); i++)
{
artistes[i].Nom = artistesCelebres[i];
}
if (_context != null)
{
_context.Artistes.AddRange(artistes);
}
return artistes;
}
/// <summary>
/// Génère un nom d'artiste réaliste
/// </summary>
private string GenererNomArtiste(Bogus.Faker f)
{
var typesArtistes = new[]
{
f.Name.FullName(),
$"Les {f.Random.WordsArray(1)[0]}",
$"{f.Random.WordsArray(1)[0]} {f.Random.WordsArray(1)[0]}",
$"{f.Company.CompanyName()}",
$"{f.Name.FirstName()} et les {f.Random.WordsArray(1)[0]}"
};
return f.PickRandom(typesArtistes);
}
/// <summary>
/// Génère une biographie réaliste en français
/// </summary>
private string GenererBiographieRealiste(Bogus.Faker f)
{
var partiesBiographie = new List<string>();
// Début de carrière
partiesBiographie.Add($"Formé(e) en {f.Date.Past(50).Year}, {f.Name.FirstName()} a commencé son parcours musical à {f.Address.City()}.");
// Style musical
partiesBiographie.Add($"Sa musique mélange {f.Music.Genre()} avec des influences de {f.Music.Genre()} et de {f.Music.Genre()}.");
// Succès
partiesBiographie.Add($"Avec plus de {f.Random.Number(10, 500)} millions de disques vendus dans le monde, {f.Name.FirstName()} est devenu(e) l'un(e) des artistes les plus influent(e)s de sa génération.");
// Albums et tournées
partiesBiographie.Add($"L'album révolutionnaire est sorti en {f.Date.Past(20).Year}, suivi de tournées à guichets fermés en Europe, Amérique du Nord et Asie.");
// Récompenses
partiesBiographie.Add($"A remporté {f.Random.Number(1, 15)} Victoires de la Musique et a reçu des critiques élogieuses pour son approche innovante de la musique.");
// Activités récentes
partiesBiographie.Add($"Actuellement en train de travailler sur de nouveaux projets, {f.Name.FirstName()} continue de repousser les limites et d'inspirer les nouvelles générations de musiciens.");
return string.Join(" ", partiesBiographie);
}
/// <summary>
/// Génère une liste de titres
/// </summary>
private List<Titre> GenererTitres(List<Artiste> artistes, int minTitres, int maxTitres)
{
var tousLesTitres = new List<Titre>();
var totalTitres = _random.Next(minTitres, maxTitres + 1);
// Distribution des titres entre artistes
var titresParArtiste = new Dictionary<int, int>();
var titresRestants = totalTitres;
foreach (var artiste in artistes)
{
int nbTitres;
if (titresRestants > artistes.Count * 3)
{
nbTitres = _random.Next(3, 12);
}
else
{
nbTitres = _random.Next(1, 8);
}
nbTitres = Math.Min(nbTitres, titresRestants);
if (nbTitres <= 0) break;
titresParArtiste[artiste.IdArtiste] = nbTitres;
titresRestants -= nbTitres;
}
// Distribution des titres restants
while (titresRestants > 0)
{
var artisteAleatoire = artistes[_random.Next(artistes.Count)];
if (!titresParArtiste.ContainsKey(artisteAleatoire.IdArtiste))
{
titresParArtiste[artisteAleatoire.IdArtiste] = 0;
}
titresParArtiste[artisteAleatoire.IdArtiste]++;
titresRestants--;
}
// Génération des titres pour chaque artiste
foreach (var artiste in artistes)
{
var nbTitres = titresParArtiste.ContainsKey(artiste.IdArtiste) ?
titresParArtiste[artiste.IdArtiste] : _random.Next(1, 5);
if (nbTitres == 0) continue;
var titreFaker = new Faker<Titre>("fr")
.RuleFor(t => t.Libelle, f => GenererTitreChanson(f))
.RuleFor(t => t.Duree, f => _random.Next(90, 480))
.RuleFor(t => t.Chronique, f => GenererChroniqueRealiste(f))
.RuleFor(t => t.DateCreation, f => f.Date.PastOffset(3).DateTime)
.RuleFor(t => t.DateSortie, (f, t) => f.Date.Between(t.DateCreation, t.DateCreation.AddMonths(12)))
.RuleFor(t => t.UrlJaquette, f => GenererUrlPochette(f))
.RuleFor(t => t.UrlEcoute, f => GenererUrlStreaming(f))
.RuleFor(t => t.NbLectures, f => _random.Next(1000, 50000000))
.RuleFor(t => t.NbLikes, (f, t) => _random.Next(0, (int)(t.NbLectures * 0.15)))
.RuleFor(t => t.Album, f => GenererNomAlbum(f))
.RuleFor(t => t.IdArtiste, artiste.IdArtiste)
.RuleFor(t => t.Artiste, artiste)
.RuleFor(t => t.Styles, () => new List<Style>())
.RuleFor(t => t.Commentaires, () => new List<Commentaire>());
var titres = titreFaker.Generate(nbTitres);
tousLesTitres.AddRange(titres);
}
if (_context != null)
{
_context.Titres.AddRange(tousLesTitres);
}
return tousLesTitres;
}
/// <summary>
/// Génère un titre de chanson réaliste en français
/// </summary>
private string GenererTitreChanson(Bogus.Faker f)
{
var titresChansons = new[]
{
$"{f.Random.WordsArray(1, 3)[0]} {f.Random.WordsArray(1, 2)[0]}",
$"{f.Hacker.Verb()} mon {f.Commerce.ProductName()}",
$"La {f.Random.WordsArray(1)[0]} de {f.Random.WordsArray(1)[0]}",
$"{f.Name.FirstName()} {f.Random.WordsArray(1)[0]}",
$"Sous le ciel de {f.Address.City()}",
$"L'amour {f.Random.WordsArray(1)[0]}",
$"Je t'aime {f.Random.WordsArray(1)[0]}",
$"Les {f.Random.WordsArray(1)[0]} de Paris",
$"Mon {f.Random.WordsArray(1)[0]} préféré",
$"Une nuit à {f.Address.City()}"
};
return f.PickRandom(titresChansons);
}
/// <summary>
/// Génère une chronique réaliste en français
/// </summary>
private string GenererChroniqueRealiste(Bogus.Faker f)
{
var modelesChronique = new[]
{
$"Ce morceau met en valeur le {f.Music.Genre()} dans sa plus belle expression. Le rythme {f.Random.WordsArray(1)[0]} combiné aux mélodies {f.Random.WordsArray(1)[0]} crée une expérience inoubliable.",
$"Un chef-d'œuvre qui mélange {f.Music.Genre()} avec des éléments de {f.Music.Genre()}. La qualité de production est exceptionnelle, et les voix sont {f.Random.WordsArray(1)[0]}.",
$"Des premières notes jusqu'au crescendo final, cette chanson vous emmène dans un voyage à travers des paysages sonores {f.Random.WordsArray(1)[0]}. Hautement recommandé pour les fans de {f.Music.Genre()}.",
$"Avec son rythme {f.Random.WordsArray(1)[0]} et ses paroles profondes, ce titre est destiné à devenir un classique du {f.Music.Genre()}.",
$"Une approche innovante du {f.Music.Genre()} qui repousse les limites tout en restant accessible. Le refrain {f.Random.WordsArray(1)[0]} est particulièrement mémorable.",
$"Cette chanson démontre pourquoi {f.Name.FirstName()} est considéré comme un pionnier du {f.Music.Genre()}. L'arrangement {f.Random.WordsArray(1)[0]} est à la fois complexe et accessible.",
$"Une composition d'une beauté saisissante qui met en valeur la versatilité de l'artiste. Le pont {f.Random.WordsArray(1)[0]} apporte une touche surprenante qui élève l'ensemble."
};
var chronique = f.PickRandom(modelesChronique);
return chronique + " " + f.Lorem.Sentence(_random.Next(2, 5));
}
/// <summary>
/// Génère une URL de pochette d'album
/// </summary>
private string GenererUrlPochette(Bogus.Faker f)
{
return $"https://picsum.photos/id/{_random.Next(1, 1000)}/300/300";
}
/// <summary>
/// Génère une URL de streaming
/// </summary>
private string GenererUrlStreaming(Bogus.Faker f)
{
var plateformes = new[] { "spotify", "apple", "deezer", "youtube", "tidal", "amazon" };
var plateforme = f.PickRandom(plateformes);
return $"https://{plateforme}.com/track/{f.Random.AlphaNumeric(22)}";
}
/// <summary>
/// Génère un nom d'album réaliste en français
/// </summary>
private string GenererNomAlbum(Bogus.Faker f)
{
var modelesAlbum = new[]
{
$"{string.Join(" ", f.Random.WordsArray(2, 3))}",
$"{f.Random.WordsArray(1)[0]} {f.Random.WordsArray(1)[0]}",
$"L'album {string.Join(" ", f.Random.WordsArray(2))}",
$"{f.Company.CatchPhrase()}",
$"{f.Random.WordsArray(1)[0]} {f.Random.Number(1, 5)}",
$"Les {f.Random.WordsArray(1)[0]} de l'âme",
$"Voyage au cœur de {f.Address.City()}",
$"Entre {f.Random.WordsArray(1)[0]} et {f.Random.WordsArray(1)[0]}"
};
return f.PickRandom(modelesAlbum);
}
/// <summary>
/// Génère une liste de commentaires en français
/// </summary>
private List<Commentaire> GenererCommentaires(List<Titre> titres, int minCommentaires, int maxCommentaires)
{
var tousLesCommentaires = new List<Commentaire>();
var modelesCommentaires = new[]
{
"J'adore ce morceau! La mélodie est incroyable.",
"Super chanson, vraiment représentative du genre.",
"Pas mon style, mais je peux apprécier le travail.",
"C'est ma nouvelle chanson préférée! Je ne peux pas arrêter de l'écouter.",
"La qualité de production est exceptionnelle. Bravo!",
"Composition intéressante, bien qu'un peu répétitive au milieu.",
"Ce morceau me rappelle ma jeunesse. Pure nostalgie.",
"Travail brillant! L'artiste s'est surpassé.",
"Je l'ai écoutée en boucle toute la semaine. Tellement entraînante!",
"Les paroles sont tellement significatives et profondes.",
"Chanson parfaite pour un road trip ou un dimanche après-midi tranquille.",
"C'est ce que la musique moderne devrait être. Innovant!",
"Un peu trop produit à mon goût, mais reste agréable.",
"La partie instrumentale est particulièrement impressionnante.",
"J'ai hâte d'entendre plus de cet artiste!",
"Cette chanson me donne des frissons à chaque fois que je l'entends.",
"Fusion intéressante des genres. Son très unique.",
"Les voix sont incroyables! Quelle étendue et quelle émotion.",
"Ça va rester dans ma playlist pour longtemps.",
"Un véritable chef-d'œuvre du début à la fin.",
"Magnifique composition, les paroles touchent le cœur.",
"Du grand art! Une claque musicale.",
"Je suis subjugué par cette chanson, vraiment de la qualité.",
"La mélodie reste dans la tête, c'est un tube assuré.",
"Une belle découverte, je vais suivre cet artiste."
};
foreach (var titre in titres)
{
var nbCommentaires = _random.Next(minCommentaires, maxCommentaires + 1);
for (int i = 0; i < nbCommentaires; i++)
{
var commentaire = new Commentaire
{
Contenu = _random.Next(0, 3) == 0
? _random.Next(0, 2) == 0
? faker.PickRandom(modelesCommentaires) + " " + faker.Lorem.Sentence()
: faker.PickRandom(modelesCommentaires)
: faker.PickRandom(modelesCommentaires) + "\n\n" + faker.Lorem.Paragraph(),
Auteur = GenererNomUtilisateur(),
DateCreation = DateTime.Now.AddDays(-_random.Next(0, 730)),
IdTitre = titre.IdTitre,
Titre = titre
};
tousLesCommentaires.Add(commentaire);
}
}
if (_context != null)
{
_context.Commentaires.AddRange(tousLesCommentaires);
}
return tousLesCommentaires;
}
private readonly Bogus.Faker faker = new Bogus.Faker("fr");
/// <summary>
/// Génère un nom d'utilisateur réaliste
/// </summary>
private string GenererNomUtilisateur()
{
var typesUtilisateurs = new[]
{
faker.Internet.UserName(),
faker.Name.FirstName().ToLower() + _random.Next(10, 999),
"fan_de_musique_" + _random.Next(100, 999),
"melomane_" + faker.Random.WordsArray(1)[0].ToLower(),
"auditeur_" + _random.Next(1000, 9999)
};
return faker.PickRandom(typesUtilisateurs);
}
/// <summary>
/// Associe des styles aux titres
/// </summary>
private void AssocierStylesAuxTitres(List<Titre> titres, List<Style> styles)
{
foreach (var titre in titres)
{
var nbStyles = _random.Next(1, 4);
var stylesTitre = styles.OrderBy(x => _random.Next()).Take(nbStyles).ToList();
titre.Styles = stylesTitre;
}
}
}
}

View File

@@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Mvc;
using Webzine.Repository.Contracts;
namespace Webzine.Components
{
public class SidebarViewComponent : ViewComponent
{
private readonly IStyleRepository styleRepository;
public SidebarViewComponent(IStyleRepository styleRepository)
{
this.styleRepository = styleRepository;
}
public IViewComponentResult Invoke()
{
var styles = this.styleRepository.FindAll();
return this.View(styles);
}
}
}

View File

@@ -45,16 +45,32 @@ try
//builder.Services.AddScoped<ICommentaireRepository, LocalCommentaireRepository>();
}
// https://learn.microsoft.com/fr-fr/aspnet/core/performance/response-compression?view=aspnetcore-10.0#configuration
// Ajoute le service de compression des réponses HTTP pour réduire la taille des données envoyées au client et améliorer les performances de l'application.
builder.Services.AddResponseCompression();
var app = builder.Build();
app.UseResponseCompression();
// Active la possibilite de servir des fichiers statiques presents dans
// le dossier wwwroot.
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
// https://learn.microsoft.com/fr-fr/aspnet/core/fundamentals/static-files?view=aspnetcore-10.0#set-http-response-headers
ctx.Context.Response.Headers.Append("Cache-Control", "public, max-age=31536000");
},
});
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<WebzineDbContext>();
db.Database.EnsureCreated();
var seeder = new SeedMockData(db);
seeder.SeedDB();
}
// Active le middleware permettant le routage des requetes entrantes.

View File

@@ -14,7 +14,8 @@
<div class="col-12 col-md-3 text-center mb-3 mb-md-0">
<img class="img-fluid img-thumbnail"
src="@titre.UrlJaquette"
alt="@titre.Libelle" />
alt="@titre.Libelle"
loading="lazy" />
</div>
<!-- Contenu -->
@@ -82,7 +83,7 @@
{
<div class="col-12 col-md-6 col-lg-4">
<div class="card h-100">
<img class="card-img-top" src="@titre.UrlJaquette" alt="@titre.Album" />
<img class="card-img-top" src="@titre.UrlJaquette" alt="@titre.Album" loading="lazy" />
<div class="card-body">
<a asp-controller="Titre" asp-action="Details" asp-route-id="@titre.IdTitre" class="card-link">

View File

@@ -32,7 +32,8 @@
<div class="col-md-3 mb-3">
<img src="@premierTitre.UrlJaquette"
class="img-fluid shadow-sm rounded border"
alt="Pochette de @groupe.Key" />
alt="Pochette de @groupe.Key"
loading="lazy" />
</div>
<div class="col-md-9">

View File

@@ -53,7 +53,7 @@
asp-action="Details"
asp-route-id="@titre.IdTitre"
class="me-3 text-black">
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70" height="70" class="object-fit-cover" />
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70" height="70" class="object-fit-cover" loading="lazy"/>
</a>
<div class="justify-content-center d-flex flex-column">

View File

@@ -9,8 +9,6 @@
<link rel="stylesheet" href="~/css/bootstrap.min.css">
<link rel="stylesheet" href="~/css/all.min.css">
<link rel="stylesheet" href="~/css/app.css">
<script src="~/js/bootstrap.bundle.js" defer></script>
</head>
<body>
<partial name="_Header"/>
@@ -21,10 +19,12 @@
</main>
@if(ViewContext.RouteData.Values["area"]?.ToString() != "Administration")
{
<partial name="_Sidebar" />
@await Component.InvokeAsync("Sidebar")
}
</div>
</div>
<partial name="_Footer" />
</body>
</html>
<script src="~/js/bootstrap.bundle.min.js" defer></script>

View File

@@ -1,8 +1,5 @@
@*
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
*@
@{
}
@model IEnumerable<Webzine.Entity.Style>
<aside class="col-lg-3 d-none d-lg-block">
<div>
<h2>À propos</h2>
@@ -11,41 +8,14 @@
<div>
<h2>Styles</h2>
<ul>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Acid house">Acid house</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Ambient">Ambient</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Deep house">Deep house</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Disco">Disco</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Downtempo">Downtempo</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Drum n bass">Drum n bass</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Dub Techno">Dub Techno</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Electro">Electro</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Electronic">Electronic</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Experimental">Experimental</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Funk">Funk</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Garage">Garage</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Hardcore">Hardcore</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Hardstyle">Hardstyle</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Hip hop">Hip hop</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="House">House</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Indie">Indie</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Industrial">Industrial</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Jazz">Jazz</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Latin">Latin</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Metal">Metal</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Minimal">Minimal</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Pop">Pop</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Progressive">Progressive</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Punk">Punk</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="R&B">R&B</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Rap">Rap</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Reggae">Reggae</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Rock">Rock</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Soul">Soul</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Techno">Techno</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Trance">Trance</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Trip hop">Trip hop</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="UK garage">UK garage</a></li>
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="World">World</a></li>
@foreach (var style in Model)
{
<li>
<a asp-controller="Titre" asp-action="Style" asp-route-id="@style.Libelle">
@style.Libelle
</a>
</li>
}
</ul>
</div>
</aside>

View File

@@ -86,7 +86,9 @@
<div class="col-md-4 text-center">
<img src="@Model.Details.UrlJaquette"
class="img-fluid rounded shadow"
alt="Jaquette" />
alt="Jaquette"
loading="lazy"
fetchpriority="high" />
</div>
</div>

View File

@@ -29,7 +29,7 @@
<a asp-action="Details"
asp-route-id="@titre.IdTitre"
class="me-3 text-black">
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70px" height="70px" class="object-fit-cover"/>
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70px" height="70px" class="object-fit-cover" loading="lazy"/>
</a>
<!-- Infos -->

View File

@@ -18,8 +18,6 @@
<ItemGroup>
<Folder Include="Data\" />
<Folder Include="wwwroot\data\" />
<Folder Include="wwwroot\lib\" />
</ItemGroup>
<ItemGroup>

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long