Should be mostly working, doing some additional QOL

This commit is contained in:
gamer147
2025-11-30 23:00:40 -05:00
parent 8d6f0d6cfd
commit b2f4548807
24 changed files with 839 additions and 73 deletions

View File

@@ -1,15 +1,39 @@
<script lang="ts" module>
import type { NovelsQuery } from '$lib/graphql/__generated__/graphql';
import type { NovelsQuery, NovelStatus } from '$lib/graphql/__generated__/graphql';
export type NovelNode = NonNullable<NonNullable<NovelsQuery['novels']>['edges']>[number]['node'];
export interface NovelCardProps {
novel: NovelNode;
}
const statusColors: Record<NovelStatus, string> = {
IN_PROGRESS: 'bg-green-500 text-white',
COMPLETED: 'bg-blue-500 text-white',
HIATUS: 'bg-amber-500 text-white',
ABANDONED: 'bg-gray-500 text-white',
UNKNOWN: 'bg-gray-500 text-white'
};
const statusLabels: Record<NovelStatus, string> = {
IN_PROGRESS: 'Ongoing',
COMPLETED: 'Complete',
HIATUS: 'Hiatus',
ABANDONED: 'Dropped',
UNKNOWN: 'Unknown'
};
</script>
<script lang="ts">
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
import { Badge } from '$lib/components/ui/badge';
import {
Tooltip,
TooltipTrigger,
TooltipContent,
TooltipProvider
} from '$lib/components/ui/tooltip';
import { formatRelativeTime, formatAbsoluteTime } from '$lib/utils/time';
let { novel }: NovelCardProps = $props();
@@ -22,24 +46,72 @@
const title = $derived(pickText(novel.name));
const description = $derived(pickText(novel.description));
const coverSrc = $derived(novel.coverImage?.newPath ?? novel.coverImage?.originalPath);
const latestChapter = $derived(
novel.chapters?.slice().sort((a, b) => b.order - a.order)[0] ?? null
);
const chapterDisplay = $derived(latestChapter ? `Ch. ${latestChapter.order}` : null);
const lastUpdated = $derived(novel.lastUpdatedTime ? new Date(novel.lastUpdatedTime) : null);
const relativeTime = $derived(lastUpdated ? formatRelativeTime(lastUpdated) : null);
const absoluteTime = $derived(lastUpdated ? formatAbsoluteTime(lastUpdated) : null);
const status = $derived(novel.rawStatus ?? 'UNKNOWN');
const statusColor = $derived(statusColors[status]);
const statusLabel = $derived(statusLabels[status]);
</script>
<Card class="overflow-hidden border shadow-sm transition-shadow hover:shadow-md">
{#if coverSrc}
<div class="aspect-[3/4] w-full overflow-hidden bg-muted/50">
<img src={coverSrc} alt={title} class="h-full w-full object-cover" loading="lazy" />
<a
href="/novels/{novel.id}"
class="block focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-lg"
>
<Card class="overflow-hidden border shadow-sm transition-shadow hover:shadow-md h-full pt-0 gap-0">
<div class="relative">
{#if coverSrc}
<div class="aspect-[3/4] w-full overflow-hidden bg-muted/50">
<img src={coverSrc} alt={title} class="h-full w-full object-cover" loading="lazy" />
</div>
{:else}
<div class="aspect-[3/4] w-full bg-muted/50"></div>
{/if}
<Badge
class="absolute top-2 right-2 {statusColor} shadow-sm"
aria-label="Status: {statusLabel}"
>
{statusLabel}
</Badge>
</div>
{:else}
<div class="aspect-[3/4] w-full bg-muted/50"></div>
{/if}
<CardHeader class="space-y-2">
<CardTitle class="line-clamp-2 text-lg leading-tight">
{title}
</CardTitle>
</CardHeader>
<CardContent class="pt-0">
<p class="line-clamp-3 text-sm text-muted-foreground">
{description}
</p>
</CardContent>
</Card>
<CardHeader class="space-y-2 pt-4">
<CardTitle class="line-clamp-2 text-lg leading-tight" title={title}>
{title}
</CardTitle>
</CardHeader>
<CardContent class="pt-0 pb-4 space-y-3">
<p class="line-clamp-3 text-sm text-muted-foreground" title={description}>
{description}
</p>
{#if chapterDisplay || relativeTime}
<div class="flex items-center gap-1 text-xs text-muted-foreground/80">
{#if chapterDisplay}
<span>{chapterDisplay}</span>
{/if}
{#if chapterDisplay && relativeTime}
<span aria-hidden="true">·</span>
{/if}
{#if relativeTime && absoluteTime}
<TooltipProvider>
<Tooltip>
<TooltipTrigger class="cursor-default hover:text-foreground transition-colors">
<time datetime={lastUpdated?.toISOString()}>{relativeTime}</time>
</TooltipTrigger>
<TooltipContent>
{absoluteTime}
</TooltipContent>
</Tooltip>
</TooltipProvider>
{/if}
</div>
{/if}
</CardContent>
</Card>
</a>