feat: add default cover image for all media types

Added a beautiful default cover image featuring Naomi reading in her cozy
library, which will be displayed whenever a media item doesn't have a cover
image provided.

Changes:
- Added default-cover.jpg to frontend public/assets directory
- Updated all list components (games, books, music, shows, manga, art) to
  always display an image using the default as fallback
- Updated all detail components to always show cover section with default
  fallback
- Removed conditional rendering of cover images - now always visible
- Properly handles different property names (coverImage, coverArt, imageUrl)

Benefits:
- Consistent visual appearance across all media types
- No more empty spaces where cover images would be
- Better UX with uniform card layouts
- Beautiful placeholder that matches the library theme

Co-Authored-By: Naomi Carrigan <commits@nhcarrigan.com>
This commit is contained in:
2026-02-20 19:34:30 -08:00
committed by Naomi Carrigan
parent b781034fce
commit d74a342f63
13 changed files with 24 additions and 56 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 MiB

@@ -46,11 +46,9 @@ import { Art, Comment, UpdateArtDto } from '@library/shared-types';
</div>
} @else if (art()) {
<div class="art-detail-card">
@if (art()!.imageUrl) {
<div class="art-image-section">
<img [src]="art()!.imageUrl" [alt]="art()!.description || art()!.title" class="art-image-large">
</div>
}
<div class="art-image-section">
<img [src]="art()!.imageUrl || '/assets/default-cover.jpg'" [alt]="art()!.description || art()!.title" class="art-image-large">
</div>
<div class="art-content">
<div class="art-header">
@@ -395,7 +395,7 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
<a [routerLink]="['/art', art.id]" class="card-link">
<div class="art-image-container">
<img
[src]="art.imageUrl"
[src]="art.imageUrl || '/assets/default-cover.jpg'"
[alt]="art.description || art.title"
class="art-image"
>
@@ -46,11 +46,9 @@ import { Book, Comment, BookStatus, UpdateBookDto } from '@library/shared-types'
</div>
} @else if (book()) {
<div class="book-detail-card">
@if (book()!.coverImage) {
<div class="book-cover-section">
<img [src]="book()!.coverImage" [alt]="book()!.title" class="book-cover-large">
</div>
}
<div class="book-cover-section">
<img [src]="book()!.coverImage || '/assets/default-cover.jpg'" [alt]="book()!.title" class="book-cover-large">
</div>
<div class="book-content">
<div class="book-header">
@@ -643,11 +643,7 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
@for (book of paginatedBooks(); track book.id) {
<div class="book-card" [class.finished]="book.status === BookStatus.finished">
<a [routerLink]="['/books', book.id]" class="card-link">
@if (book.coverImage) {
<img [src]="book.coverImage" [alt]="book.title" class="book-cover">
} @else {
<div class="book-cover placeholder">📚</div>
}
<img [src]="book.coverImage || '/assets/default-cover.jpg'" [alt]="book.title" class="book-cover">
<div class="book-info">
<h3>{{ book.title }}</h3>
@@ -37,11 +37,9 @@ import { Game, Comment, GameStatus, UpdateGameDto } from '@library/shared-types'
</div>
} @else if (game()) {
<div class="game-detail-card">
@if (game()!.coverImage) {
<div class="game-cover-section">
<img [src]="game()!.coverImage" [alt]="game()!.title" class="game-cover-large">
</div>
}
<div class="game-cover-section">
<img [src]="game()!.coverImage || '/assets/default-cover.jpg'" [alt]="game()!.title" class="game-cover-large">
</div>
<div class="game-content">
<div class="game-header">
@@ -625,9 +625,7 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEnti
@for (game of paginatedGames(); track game.id) {
<div class="game-card" [class.completed]="game.status === GameStatus.completed">
<a [routerLink]="['/games', game.id]" class="card-link">
@if (game.coverImage) {
<img [src]="game.coverImage" [alt]="game.title" class="game-cover">
}
<img [src]="game.coverImage || '/assets/default-cover.jpg'" [alt]="game.title" class="game-cover">
<div class="game-info">
<h3>{{ game.title }}</h3>
@@ -46,11 +46,9 @@ import { Manga, Comment, MangaStatus, UpdateMangaDto } from '@library/shared-typ
</div>
} @else if (manga()) {
<div class="manga-detail-card">
@if (manga()!.coverImage) {
<div class="manga-cover-section">
<img [src]="manga()!.coverImage" [alt]="manga()!.title" class="manga-cover-large">
</div>
}
<div class="manga-cover-section">
<img [src]="manga()!.coverImage || '/assets/default-cover.jpg'" [alt]="manga()!.title" class="manga-cover-large">
</div>
<div class="manga-content">
<div class="manga-header">
@@ -562,9 +562,7 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
@for (manga of paginatedManga(); track manga.id) {
<div class="manga-card" [class.completed]="manga.status === MangaStatus.completed">
<a [routerLink]="['/manga', manga.id]" class="card-link">
@if (manga.coverImage) {
<img [src]="manga.coverImage" [alt]="manga.title" class="manga-cover">
}
<img [src]="manga.coverImage || '/assets/default-cover.jpg'" [alt]="manga.title" class="manga-cover">
<div class="manga-info">
<h3>{{ manga.title }}</h3>
@@ -46,11 +46,9 @@ import { Music, Comment, MusicStatus, MusicType, UpdateMusicDto } from '@library
</div>
} @else if (music()) {
<div class="music-detail-card">
@if (music()!.coverArt) {
<div class="music-cover-section">
<img [src]="music()!.coverArt" [alt]="music()!.title" class="music-cover-large">
</div>
}
<div class="music-cover-section">
<img [src]="music()!.coverArt || '/assets/default-cover.jpg'" [alt]="music()!.title" class="music-cover-large">
</div>
<div class="music-content">
<div class="music-header">
@@ -624,17 +624,7 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
@for (music of paginatedMusic(); track music.id) {
<div class="music-card" [class.completed]="music.status === MusicStatus.completed">
<a [routerLink]="['/music', music.id]" class="card-link">
@if (music.coverArt) {
<img [src]="music.coverArt" [alt]="music.title" class="music-cover">
} @else {
<div class="music-cover placeholder">
@switch (music.type) {
@case (MusicType.album) { 💿 }
@case (MusicType.single) { 🎵 }
@case (MusicType.ep) { 🎶 }
}
</div>
}
<img [src]="music.coverArt || '/assets/default-cover.jpg'" [alt]="music.title" class="music-cover">
<div class="music-info">
<h3>{{ music.title }}</h3>
@@ -46,11 +46,9 @@ import { Show, Comment, ShowStatus, ShowType, UpdateShowDto } from '@library/sha
</div>
} @else if (show()) {
<div class="show-detail-card">
@if (show()!.coverImage) {
<div class="show-poster-section">
<img [src]="show()!.coverImage" [alt]="show()!.title" class="show-poster-large">
</div>
}
<div class="show-poster-section">
<img [src]="show()!.coverImage || '/assets/default-cover.jpg'" [alt]="show()!.title" class="show-poster-large">
</div>
<div class="show-content">
<div class="show-header">
@@ -556,9 +556,7 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
@for (show of paginatedShows(); track show.id) {
<div class="show-card" [class.completed]="show.status === ShowStatus.completed">
<a [routerLink]="['/shows', show.id]" class="card-link">
@if (show.coverImage) {
<img [src]="show.coverImage" [alt]="show.title" class="show-cover">
}
<img [src]="show.coverImage || '/assets/default-cover.jpg'" [alt]="show.title" class="show-cover">
<div class="show-info">
<h3>{{ show.title }}</h3>