chore: lint and format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 19:38:37 +01:00
parent a9e4ed6049
commit 1b724e86c9
19 changed files with 407 additions and 361 deletions

View File

@@ -21,7 +21,6 @@
const seconds = totalSeconds % 60; const seconds = totalSeconds % 60;
return `${minutes}:${seconds.toString().padStart(2, "0")}`; return `${minutes}:${seconds.toString().padStart(2, "0")}`;
} }
</script> </script>
<Card <Card

View File

@@ -1,23 +1,23 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script> </script>
<div <div
bind:this={ref} bind:this={ref}
data-slot="empty-content" data-slot="empty-content"
class={cn( class={cn(
"flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance", "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
className className,
)} )}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
</div> </div>

View File

@@ -1,23 +1,23 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script> </script>
<div <div
bind:this={ref} bind:this={ref}
data-slot="empty-description" data-slot="empty-description"
class={cn( class={cn(
"text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4", "text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4",
className className,
)} )}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
</div> </div>

View File

@@ -1,20 +1,20 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script> </script>
<div <div
bind:this={ref} bind:this={ref}
data-slot="empty-header" data-slot="empty-header"
class={cn("flex max-w-sm flex-col items-center gap-2 text-center", className)} class={cn("flex max-w-sm flex-col items-center gap-2 text-center", className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
</div> </div>

View File

@@ -1,41 +1,41 @@
<script lang="ts" module> <script lang="ts" module>
import { tv, type VariantProps } from "tailwind-variants"; import { tv, type VariantProps } from "tailwind-variants";
export const emptyMediaVariants = tv({ export const emptyMediaVariants = tv({
base: "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0", base: "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
variants: { variants: {
variant: { variant: {
default: "bg-transparent", default: "bg-transparent",
icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6", icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6",
}, },
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
}); });
export type EmptyMediaVariant = VariantProps<typeof emptyMediaVariants>["variant"]; export type EmptyMediaVariant = VariantProps<typeof emptyMediaVariants>["variant"];
</script> </script>
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
variant = "default", variant = "default",
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & { variant?: EmptyMediaVariant } = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> & { variant?: EmptyMediaVariant } = $props();
</script> </script>
<div <div
bind:this={ref} bind:this={ref}
data-slot="empty-icon" data-slot="empty-icon"
data-variant={variant} data-variant={variant}
class={cn(emptyMediaVariants({ variant }), className)} class={cn(emptyMediaVariants({ variant }), className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
</div> </div>

View File

@@ -1,20 +1,20 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script> </script>
<div <div
bind:this={ref} bind:this={ref}
data-slot="empty-title" data-slot="empty-title"
class={cn("text-lg font-medium tracking-tight", className)} class={cn("text-lg font-medium tracking-tight", className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
</div> </div>

View File

@@ -1,23 +1,23 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script> </script>
<div <div
bind:this={ref} bind:this={ref}
data-slot="empty" data-slot="empty"
class={cn( class={cn(
"flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12", "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12",
className className,
)} )}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
</div> </div>

View File

@@ -6,17 +6,17 @@ import Description from "./empty-description.svelte";
import Content from "./empty-content.svelte"; import Content from "./empty-content.svelte";
export { export {
Root, Root,
Header, Header,
Media, Media,
Title, Title,
Description, Description,
Content, Content,
// //
Root as Empty, Root as Empty,
Header as EmptyHeader, Header as EmptyHeader,
Media as EmptyMedia, Media as EmptyMedia,
Title as EmptyTitle, Title as EmptyTitle,
Description as EmptyDescription, Description as EmptyDescription,
Content as EmptyContent, Content as EmptyContent,
}; };

View File

@@ -912,20 +912,14 @@ const UPDATE_RECORDING_MUTATION = gql`
} }
`; `;
export async function updateRecording( export async function updateRecording(id: string, fields: { status?: string; public?: boolean }) {
id: string, return loggedApiCall("updateRecording", async () => {
fields: { status?: string; public?: boolean }, const data = await getGraphQLClient().request<{ updateRecording: Recording }>(
) { UPDATE_RECORDING_MUTATION,
return loggedApiCall( { id, ...fields },
"updateRecording", );
async () => { return data.updateRecording;
const data = await getGraphQLClient().request<{ updateRecording: Recording }>( });
UPDATE_RECORDING_MUTATION,
{ id, ...fields },
);
return data.updateRecording;
},
);
} }
const DELETE_RECORDING_MUTATION = gql` const DELETE_RECORDING_MUTATION = gql`

View File

@@ -68,7 +68,10 @@
<!-- Sidebar (desktop only) --> <!-- Sidebar (desktop only) -->
<aside class="hidden lg:flex w-56 shrink-0 flex-col border-r border-border/40"> <aside class="hidden lg:flex w-56 shrink-0 flex-col border-r border-border/40">
<div class="px-4 py-5 border-b border-border/40"> <div class="px-4 py-5 border-b border-border/40">
<a href="/" class="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"> <a
href="/"
class="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
>
<span class="icon-[ri--arrow-left-line] h-3.5 w-3.5"></span> <span class="icon-[ri--arrow-left-line] h-3.5 w-3.5"></span>
{$_("admin.nav.back_to_site")} {$_("admin.nav.back_to_site")}
</a> </a>
@@ -80,8 +83,11 @@
{getUserInitials(displayName)} {getUserInitials(displayName)}
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<span class="absolute -bottom-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full bg-primary ring-2 ring-background"> <span
<span class="icon-[ri--shield-keyhole-fill] h-2.5 w-2.5 text-primary-foreground"></span> class="absolute -bottom-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full bg-primary ring-2 ring-background"
>
<span class="icon-[ri--shield-keyhole-fill] h-2.5 w-2.5 text-primary-foreground"
></span>
</span> </span>
</div> </div>
<div class="min-w-0"> <div class="min-w-0">

View File

@@ -101,7 +101,10 @@
<div class="mb-6"> <div class="mb-6">
<h1 class="text-2xl font-bold">{data.article.title}</h1> <h1 class="text-2xl font-bold">{data.article.title}</h1>
<p class="text-xs text-muted-foreground mt-0.5"> <p class="text-xs text-muted-foreground mt-0.5">
{data.article.slug}{data.article.category ? " · " + data.article.category : ""}{data.article.author ? " · " + data.article.author.artist_name : ""} {data.article.slug}{data.article.category ? " · " + data.article.category : ""}{data.article
.author
? " · " + data.article.author.artist_name
: ""}
</p> </p>
</div> </div>
@@ -238,7 +241,10 @@
<div class="space-y-1.5"> <div class="space-y-1.5">
<Label>{$_("admin.common.tags")}</Label> <Label>{$_("admin.common.tags")}</Label>
<TagsInput bind:value={tags} class="bg-background/50 border-primary/20 focus:border-primary" /> <TagsInput
bind:value={tags}
class="bg-background/50 border-primary/20 focus:border-primary"
/>
</div> </div>
<label class="flex items-center gap-2 cursor-pointer"> <label class="flex items-center gap-2 cursor-pointer">

View File

@@ -187,7 +187,10 @@
<div class="space-y-1.5"> <div class="space-y-1.5">
<Label>{$_("admin.common.tags")}</Label> <Label>{$_("admin.common.tags")}</Label>
<TagsInput bind:value={tags} class="bg-background/50 border-primary/20 focus:border-primary" /> <TagsInput
bind:value={tags}
class="bg-background/50 border-primary/20 focus:border-primary"
/>
</div> </div>
<label class="flex items-center gap-2 cursor-pointer"> <label class="flex items-center gap-2 cursor-pointer">

View File

@@ -182,7 +182,11 @@
class="h-20 w-20 rounded-full object-cover mb-2" class="h-20 w-20 rounded-full object-cover mb-2"
/> />
{/if} {/if}
<FileDropZone accept="image/*" maxFileSize={10 * MEGABYTE} onUpload={handleAvatarUpload} /> <FileDropZone
accept="image/*"
maxFileSize={10 * MEGABYTE}
onUpload={handleAvatarUpload}
/>
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">
@@ -194,7 +198,11 @@
class="w-full h-24 rounded object-cover mb-2" class="w-full h-24 rounded object-cover mb-2"
/> />
{/if} {/if}
<FileDropZone accept="image/*" maxFileSize={10 * MEGABYTE} onUpload={handleBannerUpload} /> <FileDropZone
accept="image/*"
maxFileSize={10 * MEGABYTE}
onUpload={handleBannerUpload}
/>
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">
@@ -207,7 +215,11 @@
class="w-full h-48 rounded object-cover mb-2" class="w-full h-48 rounded object-cover mb-2"
/> />
{/if} {/if}
<FileDropZone accept="image/*" maxFileSize={10 * MEGABYTE} onUpload={handlePhotoUpload2} /> <FileDropZone
accept="image/*"
maxFileSize={10 * MEGABYTE}
onUpload={handlePhotoUpload2}
/>
</div> </div>
<label <label

View File

@@ -110,7 +110,9 @@
<div class="mb-6"> <div class="mb-6">
<h1 class="text-2xl font-bold">{data.video.title}</h1> <h1 class="text-2xl font-bold">{data.video.title}</h1>
<p class="text-xs text-muted-foreground mt-0.5"> <p class="text-xs text-muted-foreground mt-0.5">
{data.video.slug}{data.video.premium ? " · premium" : ""}{data.video.featured ? " · featured" : ""} {data.video.slug}{data.video.premium ? " · premium" : ""}{data.video.featured
? " · featured"
: ""}
</p> </p>
</div> </div>
@@ -177,7 +179,10 @@
<div class="space-y-1.5"> <div class="space-y-1.5">
<Label>{$_("admin.common.tags")}</Label> <Label>{$_("admin.common.tags")}</Label>
<TagsInput bind:value={tags} class="bg-background/50 border-primary/20 focus:border-primary" /> <TagsInput
bind:value={tags}
class="bg-background/50 border-primary/20 focus:border-primary"
/>
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">

View File

@@ -161,7 +161,10 @@
<div class="space-y-1.5"> <div class="space-y-1.5">
<Label>{$_("admin.common.tags")}</Label> <Label>{$_("admin.common.tags")}</Label>
<TagsInput bind:value={tags} class="bg-background/50 border-primary/20 focus:border-primary" /> <TagsInput
bind:value={tags}
class="bg-background/50 border-primary/20 focus:border-primary"
/>
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">

View File

@@ -65,7 +65,10 @@
<!-- Sidebar (desktop only) --> <!-- Sidebar (desktop only) -->
<aside class="hidden lg:flex w-56 shrink-0 flex-col border-r border-border/40"> <aside class="hidden lg:flex w-56 shrink-0 flex-col border-r border-border/40">
<div class="px-4 py-5 border-b border-border/40"> <div class="px-4 py-5 border-b border-border/40">
<a href="/" class="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"> <a
href="/"
class="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
>
<span class="icon-[ri--arrow-left-line] h-3.5 w-3.5"></span> <span class="icon-[ri--arrow-left-line] h-3.5 w-3.5"></span>
{$_("me.nav.back_to_site")} {$_("me.nav.back_to_site")}
</a> </a>

View File

@@ -135,150 +135,150 @@
</div> </div>
<Card class="bg-card/50 border-primary/20 max-w-2xl"> <Card class="bg-card/50 border-primary/20 max-w-2xl">
<CardHeader> <CardHeader>
<CardTitle>{$_("me.settings.profile_title")}</CardTitle> <CardTitle>{$_("me.settings.profile_title")}</CardTitle>
<CardDescription>{$_("me.settings.profile_subtitle")}</CardDescription> <CardDescription>{$_("me.settings.profile_subtitle")}</CardDescription>
</CardHeader> </CardHeader>
<CardContent class="space-y-4"> <CardContent class="space-y-4">
<form onsubmit={handleProfileSubmit} class="space-y-4"> <form onsubmit={handleProfileSubmit} class="space-y-4">
<div class="space-y-2"> <div class="space-y-2">
<Label>{$_("me.settings.avatar")}</Label> <Label>{$_("me.settings.avatar")}</Label>
<div class="flex items-center gap-5"> <div class="flex items-center gap-5">
<FileDropZone <FileDropZone
id="avatar" id="avatar"
fileCount={0} fileCount={0}
maxFiles={1} maxFiles={1}
maxFileSize={2 * MEGABYTE} maxFileSize={2 * MEGABYTE}
onUpload={handleFilesUpload} onUpload={handleFilesUpload}
accept="image/*" accept="image/*"
class="h-auto w-auto shrink-0 border-none p-0 rounded-full hover:bg-transparent" class="h-auto w-auto shrink-0 border-none p-0 rounded-full hover:bg-transparent"
> >
<div class="relative group cursor-pointer w-24 h-24"> <div class="relative group cursor-pointer w-24 h-24">
{#if avatar}
<img
src={avatar.url}
alt={avatar.name}
class="w-24 h-24 rounded-full object-cover ring-4 ring-primary/20 group-hover:ring-primary/50 transition-all"
/>
<div
class="absolute inset-0 rounded-full bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"
>
<span class="icon-[ri--camera-line] w-7 h-7 text-white"></span>
</div>
{:else}
<div
class="w-24 h-24 rounded-full border-2 border-dashed border-primary/30 group-hover:border-primary/60 bg-primary/5 group-hover:bg-primary/10 transition-all flex flex-col items-center justify-center gap-1"
>
<span
class="icon-[ri--camera-line] w-7 h-7 text-primary/50 group-hover:text-primary/80 transition-colors"
></span>
<span class="text-xs text-muted-foreground">Upload</span>
</div>
{/if}
</div>
</FileDropZone>
<div class="flex flex-col gap-1">
<p class="text-sm text-muted-foreground">JPG, PNG · max 2 MB</p>
<p class="text-xs text-muted-foreground/70">
Click or drop to {avatar ? "change" : "upload"}
</p>
{#if avatar} {#if avatar}
<Button <img
variant="ghost" src={avatar.url}
size="sm" alt={avatar.name}
onclick={handleAvatarRemove} class="w-24 h-24 rounded-full object-cover ring-4 ring-primary/20 group-hover:ring-primary/50 transition-all"
class="cursor-pointer w-fit mt-1 px-2 h-7 text-xs text-muted-foreground hover:text-destructive hover:bg-destructive/10" />
<div
class="absolute inset-0 rounded-full bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"
> >
<span class="icon-[ri--delete-bin-line] w-3.5 h-3.5 mr-1"></span> <span class="icon-[ri--camera-line] w-7 h-7 text-white"></span>
Remove </div>
</Button> {:else}
<div
class="w-24 h-24 rounded-full border-2 border-dashed border-primary/30 group-hover:border-primary/60 bg-primary/5 group-hover:bg-primary/10 transition-all flex flex-col items-center justify-center gap-1"
>
<span
class="icon-[ri--camera-line] w-7 h-7 text-primary/50 group-hover:text-primary/80 transition-colors"
></span>
<span class="text-xs text-muted-foreground">Upload</span>
</div>
{/if} {/if}
</div> </div>
</FileDropZone>
<div class="flex flex-col gap-1">
<p class="text-sm text-muted-foreground">JPG, PNG · max 2 MB</p>
<p class="text-xs text-muted-foreground/70">
Click or drop to {avatar ? "change" : "upload"}
</p>
{#if avatar}
<Button
variant="ghost"
size="sm"
onclick={handleAvatarRemove}
class="cursor-pointer w-fit mt-1 px-2 h-7 text-xs text-muted-foreground hover:text-destructive hover:bg-destructive/10"
>
<span class="icon-[ri--delete-bin-line] w-3.5 h-3.5 mr-1"></span>
Remove
</Button>
{/if}
</div> </div>
</div> </div>
</div>
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="firstName">{$_("me.settings.first_name")}</Label>
<Input
id="firstName"
placeholder={$_("me.settings.first_name_placeholder")}
bind:value={firstName}
required
class="bg-background/50 border-primary/20 focus:border-primary"
/>
</div>
<div class="space-y-2">
<Label for="lastName">{$_("me.settings.last_name")}</Label>
<Input
id="lastName"
placeholder={$_("me.settings.last_name_placeholder")}
bind:value={lastName}
required
class="bg-background/50 border-primary/20 focus:border-primary"
/>
</div>
</div>
<div class="space-y-2"> <div class="space-y-2">
<Label for="artistName">{$_("me.settings.artist_name")}</Label> <Label for="firstName">{$_("me.settings.first_name")}</Label>
<Input <Input
id="artistName" id="firstName"
placeholder={$_("me.settings.artist_name_placeholder")} placeholder={$_("me.settings.first_name_placeholder")}
bind:value={artistName} bind:value={firstName}
required required
class="bg-background/50 border-primary/20 focus:border-primary" class="bg-background/50 border-primary/20 focus:border-primary"
/> />
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<Label for="description">{$_("me.settings.description")}</Label> <Label for="lastName">{$_("me.settings.last_name")}</Label>
<Textarea <Input
id="description" id="lastName"
bind:value={description} placeholder={$_("me.settings.last_name_placeholder")}
placeholder={$_("me.settings.description_placeholder")} bind:value={lastName}
class="bg-background/50 border-primary/20 focus:border-primary" required
rows={3}
/>
</div>
<div class="space-y-2">
<Label for="tags">{$_("me.settings.tags")}</Label>
<TagsInput
id="tags"
bind:value={tags}
placeholder={$_("me.settings.tags_placeholder")}
class="bg-background/50 border-primary/20 focus:border-primary" class="bg-background/50 border-primary/20 focus:border-primary"
/> />
</div> </div>
</div>
{#if isProfileError} <div class="space-y-2">
<div class="grid w-full items-start gap-4"> <Label for="artistName">{$_("me.settings.artist_name")}</Label>
<Alert.Root variant="destructive"> <Input
<Alert.Title class="items-center flex"> id="artistName"
<span class="icon-[ri--alert-line] inline-block w-4 h-4 mr-1"></span> placeholder={$_("me.settings.artist_name_placeholder")}
{$_("me.settings.error")} bind:value={artistName}
</Alert.Title> required
<Alert.Description>{profileError}</Alert.Description> class="bg-background/50 border-primary/20 focus:border-primary"
</Alert.Root> />
</div> </div>
<div class="space-y-2">
<Label for="description">{$_("me.settings.description")}</Label>
<Textarea
id="description"
bind:value={description}
placeholder={$_("me.settings.description_placeholder")}
class="bg-background/50 border-primary/20 focus:border-primary"
rows={3}
/>
</div>
<div class="space-y-2">
<Label for="tags">{$_("me.settings.tags")}</Label>
<TagsInput
id="tags"
bind:value={tags}
placeholder={$_("me.settings.tags_placeholder")}
class="bg-background/50 border-primary/20 focus:border-primary"
/>
</div>
{#if isProfileError}
<div class="grid w-full items-start gap-4">
<Alert.Root variant="destructive">
<Alert.Title class="items-center flex">
<span class="icon-[ri--alert-line] inline-block w-4 h-4 mr-1"></span>
{$_("me.settings.error")}
</Alert.Title>
<Alert.Description>{profileError}</Alert.Description>
</Alert.Root>
</div>
{/if}
<Button
type="submit"
class="cursor-pointer w-full bg-gradient-to-r from-primary to-accent hover:from-primary/90 hover:to-accent/90"
disabled={isProfileLoading}
>
{#if isProfileLoading}
<div
class="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mr-2"
></div>
{$_("me.settings.updating_profile")}
{:else}
{$_("me.settings.update_profile")}
{/if} {/if}
</Button>
<Button </form>
type="submit" </CardContent>
class="cursor-pointer w-full bg-gradient-to-r from-primary to-accent hover:from-primary/90 hover:to-accent/90" </Card>
disabled={isProfileLoading}
>
{#if isProfileLoading}
<div
class="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mr-2"
></div>
{$_("me.settings.updating_profile")}
{:else}
{$_("me.settings.update_profile")}
{/if}
</Button>
</form>
</CardContent>
</Card>
</div> </div>

View File

@@ -63,101 +63,101 @@
</div> </div>
<Card class="bg-card/50 border-primary/20 max-w-2xl"> <Card class="bg-card/50 border-primary/20 max-w-2xl">
<CardHeader> <CardHeader>
<CardTitle>{$_("me.settings.privacy_title")}</CardTitle> <CardTitle>{$_("me.settings.privacy_title")}</CardTitle>
<CardDescription>{$_("me.settings.privacy_subtitle")}</CardDescription> <CardDescription>{$_("me.settings.privacy_subtitle")}</CardDescription>
</CardHeader> </CardHeader>
<CardContent class="space-y-4"> <CardContent class="space-y-4">
<form onsubmit={handleSecuritySubmit} class="space-y-4"> <form onsubmit={handleSecuritySubmit} class="space-y-4">
<div class="space-y-2"> <div class="space-y-2">
<Label for="email">{$_("me.settings.email")}</Label> <Label for="email">{$_("me.settings.email")}</Label>
<Input
id="email"
type="email"
placeholder={$_("me.settings.email_placeholder")}
bind:value={email}
required
class="bg-background/50 border-primary/20 focus:border-primary"
/>
</div>
<div class="space-y-2">
<Label for="password">{$_("me.settings.password")}</Label>
<div class="relative">
<Input <Input
id="email" id="password"
type="email" type={showPassword ? "text" : "password"}
placeholder={$_("me.settings.email_placeholder")} placeholder={$_("me.settings.password_placeholder")}
bind:value={email} bind:value={password}
required required
class="bg-background/50 border-primary/20 focus:border-primary" class="bg-background/50 border-primary/20 focus:border-primary pr-10"
/> />
<button
type="button"
onclick={() => (showPassword = !showPassword)}
class="cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
>
{#if showPassword}
<span class="icon-[ri--eye-off-line] w-4 h-4"></span>
{:else}
<span class="icon-[ri--eye-line] w-4 h-4"></span>
{/if}
</button>
</div> </div>
</div>
<div class="space-y-2"> <div class="space-y-2">
<Label for="password">{$_("me.settings.password")}</Label> <Label for="confirmPassword">{$_("me.settings.confirm_password")}</Label>
<div class="relative"> <div class="relative">
<Input <Input
id="password" id="confirmPassword"
type={showPassword ? "text" : "password"} type={showConfirmPassword ? "text" : "password"}
placeholder={$_("me.settings.password_placeholder")} placeholder={$_("me.settings.confirm_password_placeholder")}
bind:value={password} bind:value={confirmPassword}
required required
class="bg-background/50 border-primary/20 focus:border-primary pr-10" class="bg-background/50 border-primary/20 focus:border-primary pr-10"
/> />
<button <button
type="button" type="button"
onclick={() => (showPassword = !showPassword)} onclick={() => (showConfirmPassword = !showConfirmPassword)}
class="cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground" class="cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
> >
{#if showPassword} {#if showConfirmPassword}
<span class="icon-[ri--eye-off-line] w-4 h-4"></span> <span class="icon-[ri--eye-off-line] w-4 h-4"></span>
{:else} {:else}
<span class="icon-[ri--eye-line] w-4 h-4"></span> <span class="icon-[ri--eye-line] w-4 h-4"></span>
{/if} {/if}
</button> </button>
</div>
</div> </div>
</div>
<div class="space-y-2"> {#if isSecurityError}
<Label for="confirmPassword">{$_("me.settings.confirm_password")}</Label> <div class="grid w-full items-start gap-4">
<div class="relative"> <Alert.Root variant="destructive">
<Input <Alert.Title class="items-center flex">
id="confirmPassword" <span class="icon-[ri--alert-line] inline-block w-4 h-4 mr-1"></span>
type={showConfirmPassword ? "text" : "password"} {$_("me.settings.error")}
placeholder={$_("me.settings.confirm_password_placeholder")} </Alert.Title>
bind:value={confirmPassword} <Alert.Description>{securityError}</Alert.Description>
required </Alert.Root>
class="bg-background/50 border-primary/20 focus:border-primary pr-10"
/>
<button
type="button"
onclick={() => (showConfirmPassword = !showConfirmPassword)}
class="cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
>
{#if showConfirmPassword}
<span class="icon-[ri--eye-off-line] w-4 h-4"></span>
{:else}
<span class="icon-[ri--eye-line] w-4 h-4"></span>
{/if}
</button>
</div>
</div> </div>
{/if}
{#if isSecurityError} <Button
<div class="grid w-full items-start gap-4"> type="submit"
<Alert.Root variant="destructive"> class="cursor-pointer w-full bg-gradient-to-r from-primary to-accent hover:from-primary/90 hover:to-accent/90"
<Alert.Title class="items-center flex"> disabled={isSecurityLoading}
<span class="icon-[ri--alert-line] inline-block w-4 h-4 mr-1"></span> >
{$_("me.settings.error")} {#if isSecurityLoading}
</Alert.Title> <div
<Alert.Description>{securityError}</Alert.Description> class="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mr-2"
</Alert.Root> ></div>
</div> {$_("me.settings.updating_security")}
{:else}
{$_("me.settings.update_security")}
{/if} {/if}
</Button>
<Button </form>
type="submit" </CardContent>
class="cursor-pointer w-full bg-gradient-to-r from-primary to-accent hover:from-primary/90 hover:to-accent/90" </Card>
disabled={isSecurityLoading}
>
{#if isSecurityLoading}
<div
class="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mr-2"
></div>
{$_("me.settings.updating_security")}
{:else}
{$_("me.settings.update_security")}
{/if}
</Button>
</form>
</CardContent>
</Card>
</div> </div>

View File

@@ -9,9 +9,24 @@
const { children, data } = $props(); const { children, data } = $props();
const navLinks = $derived([ const navLinks = $derived([
{ name: $_("play.nav.play"), href: "/play/buttplug", icon: "icon-[ri--rocket-line]", exact: false }, {
{ name: $_("play.nav.recordings"), href: "/play/recordings", icon: "icon-[ri--play-list-2-line]", exact: false }, name: $_("play.nav.play"),
{ name: $_("play.nav.leaderboard"), href: "/play/leaderboard", icon: "icon-[ri--trophy-line]", exact: false }, href: "/play/buttplug",
icon: "icon-[ri--rocket-line]",
exact: false,
},
{
name: $_("play.nav.recordings"),
href: "/play/recordings",
icon: "icon-[ri--play-list-2-line]",
exact: false,
},
{
name: $_("play.nav.leaderboard"),
href: "/play/leaderboard",
icon: "icon-[ri--trophy-line]",
exact: false,
},
]); ]);
function isActive(link: { href: string; exact: boolean }) { function isActive(link: { href: string; exact: boolean }) {