Should be mostly working, doing some additional QOL
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user