Skip to content

Commit

Permalink
Feat/better status page (#170)
Browse files Browse the repository at this point in the history
* feat: status page improvements

* feat: status page improvements

* feat: status page improvements
  • Loading branch information
AyushSehrawat authored Jan 26, 2024
1 parent ff3cb20 commit 543e4fe
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 47 deletions.
33 changes: 33 additions & 0 deletions frontend/src/routes/api/items/[id]/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { json } from '@sveltejs/kit';

export const GET = async ({ fetch, params }) => {
const id: number = Number(params.id);
console.log(`Fetching extended data of item ${id} from backend`);

async function getExtendedData() {
try {
const res = await fetch(`http://127.0.0.1:8080/items/extended/${id}`);
if (res.ok) {
return await res.json();
}
return {
status: res.status,
statusText: res.statusText
};
} catch (e) {
console.error(e);
return {
status: 503,
statusText: 'Unable to fetch extended data. API is down.'
};
}
}

const data = await getExtendedData();

return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json'
}
});
};
172 changes: 125 additions & 47 deletions frontend/src/routes/status/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { Button } from '$lib/components/ui/button';
import { Badge } from '$lib/components/ui/badge';
import * as Tooltip from '$lib/components/ui/tooltip';
import * as Dialog from '$lib/components/ui/dialog';
import { Loader2, ArrowUpRight, RotateCw, MoveUpRight } from 'lucide-svelte';
import { toast } from 'svelte-sonner';
import type { StatusInfo } from '$lib/types';
Expand Down Expand Up @@ -47,6 +48,18 @@
description: 'Items which are in your plex library but are missing some files'
}
};
let extendedDataLoading = false;
let extendedItem: any;
async function getExtendedData(id: number) {
extendedDataLoading = true;
const res = await fetch(`/api/items/${id}`);
const data = await res.json();
console.log(data);
extendedItem = data.item;
extendedDataLoading = false;
}
</script>

<svelte:head>
Expand Down Expand Up @@ -110,56 +123,121 @@

