mirror of
https://github.com/Dvorinka/SEEN.git
synced 2026-06-05 13:03:01 +00:00
small fix, don't worry about it
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
import { Clapperboard, Clock3, Flame, Star } from 'lucide-solid'
|
||||
import { For, Show, createMemo, createResource } from 'solid-js'
|
||||
import { MediaCard } from '@/components/media/media-card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { DataRow } from '@/components/ui/data-row'
|
||||
import { EmptyState } from '@/components/ui/empty-state'
|
||||
import { SectionHeading } from '@/components/ui/section-heading'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { discoverService } from '@/services/discover-service'
|
||||
import type { MediaItem } from '@/types/domain'
|
||||
import { formatDate } from '@/utils/format'
|
||||
import { mediaBadgeVariant, mediaTypeLabel } from '@/utils/media'
|
||||
|
||||
const moviesOnly = (items: MediaItem[]) => items.filter((item) => item.type === 'movie')
|
||||
|
||||
export const MoviesPage = () => {
|
||||
const [discoverData, { refetch }] = createResource(() => discoverService.getSections({ page: 1, pageSize: 5 }))
|
||||
const allSections = createMemo(() => discoverData() ?? [])
|
||||
const trendingMovies = createMemo(() =>
|
||||
moviesOnly(allSections().find((section) => section.kind === 'trending')?.items ?? []),
|
||||
)
|
||||
const topRatedMovies = createMemo(() =>
|
||||
moviesOnly(allSections().find((section) => section.kind === 'top-rated')?.items ?? []),
|
||||
)
|
||||
const upcomingMovies = createMemo(() =>
|
||||
moviesOnly(allSections().find((section) => section.kind === 'upcoming')?.items ?? []),
|
||||
)
|
||||
|
||||
const averageRating = createMemo(() => {
|
||||
const items = topRatedMovies()
|
||||
if (items.length === 0) return 0
|
||||
return (items.reduce((acc, item) => acc + item.rating, 0) / items.length).toFixed(1)
|
||||
})
|
||||
|
||||
return (
|
||||
<section class="space-y-6" data-testid="movies-page">
|
||||
<Card class="animate-stagger">
|
||||
<CardContent class="space-y-6 pt-6">
|
||||
<SectionHeading
|
||||
eyebrow="Movies"
|
||||
title="A dedicated movie surface with trending, top rated, and upcoming picks."
|
||||
description="Focused on film browsing without mixing in episodic or game noise."
|
||||
badge={<Badge variant="neutral">{trendingMovies().length + topRatedMovies().length} loaded cards</Badge>}
|
||||
action={<Button variant="subtle" size="sm" onClick={() => refetch()}>Refresh</Button>}
|
||||
/>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<div class="rounded-2xl border border-border bg-muted/40 p-4">
|
||||
<p class="text-xs uppercase tracking-[0.2em] text-muted-fg">Trending</p>
|
||||
<p class="mt-2 text-2xl font-semibold text-fg">{trendingMovies().length}</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-border bg-muted/40 p-4">
|
||||
<p class="text-xs uppercase tracking-[0.2em] text-muted-fg">Top rated</p>
|
||||
<p class="mt-2 text-2xl font-semibold text-fg">{topRatedMovies().length}</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-border bg-muted/40 p-4">
|
||||
<p class="text-xs uppercase tracking-[0.2em] text-muted-fg">Upcoming</p>
|
||||
<p class="mt-2 text-2xl font-semibold text-fg">{upcomingMovies().length}</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-border bg-muted/40 p-4">
|
||||
<p class="text-xs uppercase tracking-[0.2em] text-muted-fg">Avg rating</p>
|
||||
<p class="mt-2 text-2xl font-semibold text-fg">{averageRating()}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Show when={discoverData.loading && allSections().length === 0}>
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<For each={Array.from({ length: 6 }, (_, index) => index)}>{() => <Skeleton class="h-64" />}</For>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div class="grid gap-6 xl:grid-cols-[minmax(0,1.05fr)_360px]">
|
||||
<Card>
|
||||
<CardContent class="space-y-5 pt-6">
|
||||
<SectionHeading
|
||||
title="Trending movies"
|
||||
description="Films currently drawing the most attention."
|
||||
badge={<Badge variant="accent">{trendingMovies().length} active</Badge>}
|
||||
/>
|
||||
<Show
|
||||
when={trendingMovies().length > 0}
|
||||
fallback={<EmptyState title="No trending movies" description="Movie feed is currently empty." />}
|
||||
>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<For each={trendingMovies().slice(0, 6)}>{(item) => <MediaCard item={item} />}</For>
|
||||
</div>
|
||||
</Show>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent class="space-y-5 pt-6">
|
||||
<SectionHeading
|
||||
title="Signals"
|
||||
description="Quick indicators for film curation quality."
|
||||
badge={<Badge variant="secondary">Live</Badge>}
|
||||
/>
|
||||
<div class="space-y-3">
|
||||
<DataRow
|
||||
tone="accent"
|
||||
eyebrow="Momentum"
|
||||
title={`${trendingMovies().length} titles trending`}
|
||||
description="High-interest films currently crossing discovery rails."
|
||||
trailing={<Flame class="h-4 w-4" />}
|
||||
/>
|
||||
<DataRow
|
||||
tone="neutral"
|
||||
eyebrow="Quality"
|
||||
title={`${averageRating()}/10 average top-rated score`}
|
||||
description="Ranking quality based on current top-rated snapshot."
|
||||
trailing={<Star class="h-4 w-4" />}
|
||||
/>
|
||||
<DataRow
|
||||
tone="secondary"
|
||||
eyebrow="Pipeline"
|
||||
title={`${upcomingMovies().length} upcoming releases`}
|
||||
description="Near-term movie launches currently visible in calendar feed."
|
||||
trailing={<Clock3 class="h-4 w-4" />}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardContent class="space-y-5 pt-6">
|
||||
<SectionHeading
|
||||
title="Upcoming releases"
|
||||
description="Movies arriving soon."
|
||||
badge={<Badge variant="neutral">{upcomingMovies().length} upcoming</Badge>}
|
||||
/>
|
||||
<Show
|
||||
when={upcomingMovies().length > 0}
|
||||
fallback={<EmptyState title="No upcoming movies" description="Upcoming movie release feed is empty." />}
|
||||
>
|
||||
<div class="space-y-3">
|
||||
<For each={upcomingMovies()}>
|
||||
{(item) => (
|
||||
<DataRow
|
||||
tone={mediaBadgeVariant(item.type)}
|
||||
eyebrow={mediaTypeLabel(item.type)}
|
||||
title={item.title}
|
||||
description={item.genres.slice(0, 2).join(' • ')}
|
||||
meta={formatDate(item.releaseDate)}
|
||||
badges={<Badge variant={mediaBadgeVariant(item.type)}>{mediaTypeLabel(item.type)}</Badge>}
|
||||
trailing={<Clapperboard class="h-4 w-4" />}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Show when={discoverData.error}>
|
||||
<div class="rounded-[1.5rem] border border-secondary/24 bg-secondary/10 px-4 py-4 text-sm text-fg">
|
||||
{discoverData.error instanceof Error ? discoverData.error.message : 'Failed to load movies'}
|
||||
</div>
|
||||
</Show>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user