fix: resolve lint errors from ACL/admin implementation

- Remove unused requireOwnerOrAdmin import from videos.ts
- Remove unused requireAuth import from users.ts
- Remove unused GraphQLError import from articles.ts
- Replace URLSearchParams with SvelteURLSearchParams in admin users page
- Apply prettier formatting to all changed files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 12:35:11 +01:00
parent c1770ab9c9
commit ad7ceee5f8
18 changed files with 112 additions and 130 deletions

View File

@@ -90,7 +90,10 @@
{/each}
{#if (recording.device_info?.length ?? 0) > 2}
<div class="text-xs text-muted-foreground/60 px-2">
+{(recording.device_info?.length ?? 0) - 2} more device{(recording.device_info?.length ?? 0) - 2 > 1
+{(recording.device_info?.length ?? 0) - 2} more device{(recording.device_info?.length ??
0) -
2 >
1
? "s"
: ""}
</div>

View File

@@ -16,9 +16,7 @@
<div class="flex min-h-screen bg-background">
<!-- Sidebar -->
<aside
class="w-56 shrink-0 border-r border-border/40 bg-card/60 backdrop-blur-sm flex flex-col"
>
<aside class="w-56 shrink-0 border-r border-border/40 bg-card/60 backdrop-blur-sm flex flex-col">
<div class="px-4 py-5 border-b border-border/40">
<a href="/" class="text-xs text-muted-foreground hover:text-foreground transition-colors">
← Back to site

View File

@@ -21,14 +21,12 @@
let tags = $state<string[]>(data.article.tags ?? []);
let featured = $state(data.article.featured ?? false);
let publishDate = $state(
data.article.publish_date
? new Date(data.article.publish_date).toISOString().slice(0, 16)
: "",
data.article.publish_date ? new Date(data.article.publish_date).toISOString().slice(0, 16) : "",
);
let imageId = $state<string | null>(data.article.image ?? null);
let saving = $state(false);
let preview = $derived(content ? marked.parse(content) as string : "");
let preview = $derived(content ? (marked.parse(content) as string) : "");
async function handleImageUpload(files: File[]) {
const file = files[0];
@@ -98,10 +96,7 @@
<div class="space-y-1.5">
<Label>Content (Markdown)</Label>
<div class="grid grid-cols-2 gap-4 min-h-96">
<Textarea
bind:value={content}
class="h-full min-h-96 font-mono text-sm resize-none"
/>
<Textarea bind:value={content} class="h-full min-h-96 font-mono text-sm resize-none" />
<div
class="rounded-lg border border-border/40 bg-muted/20 p-4 overflow-auto prose prose-sm max-w-none prose-headings:text-foreground prose-p:text-muted-foreground"
>
@@ -117,13 +112,13 @@
<div class="space-y-1.5">
<Label>Cover image</Label>
{#if imageId}
<img src={getAssetUrl(imageId, "thumbnail")} alt="" class="h-24 rounded object-cover mb-2" />
<img
src={getAssetUrl(imageId, "thumbnail")}
alt=""
class="h-24 rounded object-cover mb-2"
/>
{/if}
<FileDropZone
accept="image/*"
maxFileSize={10 * MEGABYTE}
onUpload={handleImageUpload}
/>
<FileDropZone accept="image/*" maxFileSize={10 * MEGABYTE} onUpload={handleImageUpload} />
</div>
<div class="grid grid-cols-2 gap-4">

View File

@@ -21,7 +21,7 @@
let imageId = $state<string | null>(null);
let saving = $state(false);
let preview = $derived(content ? marked.parse(content) as string : "");
let preview = $derived(content ? (marked.parse(content) as string) : "");
function generateSlug(t: string) {
return t
@@ -87,7 +87,9 @@
<Input
id="title"
bind:value={title}
oninput={() => { if (!slug) slug = generateSlug(title); }}
oninput={() => {
if (!slug) slug = generateSlug(title);
}}
placeholder="Article title"
/>
</div>
@@ -125,11 +127,7 @@
<div class="space-y-1.5">
<Label>Cover image</Label>
<FileDropZone
accept="image/*"
maxFileSize={10 * MEGABYTE}
onUpload={handleImageUpload}
/>
<FileDropZone accept="image/*" maxFileSize={10 * MEGABYTE} onUpload={handleImageUpload} />
{#if imageId}<p class="text-xs text-green-600 mt-1">Image uploaded ✓</p>{/if}
</div>

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import { goto, invalidateAll } from "$app/navigation";
import { page } from "$app/state";
import { SvelteURLSearchParams } from "svelte/reactivity";
import { toast } from "svelte-sonner";
import { adminUpdateUser, adminDeleteUser } from "$lib/services";
import { getAssetUrl } from "$lib/api";
@@ -25,7 +26,7 @@
function debounceSearch(value: string) {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
const params = new URLSearchParams(page.url.searchParams);
const params = new SvelteURLSearchParams(page.url.searchParams.toString());
if (value) params.set("search", value);
else params.delete("search");
params.delete("offset");
@@ -34,7 +35,7 @@
}
function setRole(role: string) {
const params = new URLSearchParams(page.url.searchParams);
const params = new SvelteURLSearchParams(page.url.searchParams.toString());
if (role) params.set("role", role);
else params.delete("role");
params.delete("offset");
@@ -193,7 +194,7 @@
variant="outline"
disabled={data.offset === 0}
onclick={() => {
const params = new URLSearchParams(page.url.searchParams);
const params = new SvelteURLSearchParams(page.url.searchParams.toString());
params.set("offset", String(Math.max(0, data.offset - data.limit)));
goto(`?${params.toString()}`);
}}
@@ -205,7 +206,7 @@
variant="outline"
disabled={data.offset + data.limit >= data.total}
onclick={() => {
const params = new URLSearchParams(page.url.searchParams);
const params = new SvelteURLSearchParams(page.url.searchParams.toString());
params.set("offset", String(data.offset + data.limit));
goto(`?${params.toString()}`);
}}
@@ -230,11 +231,7 @@
</Dialog.Header>
<Dialog.Footer>
<Button variant="outline" onclick={() => (deleteOpen = false)}>Cancel</Button>
<Button
variant="destructive"
disabled={deleting}
onclick={handleDelete}
>
<Button variant="destructive" disabled={deleting} onclick={handleDelete}>
{deleting ? "Deleting…" : "Delete"}
</Button>
</Dialog.Footer>

View File

@@ -87,8 +87,7 @@
>
{/if}
{#if video.featured}
<span
class="px-1.5 py-0.5 rounded text-xs font-medium bg-primary/10 text-primary"
<span class="px-1.5 py-0.5 rounded text-xs font-medium bg-primary/10 text-primary"
>Featured</span
>
{/if}

View File

@@ -19,9 +19,7 @@
let premium = $state(data.video.premium ?? false);
let featured = $state(data.video.featured ?? false);
let uploadDate = $state(
data.video.upload_date
? new Date(data.video.upload_date).toISOString().slice(0, 16)
: "",
data.video.upload_date ? new Date(data.video.upload_date).toISOString().slice(0, 16) : "",
);
let imageId = $state<string | null>(data.video.image ?? null);
let movieId = $state<string | null>(data.video.movie ?? null);
@@ -118,13 +116,13 @@
<div class="space-y-1.5">
<Label>Cover image</Label>
{#if imageId}
<img src={getAssetUrl(imageId, "thumbnail")} alt="" class="h-24 rounded object-cover mb-2" />
<img
src={getAssetUrl(imageId, "thumbnail")}
alt=""
class="h-24 rounded object-cover mb-2"
/>
{/if}
<FileDropZone
accept="image/*"
maxFileSize={10 * MEGABYTE}
onUpload={handleImageUpload}
/>
<FileDropZone accept="image/*" maxFileSize={10 * MEGABYTE} onUpload={handleImageUpload} />
</div>
<div class="space-y-1.5">
@@ -132,11 +130,7 @@
{#if movieId}
<p class="text-xs text-muted-foreground mb-1">Current file: {movieId}</p>
{/if}
<FileDropZone
accept="video/*"
maxFileSize={2000 * MEGABYTE}
onUpload={handleVideoUpload}
/>
<FileDropZone accept="video/*" maxFileSize={2000 * MEGABYTE} onUpload={handleVideoUpload} />
</div>
<div class="space-y-1.5">

View File

@@ -110,7 +110,9 @@
<Input
id="title"
bind:value={title}
oninput={() => { if (!slug) slug = generateSlug(title); }}
oninput={() => {
if (!slug) slug = generateSlug(title);
}}
placeholder="Video title"
/>
</div>
@@ -122,26 +124,23 @@
<div class="space-y-1.5">
<Label for="description">Description</Label>
<Textarea id="description" bind:value={description} placeholder="Optional description" rows={3} />
<Textarea
id="description"
bind:value={description}
placeholder="Optional description"
rows={3}
/>
</div>
<div class="space-y-1.5">
<Label>Cover image</Label>
<FileDropZone
accept="image/*"
maxFileSize={10 * MEGABYTE}
onUpload={handleImageUpload}
/>
<FileDropZone accept="image/*" maxFileSize={10 * MEGABYTE} onUpload={handleImageUpload} />
{#if imageId}<p class="text-xs text-green-600 mt-1">Image uploaded ✓</p>{/if}
</div>
<div class="space-y-1.5">
<Label>Video file</Label>
<FileDropZone
accept="video/*"
maxFileSize={2000 * MEGABYTE}
onUpload={handleVideoUpload}
/>
<FileDropZone accept="video/*" maxFileSize={2000 * MEGABYTE} onUpload={handleVideoUpload} />
{#if movieId}<p class="text-xs text-green-600 mt-1">Video uploaded ✓</p>{/if}
</div>

View File

@@ -141,37 +141,37 @@
<!-- Author Bio -->
{#if data.article.author}
<Card class="p-0 bg-gradient-to-r from-card/50 to-card">
<CardContent class="p-6">
<div class="flex items-start gap-4">
<img
src={getAssetUrl(data.article.author.avatar, "mini")}
alt={data.article.author.first_name}
class="w-16 h-16 rounded-full object-cover ring-2 ring-primary/20"
/>
<div class="flex-1">
<h3 class="font-semibold text-lg mb-2">
About {data.article.author.first_name}
</h3>
{#if data.article.author.description}
<p class="text-muted-foreground mb-4">
{data.article.author.description}
</p>
{/if}
{#if data.article.author.website}
<div class="flex gap-4 text-sm">
<a
href={"https://" + data.article.author.website}
class="text-primary hover:underline"
>
{data.article.author.website}
</a>
</div>
{/if}
<Card class="p-0 bg-gradient-to-r from-card/50 to-card">
<CardContent class="p-6">
<div class="flex items-start gap-4">
<img
src={getAssetUrl(data.article.author.avatar, "mini")}
alt={data.article.author.first_name}
class="w-16 h-16 rounded-full object-cover ring-2 ring-primary/20"
/>
<div class="flex-1">
<h3 class="font-semibold text-lg mb-2">
About {data.article.author.first_name}
</h3>
{#if data.article.author.description}
<p class="text-muted-foreground mb-4">
{data.article.author.description}
</p>
{/if}
{#if data.article.author.website}
<div class="flex gap-4 text-sm">
<a
href={"https://" + data.article.author.website}
class="text-primary hover:underline"
>
{data.article.author.website}
</a>
</div>
{/if}
</div>
</div>
</div>
</CardContent>
</Card>
</CardContent>
</Card>
{/if}
</article>

View File

@@ -7,7 +7,9 @@ export const GET = async () => {
excludeRoutePatterns: ["^/signup/verify", "^/password/reset", "^/me", "^/play", "^/tags/.+"],
paramValues: {
"/magazine/[slug]": (await getArticles(fetch)).map((a) => a.slug),
"/models/[slug]": (await getModels(fetch)).map((a) => a.slug).filter((s): s is string => s !== null),
"/models/[slug]": (await getModels(fetch))
.map((a) => a.slug)
.filter((s): s is string => s !== null),
"/videos/[slug]": (await getVideos(fetch)).map((a) => a.slug),
},
defaultChangefreq: "always",

View File

@@ -27,7 +27,7 @@
<Meta
title={displayName}
description={data.user.description || `${displayName}'s profile`}
image={data.user.avatar ? getAssetUrl(data.user.avatar, "thumbnail") ?? undefined : undefined}
image={data.user.avatar ? (getAssetUrl(data.user.avatar, "thumbnail") ?? undefined) : undefined}
/>
<div class="relative min-h-screen bg-gradient-to-br from-background via-primary/5 to-accent/5">
@@ -91,8 +91,6 @@
>
</div>
{#if data.user.description}
<p class="text-muted-foreground mb-4">
{data.user.description}
@@ -183,7 +181,7 @@
{$_("gamification.achievements")} ({data.gamification.achievements.length})
</h3>
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-3">
{#each (data.gamification?.achievements ?? []) as achievement (achievement.id)}
{#each data.gamification?.achievements ?? [] as achievement (achievement.id)}
<div
class="flex flex-col items-center gap-2 p-3 rounded-lg bg-accent/10 border border-border/30 hover:border-primary/50 transition-colors"
title={achievement.description}
@@ -194,7 +192,9 @@
</span>
{#if achievement.date_unlocked}
<span class="text-xs text-muted-foreground">
{new Date(achievement.date_unlocked).toLocaleDateString($locale ?? undefined)}
{new Date(achievement.date_unlocked).toLocaleDateString(
$locale ?? undefined,
)}
</span>
{/if}
</div>