Files
minstrel/web/src/lib/components/FlagPopover.svelte
T

103 lines
3.3 KiB
Svelte

<script lang="ts">
import { untrack } from 'svelte';
import { Flag } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import { flagTrack } from '$lib/api/quarantine';
import { errCode } from '$lib/api/errors';
import { qk } from '$lib/api/queries';
import type { TrackRef, LidarrQuarantineReason } from '$lib/api/types';
let {
track,
onClose,
initialReason,
initialNotes
}: {
track: TrackRef;
onClose: () => void;
initialReason?: LidarrQuarantineReason;
initialNotes?: string;
} = $props();
let reason: LidarrQuarantineReason = $state(untrack(() => initialReason ?? 'bad_rip'));
let notes = $state(untrack(() => initialNotes ?? ''));
let submitting = $state(false);
let error = $state<string | null>(null);
const isUpdate = $derived(!!initialReason);
const client = useQueryClient();
async function submit() {
submitting = true;
error = null;
try {
await flagTrack({ track_id: track.id, reason, notes: notes.trim() || undefined });
await client.invalidateQueries({ queryKey: qk.myQuarantine() });
onClose();
} catch (e) {
const code = errCode(e);
error = code === 'unknown' ? 'flag_failed' : code;
} finally {
submitting = false;
}
}
</script>
<div
role="dialog"
tabindex="-1"
aria-modal="false"
aria-labelledby="flag-popover-title"
class="absolute right-0 z-30 mt-1 w-72 rounded-lg border border-border bg-surface p-3 shadow-xl"
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => { e.stopPropagation(); if (e.key === 'Escape') onClose(); }}
>
<h4 id="flag-popover-title" class="text-sm font-medium text-text-primary">
Flag this track as broken
</h4>
<label class="mt-2 block">
<span class="block text-xs text-text-secondary">Reason</span>
<select
bind:value={reason}
class="mt-1 w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-accent"
>
<option value="bad_rip">Bad rip</option>
<option value="wrong_file">Wrong file</option>
<option value="wrong_tags">Wrong tags</option>
<option value="duplicate">Duplicate</option>
<option value="other">Other</option>
</select>
</label>
<label class="mt-2 block">
<span class="block text-xs text-text-secondary">Notes (optional)</span>
<textarea
bind:value={notes}
maxlength="200"
placeholder="What's wrong with it?"
class="mt-1 w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
rows="2"
></textarea>
</label>
{#if error}
<p class="mt-2 text-xs text-error">Couldn't save flag — {error}</p>
{/if}
<div class="mt-3 flex justify-end gap-2">
<button
type="button"
class="rounded-md border border-border px-2.5 py-1 text-sm text-text-secondary hover:text-text-primary"
onclick={onClose}
>
Cancel
</button>
<button
type="button"
onclick={submit}
disabled={submitting}
class="inline-flex items-center gap-1 rounded-md bg-action-secondary px-2.5 py-1 text-sm text-action-fg disabled:opacity-50"
>
<Flag size={14} strokeWidth={1} />
{isUpdate ? 'Update flag' : 'Flag'}
</button>
</div>
</div>