Merge branch 'outpoot:main' into main

This commit is contained in:
Max 2025-06-15 16:48:46 +02:00 committed by GitHub
commit 5e507c3df2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 6196 additions and 109 deletions

View file

@ -31,7 +31,8 @@
Hammer,
BookOpen,
Info,
Bell
Bell,
Crown
} from 'lucide-svelte';
import { mode, setMode } from 'mode-watcher';
import type { HTMLAttributes } from 'svelte/elements';
@ -85,7 +86,7 @@
function handleModeToggle() {
setMode(mode.current === 'light' ? 'dark' : 'light');
setOpenMobile(false);
// Remove setOpenMobile(false) to keep menu open
}
function formatCurrency(value: number): string {
@ -152,6 +153,11 @@
showUserManual = true;
setOpenMobile(false);
}
function handlePrestigeClick() {
goto('/prestige');
setOpenMobile(false);
}
</script>
<SignInConfirmDialog bind:open={shouldSignIn} />
@ -195,22 +201,6 @@
</Sidebar.MenuButton>
</Sidebar.MenuItem>
{/each}
<Sidebar.MenuItem>
<Sidebar.MenuButton>
{#snippet child({ props }: { props: MenuButtonProps })}
<button onclick={handleModeToggle} {...props}>
{#if mode.current === 'light'}
<Moon class="h-5 w-5" />
<span>Dark Mode</span>
{:else}
<Sun class="h-5 w-5" />
<span>Light Mode</span>
{/if}
</button>
{/snippet}
</Sidebar.MenuButton>
</Sidebar.MenuItem>
</Sidebar.Menu>
</Sidebar.GroupContent>
</Sidebar.Group>
@ -421,6 +411,8 @@
</div>
</DropdownMenu.Label>
<DropdownMenu.Separator />
<!-- Profile & Settings Group -->
<DropdownMenu.Group>
<DropdownMenu.Item onclick={handleAccountClick}>
<User />
@ -430,10 +422,16 @@
<Settings />
Settings
</DropdownMenu.Item>
<DropdownMenu.Item onclick={handleUserManualClick}>
<BookOpen />
User Manual
<DropdownMenu.Item onclick={handlePrestigeClick}>
<Crown />
Prestige
</DropdownMenu.Item>
</DropdownMenu.Group>
<DropdownMenu.Separator />
<!-- Features Group -->
<DropdownMenu.Group>
<DropdownMenu.Item
onclick={() => {
showPromoCode = true;
@ -443,10 +441,24 @@
<Gift />
Promo code
</DropdownMenu.Item>
<DropdownMenu.Item onclick={handleUserManualClick}>
<BookOpen />
User Manual
</DropdownMenu.Item>
<DropdownMenu.Item onclick={handleModeToggle}>
{#if mode.current === 'light'}
<Moon />
Dark Mode
{:else}
<Sun />
Light Mode
{/if}
</DropdownMenu.Item>
</DropdownMenu.Group>
{#if $USER_DATA?.isAdmin}
<DropdownMenu.Separator />
<!-- Admin Group -->
<DropdownMenu.Group>
<DropdownMenu.Item
onclick={handleAdminClick}
@ -471,8 +483,11 @@
</DropdownMenu.Item>
</DropdownMenu.Group>
{/if}
<DropdownMenu.Separator />
<!-- Legal Group -->
<DropdownMenu.Group>
<DropdownMenu.Separator />
<DropdownMenu.Item onclick={handleTermsClick}>
<Scale />
Terms of Service
@ -482,7 +497,10 @@
Privacy Policy
</DropdownMenu.Item>
</DropdownMenu.Group>
<DropdownMenu.Separator />
<!-- Sign Out -->
<DropdownMenu.Item
onclick={() => {
signOut().then(() => {

View file

@ -1,7 +1,8 @@
<script lang="ts">
import type { UserProfile } from '$lib/types/user-profile';
import SilentBadge from './SilentBadge.svelte';
import { Hash, Hammer, Flame } from 'lucide-svelte';
import { Hash, Hammer, Flame, Star } from 'lucide-svelte';
import { getPrestigeName, getPrestigeColor } from '$lib/utils';
let {
user,
@ -14,14 +15,23 @@
} = $props();
let badgeClass = $derived(size === 'sm' ? 'text-xs' : '');
let prestigeName = $derived(user.prestigeLevel ? getPrestigeName(user.prestigeLevel) : null);
let prestigeColor = $derived(user.prestigeLevel ? getPrestigeColor(user.prestigeLevel) : 'text-gray-500');
</script>
<div class="flex items-center gap-1">
{#if showId}
<SilentBadge icon={Hash} class="text-muted-foreground {badgeClass}" text="#{user.id} to join" />
{/if}
{#if prestigeName}
<SilentBadge icon={Star} text={prestigeName} class="{prestigeColor} {badgeClass}" />
{/if}
{#if user.loginStreak && user.loginStreak > 1}
<SilentBadge icon={Flame} text="{user.loginStreak} day streak" class="text-orange-500 {badgeClass}" />
<SilentBadge
icon={Flame}
text="{user.loginStreak} day streak"
class="text-orange-500 {badgeClass}"
/>
{/if}
{#if user.isAdmin}
<SilentBadge icon={Hammer} text="Admin" class="text-primary {badgeClass}" />

View file

@ -0,0 +1,150 @@
<script lang="ts">
import * as Card from '$lib/components/ui/card';
import { Skeleton } from '$lib/components/ui/skeleton';
import * as Avatar from '$lib/components/ui/avatar';
import { Star } from 'lucide-svelte';
</script>
<div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
<!-- Main Content Column -->
<div class="flex flex-col lg:col-span-2">
<!-- How Card Skeleton -->
<Card.Root class="mb-6 gap-1">
<Card.Header class="pb-2">
<Card.Title class="text-base">How</Card.Title>
</Card.Header>
<Card.Content class="space-y-4">
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
{#each Array(3) as _, i}
<div class="flex gap-3">
<div
class="bg-primary/10 text-primary flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-sm font-medium"
>
{i + 1}
</div>
<div class="flex-1 space-y-2">
<Skeleton class="h-4 w-24" />
<Skeleton class="h-3 w-full" />
<Skeleton class="h-3 w-3/4" />
</div>
</div>
{/each}
</div>
</Card.Content>
</Card.Root>
<!-- Progress Card Skeleton -->
<Card.Root class="flex flex-1 flex-col gap-1">
<Card.Header>
<Card.Title class="flex items-center gap-2">
<Star class="h-5 w-5" />
Progress
</Card.Title>
</Card.Header>
<Card.Content class="flex flex-1 flex-col space-y-6">
<!-- Progress Section -->
<div class="space-y-6">
<div class="space-y-2">
<div class="flex items-center justify-between text-sm">
<Skeleton class="h-4 w-32" />
<Skeleton class="h-4 w-12" />
</div>
<Skeleton class="h-2 w-full rounded-full" />
</div>
<!-- Financial Details Table Skeleton -->
<div class="overflow-hidden rounded-xl border">
<table class="w-full text-sm">
<tbody class="divide-y">
{#each Array(3) as _}
<tr>
<td class="px-3 py-2">
<Skeleton class="h-4 w-20" />
</td>
<td class="px-3 py-2 text-right">
<Skeleton class="ml-auto h-4 w-24" />
</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
<Skeleton class="h-4 w-40" />
<!-- Prestige Button Skeleton -->
<Skeleton class="h-12 w-full rounded-lg" />
</Card.Content>
</Card.Root>
</div>
<!-- Right Column - Info -->
<div class="flex flex-col space-y-4">
<!-- Profile Preview Card Skeleton -->
<Card.Root class="flex-1 gap-1">
<Card.Header class="pb-2">
<Card.Title class="text-base">Preview</Card.Title>
</Card.Header>
<Card.Content class="space-y-4">
<!-- Current Profile -->
<div class="space-y-2">
<Skeleton class="h-3 w-12" />
<div class="flex items-center gap-3 rounded-lg border p-3">
<Avatar.Root class="h-10 w-10 shrink-0">
<Avatar.Fallback>
<Skeleton class="h-full w-full rounded-full" />
</Avatar.Fallback>
</Avatar.Root>
<div class="min-w-0 flex-1 space-y-1">
<div class="flex min-w-0 items-center gap-2">
<Skeleton class="h-4 w-20" />
<Skeleton class="h-4 w-16" />
</div>
<Skeleton class="h-3 w-16" />
</div>
</div>
</div>
<!-- Prestige Preview -->
<div class="space-y-2">
<Skeleton class="h-3 w-10" />
<div
class="flex items-center gap-3 rounded-lg border-2 border-yellow-500/30 bg-yellow-50/50 p-3 dark:bg-yellow-950/20"
>
<Avatar.Root class="h-10 w-10 shrink-0">
<Avatar.Fallback>
<Skeleton class="h-full w-full rounded-full" />
</Avatar.Fallback>
</Avatar.Root>
<div class="min-w-0 flex-1 space-y-1">
<div class="flex min-w-0 items-center gap-2">
<Skeleton class="h-4 w-20" />
<Skeleton class="h-4 w-20" />
</div>
<Skeleton class="h-3 w-16" />
</div>
</div>
</div>
</Card.Content>
</Card.Root>
<!-- All Prestige Levels Skeleton -->
<Card.Root class="flex-1 gap-1">
<Card.Header class="pb-2">
<Card.Title class="text-base">Levels</Card.Title>
</Card.Header>
<Card.Content class="space-y-1">
{#each Array(5) as _}
<div class="flex items-center justify-between py-1">
<div class="flex items-center gap-2">
<Skeleton class="h-4 w-4" />
<Skeleton class="h-4 w-20" />
</div>
<Skeleton class="h-3 w-16" />
</div>
{/each}
</Card.Content>
</Card.Root>
</div>
</div>

View file

@ -0,0 +1,7 @@
import Root from "./progress.svelte";
export {
Root,
//
Root as Progress,
};

View file

@ -0,0 +1,27 @@
<script lang="ts">
import { Progress as ProgressPrimitive } from "bits-ui";
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
max = 100,
value,
...restProps
}: WithoutChildrenOrChild<ProgressPrimitive.RootProps> = $props();
</script>
<ProgressPrimitive.Root
bind:ref
data-slot="progress"
class={cn("bg-primary/20 relative h-2 w-full overflow-hidden rounded-full", className)}
{value}
{max}
{...restProps}
>
<div
data-slot="progress-indicator"
class="bg-primary h-full w-full flex-1 transition-all"
style="transform: translateX(-{100 - (100 * (value ?? 0)) / (max ?? 1)}%)"
></div>
</ProgressPrimitive.Root>