mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-26 04:04:39 +02:00
feat(shared-ui): add reusable settings components with glass styling
- Add SettingsPage, SettingsSection, SettingsCard components - Add SettingsRow, SettingsToggle for interactive elements - Add SettingsDangerZone, SettingsDangerButton for destructive actions - Apply glass morphism styling matching PillNavigation - Migrate settings pages in manacore, presi, zitare apps - Migrate archived apps: maerchenzauber, memoro, nutriphi, uload 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3cfa6a765a
commit
7deb5b9a1e
16 changed files with 2391 additions and 1222 deletions
|
|
@ -1,24 +1,34 @@
|
|||
<script lang="ts">
|
||||
import { Card, Button, Input } from '@manacore/shared-ui';
|
||||
import { Button, Input } from '@manacore/shared-ui';
|
||||
import {
|
||||
SettingsPage,
|
||||
SettingsSection,
|
||||
SettingsCard,
|
||||
SettingsRow,
|
||||
SettingsDangerZone,
|
||||
SettingsDangerButton,
|
||||
} from '@manacore/shared-ui';
|
||||
import { enhance } from '$app/forms';
|
||||
|
||||
let { data, form } = $props();
|
||||
let loading = $state(false);
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="mb-8">
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Settings</h1>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Manage your account settings and preferences
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-2">
|
||||
<!-- Profile Settings -->
|
||||
<Card>
|
||||
<h2 class="mb-4 text-lg font-semibold text-gray-900 dark:text-white">Profile Information</h2>
|
||||
<SettingsPage title="Settings" subtitle="Manage your account settings and preferences.">
|
||||
<!-- Profile Section -->
|
||||
<SettingsSection title="Profile Information">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
|
||||
<SettingsCard>
|
||||
<form
|
||||
method="POST"
|
||||
action="?/updateProfile"
|
||||
|
|
@ -29,6 +39,7 @@
|
|||
loading = false;
|
||||
};
|
||||
}}
|
||||
class="p-5"
|
||||
>
|
||||
{#if form?.success}
|
||||
<div
|
||||
|
|
@ -50,7 +61,7 @@
|
|||
<div>
|
||||
<label
|
||||
for="email"
|
||||
class="mb-2 block text-sm font-medium text-gray-900 dark:text-gray-100"
|
||||
class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
|
|
@ -59,15 +70,15 @@
|
|||
id="email"
|
||||
value={data.session?.user?.email || ''}
|
||||
disabled
|
||||
class="bg-gray-50 dark:bg-gray-900"
|
||||
class="bg-[hsl(var(--muted))]"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Email cannot be changed</p>
|
||||
<p class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">Email cannot be changed</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
for="firstName"
|
||||
class="mb-2 block text-sm font-medium text-gray-900 dark:text-gray-100"
|
||||
class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]"
|
||||
>
|
||||
First Name
|
||||
</label>
|
||||
|
|
@ -83,7 +94,7 @@
|
|||
<div>
|
||||
<label
|
||||
for="lastName"
|
||||
class="mb-2 block text-sm font-medium text-gray-900 dark:text-gray-100"
|
||||
class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]"
|
||||
>
|
||||
Last Name
|
||||
</label>
|
||||
|
|
@ -101,74 +112,116 @@
|
|||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
<!-- Account Stats -->
|
||||
<Card>
|
||||
<h2 class="mb-4 text-lg font-semibold text-gray-900 dark:text-white">Account Information</h2>
|
||||
<!-- Account Info Section -->
|
||||
<SettingsSection title="Account Information">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<div class="mb-1 flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-gray-600 dark:text-gray-400"
|
||||
>Available Credits</span
|
||||
>
|
||||
<span class="text-2xl font-bold text-primary-600 dark:text-primary-400">
|
||||
{data.profile?.credits || 0}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsCard>
|
||||
<SettingsRow label="Available Credits">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
<span class="text-2xl font-bold text-[hsl(var(--primary))]">
|
||||
{data.profile?.credits || 0}
|
||||
</span>
|
||||
</SettingsRow>
|
||||
|
||||
<div class="border-t border-gray-200 pt-4 dark:border-gray-700">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-gray-600 dark:text-gray-400"
|
||||
>Subscription Plan</span
|
||||
>
|
||||
<span
|
||||
class="rounded-full bg-blue-100 px-3 py-1 text-xs font-medium text-blue-800 dark:bg-blue-900/20 dark:text-blue-400"
|
||||
>
|
||||
{data.profile?.subscription_plan_id || 'Free'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsRow label="Subscription Plan">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
<span class="rounded-full bg-blue-100 px-3 py-1 text-xs font-medium text-blue-800 dark:bg-blue-900/20 dark:text-blue-400">
|
||||
{data.profile?.subscription_plan_id || 'Free'}
|
||||
</span>
|
||||
</SettingsRow>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-gray-600 dark:text-gray-400"
|
||||
>Subscription Status</span
|
||||
>
|
||||
<span
|
||||
class="rounded-full {data.profile?.subscription_status === 'active'
|
||||
? 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400'
|
||||
: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-400'} px-3 py-1 text-xs font-medium"
|
||||
>
|
||||
{data.profile?.subscription_status || 'inactive'}
|
||||
</span>
|
||||
</div>
|
||||
<SettingsRow label="Subscription Status">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
<span
|
||||
class="rounded-full px-3 py-1 text-xs font-medium
|
||||
{data.profile?.subscription_status === 'active'
|
||||
? 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400'
|
||||
: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-400'}"
|
||||
>
|
||||
{data.profile?.subscription_status || 'inactive'}
|
||||
</span>
|
||||
</SettingsRow>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-gray-600 dark:text-gray-400">Member Since</span>
|
||||
<span class="text-sm text-gray-900 dark:text-white">
|
||||
{data.profile?.created_at
|
||||
? new Date(data.profile.created_at).toLocaleDateString()
|
||||
: 'N/A'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<SettingsRow label="Member Since" border={false}>
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
<span class="text-sm text-[hsl(var(--foreground))]">
|
||||
{data.profile?.created_at
|
||||
? new Date(data.profile.created_at).toLocaleDateString()
|
||||
: 'N/A'}
|
||||
</span>
|
||||
</SettingsRow>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
<!-- Danger Zone -->
|
||||
<Card class="border-red-200 dark:border-red-800">
|
||||
<h2 class="mb-4 text-lg font-semibold text-red-900 dark:text-red-400">Danger Zone</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h3 class="mb-2 font-medium text-gray-900 dark:text-white">Delete Account</h3>
|
||||
<p class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
||||
Once you delete your account, there is no going back. Please be certain.
|
||||
</p>
|
||||
<Button variant="danger" disabled>Delete Account</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Danger Zone -->
|
||||
<SettingsDangerZone title="Danger Zone">
|
||||
<SettingsDangerButton
|
||||
label="Delete Account"
|
||||
description="Once you delete your account, there is no going back. Please be certain."
|
||||
buttonText="Delete Account"
|
||||
onclick={() => {}}
|
||||
disabled
|
||||
border={false}
|
||||
>
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
</SettingsDangerButton>
|
||||
</SettingsDangerZone>
|
||||
</SettingsPage>
|
||||
|
|
|
|||
|
|
@ -2,125 +2,168 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import { auth } from '$lib/stores/auth.svelte';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import { User, Mail, Shield, LogOut, Sun, Moon, Monitor } from 'lucide-svelte';
|
||||
import {
|
||||
SettingsPage,
|
||||
SettingsSection,
|
||||
SettingsCard,
|
||||
SettingsRow,
|
||||
SettingsToggle,
|
||||
SettingsDangerZone,
|
||||
SettingsDangerButton,
|
||||
} from '@manacore/shared-ui';
|
||||
|
||||
function handleLogout() {
|
||||
auth.logout();
|
||||
goto('/login');
|
||||
}
|
||||
|
||||
function setThemeMode(mode: 'light' | 'dark' | 'system') {
|
||||
theme.setMode(mode);
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Settings - Presi</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<h1 class="text-2xl font-bold text-foreground mb-8">Settings</h1>
|
||||
<SettingsPage title="Settings" subtitle="Manage your account and preferences.">
|
||||
<!-- Account Section -->
|
||||
<SettingsSection title="Account">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Account Section -->
|
||||
<div
|
||||
class="bg-surface rounded-xl shadow-sm border border-border overflow-hidden"
|
||||
>
|
||||
<div class="p-4 border-b border-border">
|
||||
<h2 class="text-lg font-semibold text-foreground flex items-center gap-2">
|
||||
<User class="w-5 h-5 text-muted-foreground" />
|
||||
Account
|
||||
</h2>
|
||||
</div>
|
||||
<div class="p-4 space-y-4">
|
||||
<div class="flex items-center justify-between py-2">
|
||||
<div class="flex items-center gap-3">
|
||||
<Mail class="w-5 h-5 text-muted-foreground" />
|
||||
<div>
|
||||
<p class="text-sm font-medium text-foreground">Email</p>
|
||||
<p class="text-sm text-muted-foreground">{auth.user?.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-2">
|
||||
<div class="flex items-center gap-3">
|
||||
<Shield class="w-5 h-5 text-muted-foreground" />
|
||||
<div>
|
||||
<p class="text-sm font-medium text-foreground">User ID</p>
|
||||
<p class="text-sm text-muted-foreground font-mono">{auth.user?.id}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsCard>
|
||||
<SettingsRow label="Email" description={auth.user?.email || 'Not available'}>
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
</SettingsRow>
|
||||
<SettingsRow label="User ID" description={auth.user?.id || 'Not available'} border={false}>
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
<span class="font-mono text-xs text-[hsl(var(--muted-foreground))]">{auth.user?.id || '-'}</span>
|
||||
</SettingsRow>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
<!-- Appearance Section -->
|
||||
<div
|
||||
class="bg-surface rounded-xl shadow-sm border border-border overflow-hidden"
|
||||
>
|
||||
<div class="p-4 border-b border-border">
|
||||
<h2 class="text-lg font-semibold text-foreground flex items-center gap-2">
|
||||
<Sun class="w-5 h-5 text-muted-foreground" />
|
||||
Appearance
|
||||
</h2>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<p class="text-sm text-muted-foreground mb-4">Choose your preferred theme</p>
|
||||
<!-- Appearance Section -->
|
||||
<SettingsSection title="Appearance">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
|
||||
<SettingsCard>
|
||||
<div class="px-5 py-4">
|
||||
<p class="font-medium text-[hsl(var(--foreground))] mb-2">Theme</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))] mb-4">
|
||||
Choose your preferred theme
|
||||
</p>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<button
|
||||
onclick={() => theme.setMode('light')}
|
||||
class="flex flex-col items-center gap-2 p-4 rounded-lg border-2 transition-colors {theme.mode ===
|
||||
'light'
|
||||
? 'border-primary bg-primary/10'
|
||||
: 'border-border'}"
|
||||
onclick={() => setThemeMode('light')}
|
||||
class="flex flex-col items-center gap-2 p-4 rounded-lg border-2 transition-colors
|
||||
{theme.mode === 'light'
|
||||
? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.1)]'
|
||||
: 'border-[hsl(var(--border))]'}"
|
||||
>
|
||||
<Sun class="w-6 h-6 text-amber-500" />
|
||||
<span class="text-sm font-medium text-foreground">Light</span>
|
||||
<svg class="w-6 h-6 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-[hsl(var(--foreground))]">Light</span>
|
||||
</button>
|
||||
<button
|
||||
onclick={() => theme.setMode('dark')}
|
||||
class="flex flex-col items-center gap-2 p-4 rounded-lg border-2 transition-colors {theme.mode ===
|
||||
'dark'
|
||||
? 'border-primary bg-primary/10'
|
||||
: 'border-border'}"
|
||||
onclick={() => setThemeMode('dark')}
|
||||
class="flex flex-col items-center gap-2 p-4 rounded-lg border-2 transition-colors
|
||||
{theme.mode === 'dark'
|
||||
? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.1)]'
|
||||
: 'border-[hsl(var(--border))]'}"
|
||||
>
|
||||
<Moon class="w-6 h-6 text-indigo-500" />
|
||||
<span class="text-sm font-medium text-foreground">Dark</span>
|
||||
<svg class="w-6 h-6 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-[hsl(var(--foreground))]">Dark</span>
|
||||
</button>
|
||||
<button
|
||||
onclick={() => theme.setMode('system')}
|
||||
class="flex flex-col items-center gap-2 p-4 rounded-lg border-2 transition-colors {theme.mode ===
|
||||
'system'
|
||||
? 'border-primary bg-primary/10'
|
||||
: 'border-border'}"
|
||||
onclick={() => setThemeMode('system')}
|
||||
class="flex flex-col items-center gap-2 p-4 rounded-lg border-2 transition-colors
|
||||
{theme.mode === 'system'
|
||||
? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.1)]'
|
||||
: 'border-[hsl(var(--border))]'}"
|
||||
>
|
||||
<Monitor class="w-6 h-6 text-muted-foreground" />
|
||||
<span class="text-sm font-medium text-foreground">System</span>
|
||||
<svg class="w-6 h-6 text-[hsl(var(--muted-foreground))]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-[hsl(var(--foreground))]">System</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
<!-- Danger Zone -->
|
||||
<div
|
||||
class="bg-surface rounded-xl shadow-sm border border-red-300 dark:border-red-900/50 overflow-hidden"
|
||||
<!-- Danger Zone -->
|
||||
<SettingsDangerZone title="Danger Zone">
|
||||
<SettingsDangerButton
|
||||
label="Sign out"
|
||||
description="Sign out of your account on this device"
|
||||
buttonText="Sign out"
|
||||
onclick={handleLogout}
|
||||
border={false}
|
||||
>
|
||||
<div class="p-4 border-b border-red-300 dark:border-red-900/50 bg-red-50 dark:bg-red-900/20">
|
||||
<h2 class="text-lg font-semibold text-red-700 dark:text-red-400">Danger Zone</h2>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-foreground">Sign out</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Sign out of your account on this device
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onclick={handleLogout}
|
||||
class="flex items-center gap-2 px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
<LogOut class="w-4 h-4" />
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
</SettingsDangerButton>
|
||||
</SettingsDangerZone>
|
||||
</SettingsPage>
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@
|
|||
import { theme } from '$lib/stores/theme';
|
||||
import { THEME_DEFINITIONS } from '@manacore/shared-theme';
|
||||
import { ThemeColorPreview } from '@manacore/shared-theme-ui';
|
||||
import { Sparkle, Leaf, Hexagon, Waves } from '@manacore/shared-icons';
|
||||
|
||||
// Theme icon mapping
|
||||
const themeIcons = {
|
||||
sparkle: Sparkle,
|
||||
leaf: Leaf,
|
||||
hexagon: Hexagon,
|
||||
waves: Waves,
|
||||
} as const;
|
||||
import {
|
||||
SettingsPage,
|
||||
SettingsSection,
|
||||
SettingsCard,
|
||||
SettingsRow,
|
||||
SettingsToggle,
|
||||
SettingsDangerZone,
|
||||
SettingsDangerButton,
|
||||
} from '@manacore/shared-ui';
|
||||
|
||||
// Settings state
|
||||
let language = $state<'de' | 'en'>('de');
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
localStorage.setItem(key, String(value));
|
||||
}
|
||||
|
||||
function toggleDarkMode() {
|
||||
function toggleDarkMode(value: boolean) {
|
||||
theme.toggleMode();
|
||||
}
|
||||
|
||||
|
|
@ -59,388 +59,191 @@
|
|||
<title>Einstellungen - Zitare</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="settings-page">
|
||||
<div class="header-container">
|
||||
<h1>Einstellungen</h1>
|
||||
</div>
|
||||
|
||||
<SettingsPage title="Einstellungen" subtitle="Passe die App an deine Vorlieben an.">
|
||||
<!-- Personal Section -->
|
||||
<section class="settings-section">
|
||||
<h2 class="section-title">Persönlich</h2>
|
||||
<SettingsSection title="Persönlich">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-header">
|
||||
<h3>Dein Name</h3>
|
||||
<SettingsCard>
|
||||
<div class="px-5 py-4">
|
||||
<label class="block">
|
||||
<span class="font-medium text-[hsl(var(--foreground))] mb-2 block">Dein Name</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={userName}
|
||||
onblur={saveUserName}
|
||||
placeholder="Name eingeben..."
|
||||
class="w-full px-3 py-2 rounded-lg border-2 border-[hsl(var(--border))] bg-[hsl(var(--background))] text-[hsl(var(--foreground))] focus:border-[hsl(var(--primary))] focus:outline-none transition-colors"
|
||||
/>
|
||||
</label>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))] mt-2">
|
||||
Wird als Standard-Autor für eigene Zitate verwendet
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={userName}
|
||||
onblur={saveUserName}
|
||||
placeholder="Name eingeben..."
|
||||
class="text-input"
|
||||
/>
|
||||
<p class="setting-description">Wird als Standard-Autor für eigene Zitate verwendet</p>
|
||||
</div>
|
||||
</section>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
<!-- Appearance Section -->
|
||||
<section class="settings-section">
|
||||
<h2 class="section-title">Aussehen</h2>
|
||||
|
||||
<!-- Dark Mode Toggle -->
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-content">
|
||||
<h3>Dark Mode</h3>
|
||||
<p class="setting-description">Dunkles Farbschema verwenden</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" checked={theme.isDark} onchange={toggleDarkMode} />
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Theme -->
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-content">
|
||||
<h3>Aktuelles Theme</h3>
|
||||
<p class="setting-description theme-label">
|
||||
{#if THEME_DEFINITIONS[theme.variant].icon && themeIcons[THEME_DEFINITIONS[theme.variant].icon as keyof typeof themeIcons]}
|
||||
<svelte:component
|
||||
this={themeIcons[THEME_DEFINITIONS[theme.variant].icon as keyof typeof themeIcons]}
|
||||
size={16}
|
||||
weight="duotone"
|
||||
class="theme-icon"
|
||||
/>
|
||||
{/if}
|
||||
{THEME_DEFINITIONS[theme.variant].label}
|
||||
</p>
|
||||
</div>
|
||||
<button class="theme-btn" onclick={() => goto('/themes')}>
|
||||
Themes wählen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Theme Preview -->
|
||||
<div class="setting-card">
|
||||
<div class="setting-header">
|
||||
<h3>Farbvorschau</h3>
|
||||
<p class="setting-description">So sieht die App mit dem aktuellen Theme aus</p>
|
||||
</div>
|
||||
|
||||
<div class="theme-preview">
|
||||
<ThemeColorPreview
|
||||
variant={theme.variant}
|
||||
mode={theme.isDark ? 'dark' : 'light'}
|
||||
size="lg"
|
||||
<SettingsSection title="Aussehen">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
|
||||
<SettingsCard>
|
||||
<SettingsToggle
|
||||
label="Dark Mode"
|
||||
description="Dunkles Farbschema verwenden"
|
||||
isOn={theme.isDark}
|
||||
onToggle={toggleDarkMode}
|
||||
>
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
</SettingsToggle>
|
||||
|
||||
<SettingsRow
|
||||
label="Aktuelles Theme"
|
||||
description={THEME_DEFINITIONS[theme.variant].label}
|
||||
onclick={() => goto('/themes')}
|
||||
>
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
<span class="px-3 py-1.5 text-sm font-medium bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] rounded-lg">
|
||||
Themes wählen
|
||||
</span>
|
||||
</SettingsRow>
|
||||
|
||||
<div class="px-5 py-4 border-t border-[hsl(var(--border))]">
|
||||
<p class="font-medium text-[hsl(var(--foreground))] mb-2">Farbvorschau</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))] mb-4">
|
||||
So sieht die App mit dem aktuellen Theme aus
|
||||
</p>
|
||||
<div class="flex justify-center">
|
||||
<ThemeColorPreview
|
||||
variant={theme.variant}
|
||||
mode={theme.isDark ? 'dark' : 'light'}
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
<!-- Language Section -->
|
||||
<section class="settings-section">
|
||||
<h2 class="section-title">Sprache</h2>
|
||||
<SettingsSection title="Sprache">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<div class="setting-content">
|
||||
<h3>Sprache</h3>
|
||||
<p class="setting-description">Sprache der App und Zitate</p>
|
||||
</div>
|
||||
<div class="language-toggle">
|
||||
<button
|
||||
class="lang-btn"
|
||||
class:active={language === 'de'}
|
||||
onclick={() => setLanguageSetting('de')}
|
||||
>
|
||||
DE
|
||||
</button>
|
||||
<button
|
||||
class="lang-btn"
|
||||
class:active={language === 'en'}
|
||||
onclick={() => setLanguageSetting('en')}
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
<SettingsCard>
|
||||
<div class="px-5 py-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="font-medium text-[hsl(var(--foreground))]">Sprache</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">Sprache der App und Zitate</p>
|
||||
</div>
|
||||
<div class="flex rounded-full overflow-hidden border border-[hsl(var(--border))]">
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium transition-colors
|
||||
{language === 'de'
|
||||
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
|
||||
: 'bg-transparent text-[hsl(var(--foreground))]'}"
|
||||
onclick={() => setLanguageSetting('de')}
|
||||
>
|
||||
DE
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium transition-colors
|
||||
{language === 'en'
|
||||
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
|
||||
: 'bg-transparent text-[hsl(var(--foreground))]'}"
|
||||
onclick={() => setLanguageSetting('en')}
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
<!-- About Section -->
|
||||
<section class="settings-section">
|
||||
<h2 class="section-title">Über</h2>
|
||||
<SettingsSection title="Über">
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
|
||||
<div class="setting-card">
|
||||
<div class="setting-row">
|
||||
<span>Version</span>
|
||||
<span class="setting-value">1.0.0</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<SettingsCard>
|
||||
<SettingsRow label="Version" border={false}>
|
||||
<span class="text-[hsl(var(--muted-foreground))]">1.0.0</span>
|
||||
</SettingsRow>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
<!-- Data Section -->
|
||||
<section class="settings-section">
|
||||
<h2 class="section-title">Daten</h2>
|
||||
|
||||
<div class="setting-card danger">
|
||||
<button class="danger-btn" onclick={resetAllData}>
|
||||
<div>
|
||||
<h3 class="danger-title">Alle Daten zurücksetzen</h3>
|
||||
<p class="setting-description">Löscht Favoriten, Playlists und Einstellungen</p>
|
||||
</div>
|
||||
<span class="danger-icon">🗑️</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.settings-page {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: var(--spacing-2xl);
|
||||
}
|
||||
|
||||
.header-container {
|
||||
max-width: 700px;
|
||||
margin: 0 auto var(--spacing-2xl);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
max-width: 700px;
|
||||
margin: 0 auto var(--spacing-2xl);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: hsl(var(--muted-foreground));
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.setting-card {
|
||||
background: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-lg);
|
||||
margin-bottom: var(--spacing-md);
|
||||
transition:
|
||||
transform var(--transition-base),
|
||||
box-shadow var(--transition-base);
|
||||
}
|
||||
|
||||
.setting-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.setting-header {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.setting-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.setting-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
margin: 0 0 var(--spacing-xs) 0;
|
||||
}
|
||||
|
||||
.setting-description {
|
||||
font-size: 0.875rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.setting-description.theme-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.setting-description.theme-label :global(.theme-icon) {
|
||||
color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
.setting-value {
|
||||
color: hsl(var(--muted-foreground));
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* Text Input */
|
||||
.text-input {
|
||||
width: 100%;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border-radius: var(--radius-md);
|
||||
border: 2px solid hsl(var(--border));
|
||||
background: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
font-size: 1rem;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
transition: border-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.text-input:focus {
|
||||
outline: none;
|
||||
border-color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
/* Toggle Switch */
|
||||
.toggle {
|
||||
position: relative;
|
||||
width: 51px;
|
||||
height: 31px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggle input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: hsl(var(--border));
|
||||
transition: var(--transition-base);
|
||||
border-radius: 31px;
|
||||
}
|
||||
|
||||
.toggle-slider:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: white;
|
||||
transition: var(--transition-base);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.toggle input:checked + .toggle-slider {
|
||||
background-color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
.toggle input:checked + .toggle-slider:before {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
/* Theme Button */
|
||||
.theme-btn {
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border-radius: var(--radius-md);
|
||||
background: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background var(--transition-fast);
|
||||
}
|
||||
|
||||
.theme-btn:hover {
|
||||
background: hsl(var(--primary) / 0.9);
|
||||
}
|
||||
|
||||
/* Theme Preview */
|
||||
.theme-preview {
|
||||
margin-top: var(--spacing-md);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Language Toggle */
|
||||
.language-toggle {
|
||||
display: flex;
|
||||
border-radius: var(--radius-full);
|
||||
overflow: hidden;
|
||||
background: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.lang-btn {
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: hsl(var(--foreground));
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background var(--transition-fast);
|
||||
}
|
||||
|
||||
.lang-btn.active {
|
||||
background: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
/* Danger Zone */
|
||||
.setting-card.danger {
|
||||
background: hsl(var(--destructive) / 0.1);
|
||||
border-color: hsl(var(--destructive) / 0.2);
|
||||
}
|
||||
|
||||
.danger-btn {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.danger-title {
|
||||
color: hsl(var(--destructive));
|
||||
}
|
||||
|
||||
.danger-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
/* Mobile Responsiveness */
|
||||
@media (max-width: 768px) {
|
||||
.header-container,
|
||||
.settings-section {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<SettingsDangerZone title="Daten">
|
||||
<SettingsDangerButton
|
||||
label="Alle Daten zurücksetzen"
|
||||
description="Löscht Favoriten, Playlists und Einstellungen"
|
||||
buttonText="Zurücksetzen"
|
||||
onclick={resetAllData}
|
||||
border={false}
|
||||
>
|
||||
{#snippet icon()}
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
</SettingsDangerButton>
|
||||
</SettingsDangerZone>
|
||||
</SettingsPage>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue