feat(web-player): add the albums pages

A grid of your albums with a create dialog, and a detail page with the
tracklist, inline editing and a picker that lists the songs you uploaded so
they can be dropped into the album. Covers reuse the playlist mosaic since an
album has no artwork of its own yet.
This commit is contained in:
2026-07-20 20:21:22 +02:00
parent 9cd916c098
commit 76812d1eeb
13 changed files with 764 additions and 0 deletions
@@ -0,0 +1,76 @@
<script lang="ts">
import Plus from '@lucide/svelte/icons/plus';
import Disc from '@lucide/svelte/icons/disc-3';
import Page from '$lib/components/ui/Page.svelte';
import PageHeader from '$lib/components/ui/PageHeader.svelte';
import MediaGrid from '$lib/components/ui/MediaGrid.svelte';
import AlbumCard from '$lib/components/ui/AlbumCard.svelte';
import AlbumForm from '$lib/components/ui/AlbumForm.svelte';
import Modal from '$lib/components/ui/Modal.svelte';
import EmptyState from '$lib/components/ui/EmptyState.svelte';
import Button from '$lib/components/ui/Button.svelte';
let { data, form } = $props();
const items = $derived(data.albums.items);
const trackIds = $derived(data.trackIds);
let creating = $state(false);
</script>
<svelte:head>
<title>Álbumes · Musify</title>
<meta name="description" content="Los álbumes que has publicado con tu música subida." />
</svelte:head>
<Page>
<PageHeader title="Álbumes" subtitle="Agrupa la música que subes en discos.">
{#snippet actions()}
{#if items.length > 0}
<Button onclick={() => (creating = true)}>
<Plus class="h-4 w-4" strokeWidth={2.5} />
Nuevo álbum
</Button>
{/if}
{/snippet}
</PageHeader>
{#if items.length > 0}
<MediaGrid class="mt-9">
{#each items as album, i (album.id)}
<AlbumCard
id={album.id}
title={album.title}
releaseYear={album.releaseYear === null ? undefined : Number(album.releaseYear)}
trackCount={Number(album.trackCount)}
trackIds={trackIds[album.id] ?? []}
index={i}
/>
{/each}
</MediaGrid>
{:else}
<EmptyState
icon={Disc}
title="Todavía no tienes álbumes"
description="Crea el primero y añádele las canciones que hayas subido."
>
{#snippet actions()}
<Button onclick={() => (creating = true)}>
<Plus class="h-4 w-4" strokeWidth={2} />
Crear álbum
</Button>
{/snippet}
</EmptyState>
{/if}
</Page>
<Modal open={creating} onClose={() => (creating = false)} title="Nuevo álbum">
<AlbumForm
action="?/create"
titlePlaceholder="Mi primer disco"
formMessage={form?.message}
submitLabel="Crear"
submittingLabel="Creando…"
onCancel={() => (creating = false)}
/>
</Modal>