{@const icebergItems = convertIcebergItemsToObject(items.items)}
<div class="flex flex-col gap-12 mt-4 w-full">
{#each Object.keys(icebergItems) as key (key)}
<Carousel.Root opts={{ dragFree: true }} class="w-full max-w-full flex flex-col gap-4">
<div class="flex items-center justify-between">
<div class="flex flex-col">
<a href="/status/type/{key}" class="flex gap-1 items-center hover:underline">
<h3 class="text-xl md:text-2xl font-semibold">
{statusInfo[key].text ?? formatWords(key)}
</h3>
<MoveUpRight class="size-4 md:size-6" />
</a>
<p class="text-muted-foreground text-sm">{statusInfo[key].description}</p>
{#if Object.keys(icebergItems).length === 0}
<div class="flex flex-col w-full h-full font-primary">
<h3 class="text-xl font-semibold">No items found :(</h3>
<p class="text-sm text-muted-foreground">
You can request items from the content services configured.
</p>
</div>
{:else}
{#each Object.keys(icebergItems) as key (key)}
<Carousel.Root opts={{ dragFree: true }} class="w-full max-w-full flex flex-col gap-4">
<div class="flex items-center justify-between">
<div class="flex flex-col">
<a href="/status/type/{key}" class="flex gap-1 items-center hover:underline">
<h3 class="text-xl md:text-2xl font-semibold">
{statusInfo[key].text ?? formatWords(key)}
</h3>
<MoveUpRight class="size-4 md:size-6" />
</a>
<p class="text-muted-foreground text-sm">{statusInfo[key].description}</p>
</div>
<div class="flex items-center justify-center gap-2 mt-6">
<Carousel.Previous class="rounded-md static h-8 w-8" />
<Carousel.Next class="rounded-md static h-8 w-8" />
</div>
</div>
<div class="flex items-center justify-center gap-2 mt-6">
<Carousel.Previous class="rounded-md static h-8 w-8" />
<Carousel.Next class="rounded-md static h-8 w-8" />
</div>
</div>
<Carousel.Content class="flex flex-row h-full w-full">
{#each icebergItems[key] as item}
<Carousel.Item class="flex-none mr-2 min-w-0 max-w-max w-full h-full group/item">
<div class="flex flex-col w-full h-full max-w-[144px] md:max-w-[176px]">
<div class="relative h-full w-full">
<img
alt={item.imdb_id}
class="bg-cover bg-center h-[216px] w-[144px] md:w-[176px] md:h-[264px] rounded-md border-muted group-hover/item:scale-105 duration-300 transition-all ease-in-out"
src={`https://images.metahub.space/poster/small/${item.imdb_id}/img`}
/>
<div class="absolute top-2 left-2">
<Badge class="rounded-md bg-opacity-40 backdrop-blur-lg drop-shadow-lg">
{item.type === 'movie' ? 'Movie' : 'TV Show'}
</Badge>
<Carousel.Content class="flex flex-row h-full w-full">
{#each icebergItems[key] as item}
<Carousel.Item class="flex-none mr-2 min-w-0 max-w-max w-full h-full group/item">
<div class="flex flex-col w-full h-full max-w-[144px] md:max-w-[176px]">
<div class="relative h-full w-full">
<img
alt={item.imdb_id}
class="bg-cover bg-center h-[216px] w-[144px] md:w-[176px] md:h-[264px] rounded-md border-muted group-hover/item:scale-105 duration-300 transition-all ease-in-out"
src={`https://images.metahub.space/poster/small/${item.imdb_id}/img`}
/>
<div class="absolute top-2 left-2">
<Badge class="rounded-md bg-opacity-40 backdrop-blur-lg drop-shadow-lg">
{item.type === 'movie' ? 'Movie' : 'TV Show'}
</Badge>
</div>
</div>

<Dialog.Root
onOpenChange={async (open) => {
console.log(open);
if (open) {
await getExtendedData(item.item_id);
}
}}
>
<Dialog.Trigger>
<p
class="text-start text-sm mt-2 text-ellipsis line-clamp-1 group-hover/item:underline focus:underline"
>
{item.title}
</p>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>{item.title}</Dialog.Title>
<Dialog.Description class="flex flex-col gap-2">
<p class="text-muted-foreground text-sm">
Aired {formatDate(item.aired_at, 'short')}
</p>
<div
class="flex flex-wrap gap-2 w-full items-center justify-center md:justify-start"
>
{#each item.genres as genre}
<Badge variant="secondary">
{formatWords(genre)}
</Badge>
{/each}
</div>
</Dialog.Description>
</Dialog.Header>
{#if extendedDataLoading}
<div class="flex items-center gap-1 w-full justify-center">
<Loader2 class="animate-spin w-4 h-4" />
<p class="text-muted-foreground">Loading item data...</p>
</div>
{:else}
<div class="flex flex-col items-start">
<p>
The item was requested <span class="font-semibold"
>{formatDate(item.requested_at, 'long', true)}</span
>
by <span class="font-semibold">{item.requested_by}</span>.
</p>
{#if item.scraped_at}
<p>
Last scraped <span class="font-semibold"
>{formatDate(item.scraped_at, 'long', true)}</span
>
for a total of
<span class="font-semibold">{item.scraped_times}</span>
times.
</p>
{/if}
</div>
{/if}
</Dialog.Content>
</Dialog.Root>
<p class="text-muted-foreground text-xs mt-1">
{formatDate(item.aired_at, 'year')}
</p>
<p class="text-muted-foreground text-xs mt-1">
Requested {formatDate(item.requested_at, 'long', true)}
</p>
</div>
<p
class="text-start text-sm mt-2 text-ellipsis line-clamp-1 group-hover/item:underline focus:underline"
>
{item.title}
</p>
<p class="text-muted-foreground text-xs mt-1">
{formatDate(item.aired_at, 'year')}
</p>
<p class="text-muted-foreground text-xs mt-1">
Requested {formatDate(item.requested_at, 'long', true)}
</p>
</div>
</Carousel.Item>
{/each}
</Carousel.Content>
</Carousel.Root>
{/each}
</Carousel.Item>
{/each}
</Carousel.Content>
</Carousel.Root>
{/each}
{/if}
</div>
{:catch error}
<div class="flex flex-col items-center justify-center w-full h-full font-primary">
Expand Down

0 comments on commit 543e4fe

Please sign in to comment.