diff --git a/web/src/lib/components/download-season-dialog.svelte b/web/src/lib/components/download-season-dialog.svelte index e4aa01f..8365312 100644 --- a/web/src/lib/components/download-season-dialog.svelte +++ b/web/src/lib/components/download-season-dialog.svelte @@ -7,9 +7,14 @@ import type {PublicIndexerQueryResult} from '$lib/types.js'; import {convertTorrentSeasonRangeToIntegerRange, getFullyQualifiedShowName} from '$lib/utils'; + import {LoaderCircle} from "lucide-svelte"; + import * as Dialog from '$lib/components/ui/dialog/index.js'; + import * as Tabs from '$lib/components/ui/tabs/index.js'; + import * as Select from '$lib/components/ui/select/index.js'; + import * as Table from '$lib/components/ui/table/index.js'; let {show} = $props(); - + let dialogueState = $state(false); let selectedSeasonNumber: number = $state(1); let torrents: PublicIndexerQueryResult[] = $state([]); let isLoadingTorrents: boolean = $state(false); @@ -83,24 +88,27 @@ const errorMessage = `Failed to fetch torrents for show ${show.id} and season ${selectedSeasonNumber}: ${response.statusText}`; console.error(errorMessage); torrentsError = errorMessage; - toast.error(errorMessage); + if (dialogueState) + toast.error(errorMessage); return []; } const data: PublicIndexerQueryResult[] = await response.json(); console.log('Fetched torrents:', data); - if (data.length > 0) { - toast.success(`Found ${data.length} torrents.`); - } else { - toast.info('No torrents found for your query.'); + if (dialogueState) { + if (data.length > 0) { + toast.success(`Found ${data.length} torrents.`); + } else { + toast.info('No torrents found for your query.'); + } } - return data; } catch (err) { const errorMessage = `Error fetching torrents: ${err instanceof Error ? err.message : 'An unknown error occurred'}`; console.error(errorMessage); torrentsError = errorMessage; - toast.error(errorMessage); + if (dialogueState) + toast.error(errorMessage); return []; } finally { isLoadingTorrents = false; @@ -128,7 +136,7 @@ : ' - ' + filePathSuffix}.mkv {/snippet} - + Download Seasons @@ -283,6 +291,7 @@ {/each} + {torrent.seasons} {convertTorrentSeasonRangeToIntegerRange(torrent)} diff --git a/web/src/lib/components/login-form.svelte b/web/src/lib/components/login-form.svelte index 6fd9c76..a56d91c 100644 --- a/web/src/lib/components/login-form.svelte +++ b/web/src/lib/components/login-form.svelte @@ -6,6 +6,7 @@ import {goto} from '$app/navigation'; import {env} from '$env/dynamic/public'; import * as Tabs from "$lib/components/ui/tabs/index.js"; + import {toast} from 'svelte-sonner'; let apiUrl = env.PUBLIC_API_URL; @@ -37,8 +38,9 @@ if (response.ok) { console.log('Login successful!'); console.log('Received User Data: ', response); - goto('/dashboard'); errorMessage = 'Login successful! Redirecting...'; + toast.success(errorMessage); + goto('/dashboard'); } else { let errorText = await response.text(); try { @@ -47,11 +49,13 @@ } catch { errorMessage = errorText || 'Login failed. Please check your credentials.'; } + toast.error(errorMessage); console.error('Login failed:', response.status, errorText); } } catch (error) { console.error('Login request failed:', error); errorMessage = 'An error occurred during the login request.'; + toast.error(errorMessage); } finally { isLoading = false; } @@ -81,7 +85,8 @@ console.log('Registration successful!'); console.log('Received User Data: ', response); tabValue = "login"; // Switch to login tab after successful registration - errorMessage = 'Registration successful! Redirecting...'; + errorMessage = 'Registration successful! Please login.'; + toast.success(errorMessage); } else { let errorText = await response.text(); try { @@ -90,11 +95,13 @@ } catch { errorMessage = errorText || 'Registration failed. Please check your credentials.'; } + toast.error(errorMessage); console.error('Registration failed:', response.status, errorText); } } catch (error) { console.error('Registration request failed:', error); errorMessage = 'An error occurred during the Registration request.'; + toast.error(errorMessage); } finally { isLoading = false; } diff --git a/web/src/lib/components/season-requests-table.svelte b/web/src/lib/components/season-requests-table.svelte index 51cad49..e21b58c 100644 --- a/web/src/lib/components/season-requests-table.svelte +++ b/web/src/lib/components/season-requests-table.svelte @@ -35,13 +35,15 @@ requests[requestIndex].authorized = !currentAuthorizedStatus; requests[requestIndex].authorized_by = user(); } + toast.success(`Request ${!currentAuthorizedStatus ? 'approved' : 'unapproved'} successfully.`); } else { - console.error(`Failed to update request status ${response.statusText}`, await response.text()); - // Optionally, add user-facing error handling here + const errorText = await response.text(); + console.error(`Failed to update request status ${response.statusText}`, errorText); + toast.error(`Failed to update request status: ${response.statusText}`); } } catch (error) { console.error('Error updating request status:', error); - // Optionally, add user-facing error handling here + toast.error('Error updating request status: ' + (error instanceof Error ? error.message : String(error))); } } diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index 59f1fad..e7b36d9 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -41,8 +41,8 @@ export function getFullyQualifiedShowName(show: { name: string; year: number }): } export function convertTorrentSeasonRangeToIntegerRange(torrent: any): string { - if (torrent.seasons.length === 1) return torrent.seasons[0]?.toString(); - if (torrent.seasons.length >= 2) return torrent.seasons[0]?.toString() + "-" + torrent.seasons.at(-1).toString(); + if (torrent.season?.length === 1) return torrent.season[0]?.toString(); + if (torrent.season?.length >= 2) return torrent.season[0]?.toString() + "-" + torrent.season.at(-1).toString(); else { console.log("Error parsing season range: " + torrent.seasons); return "Error parsing season range: " + torrent.seasons; @@ -50,7 +50,7 @@ export function convertTorrentSeasonRangeToIntegerRange(torrent: any): string { } -export async function handleLogout(): null { +export async function handleLogout() { const response = await fetch(apiUrl + '/auth/cookie/logout', { method: 'POST', credentials: 'include' @@ -63,5 +63,4 @@ export async function handleLogout(): null { console.error('Logout failed:', response.status); toast.error('Logout failed: ' + response.status); } -} - +} \ No newline at end of file diff --git a/web/src/routes/dashboard/+layout.svelte b/web/src/routes/dashboard/+layout.svelte index d9e1ff9..aa12dc9 100644 --- a/web/src/routes/dashboard/+layout.svelte +++ b/web/src/routes/dashboard/+layout.svelte @@ -5,10 +5,12 @@ import {setContext} from 'svelte'; import {goto} from '$app/navigation'; import {base} from "$app/paths"; + import {toast} from "svelte-sonner"; let {data, children}: LayoutProps = $props(); console.log('Received User Data: ', data.user); if (!data.user.is_verified) { + toast.info("Your account requires verification. Redirecting..."); goto(base + '/login/verify') } setContext('user', () => data.user); diff --git a/web/src/routes/dashboard/tv/[showId=uuid]/+page.svelte b/web/src/routes/dashboard/tv/[showId=uuid]/+page.svelte index 071d9af..c218425 100644 --- a/web/src/routes/dashboard/tv/[showId=uuid]/+page.svelte +++ b/web/src/routes/dashboard/tv/[showId=uuid]/+page.svelte @@ -71,9 +71,9 @@ {show().overview}

-
+
{#if user().is_superuser} - + {/if}
diff --git a/web/src/routes/dashboard/tv/add-show/+page.svelte b/web/src/routes/dashboard/tv/add-show/+page.svelte index 22d7d20..7142835 100644 --- a/web/src/routes/dashboard/tv/add-show/+page.svelte +++ b/web/src/routes/dashboard/tv/add-show/+page.svelte @@ -14,6 +14,7 @@ import {goto} from '$app/navigation'; import {base} from '$app/paths'; import AddShowCard from '$lib/components/add-show-card.svelte'; + import {toast} from 'svelte-sonner'; let searchTerm: string = $state(''); let metadataProvider: string = $state('tmdb'); @@ -26,12 +27,30 @@ let url = new URL(env.PUBLIC_API_URL + '/tv/search'); url.searchParams.append('query', searchTerm); url.searchParams.append('metadata_provider', metadataProvider); - const response = await fetch(url, { - method: 'GET', - credentials: 'include' - }); - results = await response.json(); + toast.info(`Searching for "${searchTerm}" using ${metadataProvider.toUpperCase()}...`); + try { + const response = await fetch(url, { + method: 'GET', + credentials: 'include' + }); + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Search failed: ${response.status} ${errorText || response.statusText}`); + } + results = await response.json(); + if (results && results.length > 0) { + toast.success(`Found ${results.length} result(s) for "${searchTerm}".`); + } else { + toast.info(`No results found for "${searchTerm}".`); + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred during search.'; + console.error('Search error:', error); + toast.error(errorMessage); + results = null; // Clear previous results on error + } } else { + toast.warning('Please enter a search term.'); results = null; } }