Files
jellytau/src/lib/components/library/PersonDetailView.svelte
T
dtourolle d54d8cc7c4 refactor(logging): route frontend console calls through the logger
TRACES: | DR-204

484 ungated `console.*` calls across 63 non-test frontend files shipped to
end users with no way to turn them off. Mechanical substitution, no control
flow, error handling or message semantics changed:

  console.log / console.debug -> log.debug
  console.info                -> log.info
  console.warn                -> log.warn
  console.error               -> log.error

Hand-written `"[Scope] …"` prefixes are dropped where the logger's scope
now carries them; scope names that already existed are preserved verbatim
(`[Auth]`, `[VideoPlayer]`, `[PiP]`, …) and inferred from the filename
where a file had none. `src/routes/player/[id]/+page.svelte` keeps its
`NextEpisode` and `AutoPlay` sub-scopes as separate loggers rather than
flattening them into the page scope.

`grep -rn 'console\.' src/` now matches nothing outside the tests and the
facade itself.
2026-08-20 19:29:59 +02:00

109 lines
2.7 KiB
Svelte

<!-- TRACES: UR-036 | JA-030, JA-031 | DR-041 -->
<script lang="ts">
import type { MediaItem, Library } from "$lib/api/types";
import { auth } from "$lib/stores/auth";
import { onMount } from "svelte";
import LibraryGrid from "./LibraryGrid.svelte";
import CachedImage from "$lib/components/common/CachedImage.svelte";
import { goto } from "$app/navigation";
import { createLogger } from "$lib/utils/logger";
const log = createLogger("PersonDetailView");
interface Props {
person: MediaItem;
}
let { person }: Props = $props();
let movies = $state<MediaItem[]>([]);
let series = $state<MediaItem[]>([]);
let loading = $state(true);
onMount(async () => {
await loadFilmography();
});
async function loadFilmography() {
loading = true;
try {
const repo = auth.getRepository();
const result = await repo.getItemsByPerson(person.id, {
limit: 100,
includeItemTypes: ["Movie", "Series"],
});
// Separate movies and series
movies = result.items.filter(item => item.kind === "movie");
series = result.items.filter(item => item.kind === "series");
} catch (e) {
log.error("Failed to load filmography:", e);
} finally {
loading = false;
}
}
function handleItemClick(item: MediaItem | Library) {
goto(`/library/${item.id}`);
}
</script>
<div class="space-y-8">
<!-- Person header -->
<div class="flex gap-6 pt-4">
<!-- Profile image -->
<div class="flex-shrink-0 w-48">
<CachedImage
itemId={person.id}
imageType="Primary"
tag={person.imageId}
maxWidth={400}
alt={person.name}
class="w-full rounded-lg shadow-lg"
/>
</div>
<!-- Info -->
<div class="flex-1 space-y-4">
<h1 class="text-3xl font-bold text-white">{person.name}</h1>
<span class="inline-block px-2 py-1 bg-[var(--color-surface)] rounded text-sm text-gray-400">
Person
</span>
{#if person.overview}
<p class="text-gray-300 leading-relaxed max-w-2xl">{person.overview}</p>
{/if}
</div>
</div>
<!-- Filmography - separated by type -->
<div class="space-y-8">
{#if movies.length > 0}
<LibraryGrid
title="Movies"
items={movies}
{loading}
showViewToggle={false}
onItemClick={handleItemClick}
/>
{/if}
{#if series.length > 0}
<LibraryGrid
title="TV Series"
items={series}
{loading}
showViewToggle={false}
onItemClick={handleItemClick}
/>
{/if}
{#if !loading && movies.length === 0 && series.length === 0}
<div class="text-center py-12 text-gray-400">
<p>No filmography found</p>
</div>
{/if}
</div>
</div>