feat: category colour schemes, add stats to home page

This commit is contained in:
2026-02-04 18:32:55 -08:00
parent 054a55ff9c
commit 912a8887a5
7 changed files with 187 additions and 45 deletions
@@ -500,18 +500,18 @@ import { Art, CreateArtDto, UpdateArtDto, Comment } from '@library/shared-types'
} }
.btn-primary { .btn-primary {
background: var(--witch-rose); background: #fdcb6e;
color: var(--witch-moon); color: #333;
} }
.btn-secondary { .btn-secondary {
background: var(--witch-mauve); background: #6b7280;
color: var(--witch-purple); color: white;
} }
.btn-danger { .btn-danger {
background: var(--witch-plum); background: #ef4444;
color: var(--witch-moon); color: white;
} }
.btn-sm { .btn-sm {
@@ -482,8 +482,8 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment } from '@librar
} }
.filter-btn.active { .filter-btn.active {
background: var(--witch-plum); background: #8b6f47;
color: var(--witch-moon); color: white;
} }
.loading { .loading {
@@ -623,33 +623,33 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment } from '@librar
} }
.btn-primary { .btn-primary {
background: var(--witch-rose); background: #8b6f47;
color: var(--witch-moon); color: white;
} }
.btn-primary:hover { .btn-primary:hover {
background: var(--witch-plum); background: #725a3a;
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 4px 8px var(--witch-shadow); box-shadow: 0 4px 8px var(--witch-shadow);
} }
.btn-secondary { .btn-secondary {
background: var(--witch-mauve); background: #6b7280;
color: var(--witch-purple); color: white;
} }
.btn-secondary:hover { .btn-secondary:hover {
background: var(--witch-rose); background: #4b5563;
color: var(--witch-moon); color: white;
} }
.btn-danger { .btn-danger {
background: var(--witch-plum); background: #ef4444;
color: var(--witch-moon); color: white;
} }
.btn-danger:hover { .btn-danger:hover {
background: var(--witch-purple); background: #dc2626;
} }
.btn-sm { padding: 0.25rem 0.75rem; font-size: 0.85rem; } .btn-sm { padding: 0.25rem 0.75rem; font-size: 0.85rem; }
@@ -431,7 +431,7 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment } from '@librar
} }
.filter-btn.active { .filter-btn.active {
background: #3b82f6; background: #ff6b6b;
color: white; color: white;
} }
@@ -539,7 +539,7 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment } from '@librar
opacity: 0.8; opacity: 0.8;
} }
.btn-primary { background: #3b82f6; color: white; } .btn-primary { background: #ff6b6b; color: white; }
.btn-secondary { background: #6b7280; color: white; } .btn-secondary { background: #6b7280; color: white; }
.btn-danger { background: #ef4444; color: white; } .btn-danger { background: #ef4444; color: white; }
.btn-sm { padding: 0.25rem 0.75rem; font-size: 0.85rem; } .btn-sm { padding: 0.25rem 0.75rem; font-size: 0.85rem; }
@@ -10,7 +10,10 @@ import { RouterModule } from '@angular/router';
import { GamesService } from '../../services/games.service'; import { GamesService } from '../../services/games.service';
import { BooksService } from '../../services/books.service'; import { BooksService } from '../../services/books.service';
import { MusicService } from '../../services/music.service'; import { MusicService } from '../../services/music.service';
import { Game, GameStatus, Book, BookStatus, Music, MusicType } from '@library/shared-types'; import { MangaService } from '../../services/manga.service';
import { ShowsService } from '../../services/shows.service';
import { ArtService } from '../../services/art.service';
import { Game, GameStatus, Book, BookStatus, Music, MusicType, Manga, MangaStatus, Show, ShowStatus, ShowType, Art } from '@library/shared-types';
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
@@ -20,7 +23,7 @@ import { Game, GameStatus, Book, BookStatus, Music, MusicType } from '@library/s
<div class="container"> <div class="container">
<div class="hero"> <div class="hero">
<h1>Welcome to Naomi's Library</h1> <h1>Welcome to Naomi's Library</h1>
<p class="tagline">A personal collection of games, books, and music</p> <p class="tagline">A personal collection of games, books, music, manga, shows, and art</p>
</div> </div>
<div class="stats-grid"> <div class="stats-grid">
@@ -50,6 +53,33 @@ import { Game, GameStatus, Book, BookStatus, Music, MusicType } from '@library/s
<p>{{ albumsCount() }} albums, {{ singlesCount() }} singles</p> <p>{{ albumsCount() }} albums, {{ singlesCount() }} singles</p>
</div> </div>
</a> </a>
<a routerLink="/manga" class="stat-card manga-card">
<div class="icon">📖</div>
<div class="stat-info">
<h3>Manga</h3>
<div class="count">{{ mangaCount() }}</div>
<p>{{ currentlyReadingMangaCount() }} currently reading</p>
</div>
</a>
<a routerLink="/shows" class="stat-card shows-card">
<div class="icon">📺</div>
<div class="stat-info">
<h3>Shows</h3>
<div class="count">{{ showsCount() }}</div>
<p>{{ animeCount() }} anime, {{ filmsCount() }} films</p>
</div>
</a>
<a routerLink="/art" class="stat-card art-card">
<div class="icon">🎨</div>
<div class="stat-info">
<h3>Art</h3>
<div class="count">{{ artCount() }}</div>
<p>commissioned pieces</p>
</div>
</a>
</div> </div>
<div class="recent-section"> <div class="recent-section">
@@ -99,6 +129,48 @@ import { Game, GameStatus, Book, BookStatus, Music, MusicType } from '@library/s
</ul> </ul>
</div> </div>
} }
@if (recentManga().length > 0) {
<div class="recent-category">
<h3>📖 Latest Manga</h3>
<ul class="recent-list">
@for (manga of recentManga(); track manga.id) {
<li>
<a routerLink="/manga">{{ manga.title }}</a>
<span class="author">by {{ manga.author }}</span>
</li>
}
</ul>
</div>
}
@if (recentShows().length > 0) {
<div class="recent-category">
<h3>📺 Latest Shows</h3>
<ul class="recent-list">
@for (show of recentShows(); track show.id) {
<li>
<a routerLink="/shows">{{ show.title }}</a>
<span class="show-type">{{ formatShowType(show.type) }}</span>
</li>
}
</ul>
</div>
}
@if (recentArt().length > 0) {
<div class="recent-category">
<h3>🎨 Latest Art</h3>
<ul class="recent-list">
@for (art of recentArt(); track art.id) {
<li>
<a routerLink="/art">{{ art.title }}</a>
<span class="artist">by {{ art.artist }}</span>
</li>
}
</ul>
</div>
}
</div> </div>
</div> </div>
</div> </div>
@@ -154,9 +226,12 @@ import { Game, GameStatus, Book, BookStatus, Music, MusicType } from '@library/s
background: var(--witch-moon); background: var(--witch-moon);
} }
.games-card:hover { border-color: var(--witch-rose); } .games-card:hover { border-color: #ff6b6b; }
.books-card:hover { border-color: var(--witch-plum); } .books-card:hover { border-color: #8b6f47; }
.music-card:hover { border-color: var(--witch-purple); } .music-card:hover { border-color: #74b9ff; }
.manga-card:hover { border-color: #00b894; }
.shows-card:hover { border-color: #e84393; }
.art-card:hover { border-color: #fdcb6e; }
.icon { .icon {
font-size: 3rem; font-size: 3rem;
@@ -176,9 +251,12 @@ import { Game, GameStatus, Book, BookStatus, Music, MusicType } from '@library/s
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.games-card .count { color: var(--witch-rose); } .games-card .count { color: #ff6b6b; }
.books-card .count { color: var(--witch-plum); } .books-card .count { color: #8b6f47; }
.music-card .count { color: var(--witch-purple); } .music-card .count { color: #74b9ff; }
.manga-card .count { color: #00b894; }
.shows-card .count { color: #e84393; }
.art-card .count { color: #fdcb6e; }
.stat-info p { .stat-info p {
margin: 0; margin: 0;
@@ -239,7 +317,8 @@ import { Game, GameStatus, Book, BookStatus, Music, MusicType } from '@library/s
.platform, .platform,
.author, .author,
.artist { .artist,
.show-type {
display: block; display: block;
font-size: 0.85rem; font-size: 0.85rem;
color: var(--witch-plum); color: var(--witch-plum);
@@ -251,16 +330,25 @@ export class HomeComponent implements OnInit {
gamesService = inject(GamesService); gamesService = inject(GamesService);
booksService = inject(BooksService); booksService = inject(BooksService);
musicService = inject(MusicService); musicService = inject(MusicService);
mangaService = inject(MangaService);
showsService = inject(ShowsService);
artService = inject(ArtService);
games = signal<Game[]>([]); games = signal<Game[]>([]);
books = signal<Book[]>([]); books = signal<Book[]>([]);
music = signal<Music[]>([]); music = signal<Music[]>([]);
manga = signal<Manga[]>([]);
shows = signal<Show[]>([]);
art = signal<Art[]>([]);
ngOnInit() { ngOnInit() {
// Load all data // Load all data
this.gamesService.getAllGames().subscribe(games => this.games.set(games)); this.gamesService.getAllGames().subscribe(games => this.games.set(games));
this.booksService.getAllBooks().subscribe(books => this.books.set(books)); this.booksService.getAllBooks().subscribe(books => this.books.set(books));
this.musicService.getAllMusic().subscribe(music => this.music.set(music)); this.musicService.getAllMusic().subscribe(music => this.music.set(music));
this.mangaService.getAllManga().subscribe(manga => this.manga.set(manga));
this.showsService.getAllShows().subscribe(shows => this.shows.set(shows));
this.artService.getAllArt().subscribe(art => this.art.set(art));
} }
// Games stats // Games stats
@@ -305,4 +393,58 @@ export class HomeComponent implements OnInit {
recentMusic() { recentMusic() {
return this.music().slice(0, 5); return this.music().slice(0, 5);
} }
// Manga stats
mangaCount() {
return this.manga().length;
}
currentlyReadingMangaCount() {
return this.manga().filter(m => m.status === MangaStatus.reading).length;
}
recentManga() {
return this.manga().slice(0, 5);
}
// Shows stats
showsCount() {
return this.shows().length;
}
animeCount() {
return this.shows().filter(s => s.type === ShowType.anime).length;
}
filmsCount() {
return this.shows().filter(s => s.type === ShowType.film).length;
}
recentShows() {
return this.shows().slice(0, 5);
}
formatShowType(type: ShowType): string {
switch (type) {
case ShowType.tvSeries:
return 'TV Series';
case ShowType.anime:
return 'Anime';
case ShowType.film:
return 'Film';
case ShowType.documentary:
return 'Documentary';
default:
return type;
}
}
// Art stats
artCount() {
return this.art().length;
}
recentArt() {
return this.art().slice(0, 5);
}
} }
@@ -432,7 +432,7 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment } from '@li
} }
.filter-btn.active { .filter-btn.active {
background: #ec4899; background: #00b894;
color: white; color: white;
} }
@@ -541,7 +541,7 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment } from '@li
opacity: 0.8; opacity: 0.8;
} }
.btn-primary { background: #ec4899; color: white; } .btn-primary { background: #00b894; color: white; }
.btn-secondary { background: #6b7280; color: white; } .btn-secondary { background: #6b7280; color: white; }
.btn-danger { background: #ef4444; color: white; } .btn-danger { background: #ef4444; color: white; }
.btn-sm { padding: 0.25rem 0.75rem; font-size: 0.85rem; } .btn-sm { padding: 0.25rem 0.75rem; font-size: 0.85rem; }
@@ -533,8 +533,8 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment
} }
.filter-btn.active { .filter-btn.active {
background: var(--witch-rose); background: #74b9ff;
color: var(--witch-moon); color: white;
} }
.loading { .loading {
@@ -695,33 +695,33 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment
} }
.btn-primary { .btn-primary {
background: var(--witch-rose); background: #74b9ff;
color: var(--witch-moon); color: white;
} }
.btn-primary:hover { .btn-primary:hover {
background: var(--witch-plum); background: #4a9ff5;
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 4px 8px var(--witch-shadow); box-shadow: 0 4px 8px var(--witch-shadow);
} }
.btn-secondary { .btn-secondary {
background: var(--witch-mauve); background: #6b7280;
color: var(--witch-purple); color: white;
} }
.btn-secondary:hover { .btn-secondary:hover {
background: var(--witch-rose); background: #4b5563;
color: var(--witch-moon); color: white;
} }
.btn-danger { .btn-danger {
background: var(--witch-plum); background: #ef4444;
color: var(--witch-moon); color: white;
} }
.btn-danger:hover { .btn-danger:hover {
background: var(--witch-purple); background: #dc2626;
} }
.btn-sm { .btn-sm {
@@ -428,7 +428,7 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment } fro
} }
.filter-btn.active { .filter-btn.active {
background: #8b5cf6; background: #e84393;
color: white; color: white;
} }
@@ -536,7 +536,7 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment } fro
opacity: 0.8; opacity: 0.8;
} }
.btn-primary { background: #8b5cf6; color: white; } .btn-primary { background: #e84393; color: white; }
.btn-secondary { background: #6b7280; color: white; } .btn-secondary { background: #6b7280; color: white; }
.btn-danger { background: #ef4444; color: white; } .btn-danger { background: #ef4444; color: white; }
.btn-sm { padding: 0.25rem 0.75rem; font-size: 0.85rem; } .btn-sm { padding: 0.25rem 0.75rem; font-size: 0.85rem; }