feat: base64 uploads, reusable forms, Discord roles, and UX improvements #66

Merged
naomi merged 8 commits from fix/base64 into main 2026-02-20 20:32:52 -08:00
13 changed files with 24 additions and 56 deletions
Showing only changes of commit d74a342f63 - Show all commits
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>