Initial commit

This commit is contained in:
2026-07-15 10:34:32 +02:00
committed by david.alvarez
commit 5861da89b3
38 changed files with 1711 additions and 0 deletions
@@ -0,0 +1,36 @@
<div class="bg-base-100 rounded-box border border-base-300 p-3 shadow-sm space-y-3">
<div class="flex flex-wrap items-center gap-3">
<div class="flex-1 min-w-0">
<div class="font-semibold truncate">{artist.name}</div>
<div class="text-xs text-base-content/60 truncate">
{#if artist.disambiguation??}{artist.disambiguation}{#else}Artist{/if}
{#if artist.areaName??}<span class="badge badge-ghost badge-xs ml-1">{artist.areaName}</span>{/if}
</div>
</div>
<button class="btn btn-ghost btn-xs text-error"
hx-delete="/artists/{artist.mbid}"
hx-target="closest div.space-y-3"
hx-swap="outerHTML"
hx-on::after-request="DiscDrop.refreshFeed()">
Unfollow
</button>
</div>
<div class="flex flex-wrap gap-2">
{#for s in settings}
<label class="label cursor-pointer gap-1 badge badge-outline py-2">
<input type="checkbox"
class="checkbox checkbox-xs"
value="true"
{#if s.enabled}checked{/if}
hx-post="/artists/{artist.mbid}/types"
hx-vals='{"primaryType":"{s.primaryType}"}'
hx-include="this"
hx-target="#feed-list"
hx-swap="innerHTML"
hx-on::after-request="DiscDrop.refreshFeed()"/>
<span class="text-xs">{s.primaryType}</span>
</label>
{/for}
</div>
</div>
@@ -0,0 +1,18 @@
{#for row in rows}
{#include fragments/feed-row.html row=row /}
{/for}
{#if hasMore}
<button id="load-more"
class="btn btn-outline btn-block mt-2"
hx-get="/feed?offset={nextOffset}"
hx-target="this"
hx-swap="outerHTML">
Load more
</button>
{/if}
{#if !hasMore && rows.empty}
<div class="text-center text-base-content/60 py-12">
<p class="text-lg">No releases yet.</p>
<p class="text-sm">Search for an artist above and click <strong>Follow</strong> to start tracking their release groups.</p>
</div>
{/if}
@@ -0,0 +1,22 @@
<div class="flex gap-3 items-center bg-base-100 rounded-box border border-base-300 p-3 shadow-sm">
<img src="https://coverartarchive.org/release-group/{row.mbid}/front"
alt="cover"
loading="lazy"
class="w-16 h-16 rounded object-cover bg-base-200"
onerror="this.onerror=null;this.src='/static/img/no-cover.svg'"/>
<div class="flex-1 min-w-0">
<div class="flex flex-wrap items-center gap-2">
<span class="font-semibold truncate">{row.artist.name}</span>
{#if row.primaryType??}<span class="badge badge-primary badge-sm">{row.primaryType}</span>{/if}
{#if row.secondaryTypes??}<span class="badge badge-ghost badge-sm">{row.secondaryTypes}</span>{/if}
</div>
<a href="{row.mbzUrl}" target="_blank" rel="noopener" class="link link-hover text-base-content/90 block truncate">
{row.title}
</a>
</div>
<div class="text-right text-sm text-base-content/70 shrink-0">
{#if row.firstReleaseDateRaw??}{row.firstReleaseDateRaw}{#else}—{/if}
</div>
</div>
@@ -0,0 +1,39 @@
{#if results.empty}
{#if query?? && query.length() >= 2}
<div class="p-3 text-sm text-base-content/60">No artists found for "{query}".</div>
{/if}
{/if}
{#for a in results}
<div class="flex items-center gap-2 p-2 hover:bg-base-200 rounded">
<div class="flex-1 min-w-0">
<div class="font-medium truncate">{a.name}</div>
<div class="text-xs text-base-content/60 truncate">
{#if a.disambiguation??}{a.disambiguation}{#else}Artist{/if}
{#if a.area?? && a.area.name??}<span class="badge badge-ghost badge-xs ml-1">{a.area.name}</span>{/if}
</div>
</div>
{#if followedMbids.contains(a.id)}
<form hx-delete="/artists/{a.id}"
hx-target="#feed-list"
hx-swap="innerHTML"
hx-on::after-request="DiscDrop.clearSearch()">
<button type="submit" class="btn btn-ghost btn-xs text-error">Unfollow</button>
</form>
{#else}
<form hx-post="/artists/follow"
hx-target="#feed-list"
hx-swap="innerHTML"
hx-on::after-request="DiscDrop.clearSearch()">
<input type="hidden" name="mbid" value="{a.id}"/>
<input type="hidden" name="name" value="{a.name}"/>
<input type="hidden" name="disambiguation" value="{a.disambiguation}"/>
<input type="hidden" name="type" value="{a.type}"/>
<input type="hidden" name="country" value="{a.country}"/>
{#if a.area?? && a.area.name??}
<input type="hidden" name="area" value="{a.area.name}"/>
{/if}
<button type="submit" class="btn btn-primary btn-xs">Follow</button>
</form>
{/if}
</div>
{/for}
@@ -0,0 +1,39 @@
<div class="space-y-3">
<h3 class="font-bold text-lg">Settings</h3>
{#if flash == "saved"}
<div class="alert alert-success py-1 text-sm">Saved</div>
{/if}
<form hx-post="/settings" hx-target="#settings-panel-host" hx-swap="innerHTML" class="space-y-3">
<div>
<div class="text-sm font-semibold mb-1">Default primary types for new artists</div>
<div class="flex flex-wrap gap-2">
{#for t in allPrimaryTypes}
<label class="label cursor-pointer gap-1 badge badge-outline py-2">
<input type="checkbox" name="defaultPrimaryTypes" value="{t}"
class="checkbox checkbox-xs"
{#if defaultPrimaryTypes.contains(t)}checked{/if}/>
<span class="text-xs">{t}</span>
</label>
{/for}
</div>
</div>
<div>
<div class="text-sm font-semibold mb-1">Feed update schedule</div>
<div class="flex gap-2">
{#for h in scheduleOptions}
<label class="label cursor-pointer gap-1">
<input type="radio" name="syncScheduleHours" value="{h}"
class="radio radio-sm"
{#if syncScheduleHours == h}checked{/if}/>
<span class="text-xs">{h}h</span>
</label>
{/for}
</div>
</div>
<button class="btn btn-primary btn-sm w-full">Save</button>
</form>
</div>