swap improvised styling for cards

This commit is contained in:
Face 2025-07-15 18:24:54 +03:00
parent 63c6bea2b9
commit 63081f4c07
2 changed files with 145 additions and 87 deletions

View file

@ -1,29 +1,37 @@
<script lang="ts"> <script lang="ts">
import { Skeleton } from '$lib/components/ui/skeleton'; import { Skeleton } from '$lib/components/ui/skeleton';
import * as Card from '$lib/components/ui/card';
</script> </script>
<div class="grid gap-4 md:gap-6 xl:grid-cols-2"> <div class="grid gap-4 md:gap-6 xl:grid-cols-2">
<div class="flex flex-col xl:col-span-2"> <div class="flex flex-col xl:col-span-2">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{#each Array(9) as _} {#each Array(9) as _}
<div class="flex items-start gap-2 rounded-md border border-border bg-muted/50 transition ease duration-200 p-4"> <Card.Root class="gap-1">
<Skeleton class="h-14 w-14 rounded-full mr-2" /> <Card.Content>
<div class="flex flex-col flex-grow gap-2"> <div class="flex items-start gap-4">
<Skeleton class="h-5 w-32 md:h-5 md:w-40" /> <Skeleton class="h-12 w-12 rounded-full shrink-0" />
<Skeleton class="h-5 w-12 md:h-5 md:w-24" /> <div class="flex flex-col flex-grow gap-2">
<div class="flex flex-col gap-2"> <Skeleton class="h-4 w-32 md:h-4 md:w-40" />
<div class="flex items-center justify-between"> <Skeleton class="h-4 w-20 md:h-4 md:w-24" />
<Skeleton class="h-5 w-12 md:h-5 md:w-20" /> <div class="flex flex-col gap-1 mt-2">
<Skeleton class="h-5 w-12 md:h-5 md:w-20" /> <div class="flex items-center justify-between">
<Skeleton class="h-3 w-16 md:h-3 md:w-20" />
<Skeleton class="h-4 w-16 md:h-4 md:w-20" />
</div>
<div class="flex items-center justify-between">
<Skeleton class="h-3 w-12 md:h-3 md:w-16" />
<Skeleton class="h-4 w-16 md:h-4 md:w-20" />
</div>
</div>
<div class="flex items-center gap-2 mt-2">
<Skeleton class="h-4 w-4" />
<Skeleton class="h-3 w-24 md:h-3 md:w-32" />
</div>
</div> </div>
<div class="flex items-center justify-between">
<Skeleton class="h-5 w-12 md:h-5 md:w-20" />
<Skeleton class="h-5 w-12 md:h-5 md:w-20" />
</div>
<Skeleton class="h-5 w-20 md:h-5 md:w-32" />
</div> </div>
</div> </Card.Content>
</div> </Card.Root>
{/each} {/each}
</div> </div>
</div> </div>

View file

@ -7,7 +7,19 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { TrendingDown, Crown, Skull, Target, RefreshCw, Trophy, Search, SearchX, X, Wallet, Calendar } from 'lucide-svelte'; import {
TrendingDown,
Crown,
Skull,
Target,
RefreshCw,
Trophy,
Search,
SearchX,
X,
Wallet,
Calendar
} from 'lucide-svelte';
import { formatValue, getPublicUrl } from '$lib/utils'; import { formatValue, getPublicUrl } from '$lib/utils';
import Input from '$lib/components/ui/input/input.svelte'; import Input from '$lib/components/ui/input/input.svelte';
import UserProfilePreview from '$lib/components/self/UserProfilePreview.svelte'; import UserProfilePreview from '$lib/components/self/UserProfilePreview.svelte';
@ -100,7 +112,7 @@
username: row.username username: row.username
}) })
} }
] ];
const rugpullersColumns = [ const rugpullersColumns = [
{ {
@ -258,20 +270,36 @@
<p class="text-muted-foreground text-sm md:text-base">Top performers and market activity</p> <p class="text-muted-foreground text-sm md:text-base">Top performers and market activity</p>
</div> </div>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="flex items-center relative flex-grow"> <div class="relative flex flex-grow items-center">
<Search size={16} class="absolute left-3 pointer-events-none"></Search> <Search size={16} class="pointer-events-none absolute left-3"></Search>
<Input type="text" placeholder="Search by username..." class="pl-10 flex-grow" bind:value={searchQuery} onkeydown={handleSearchKeydown} /> <Input
type="text"
placeholder="Search by username..."
class="flex-grow pl-10"
bind:value={searchQuery}
onkeydown={handleSearchKeydown}
/>
</div> </div>
{#if searchQueryValue} {#if searchQueryValue}
<Button variant="outline" onclick={() => { <Button
searchQuery = ''; variant="outline"
searchQueryValue = ''; onclick={() => {
fetchLeaderboardData(); searchQuery = '';
}} disabled={loading} class="w-fit"> searchQueryValue = '';
fetchLeaderboardData();
}}
disabled={loading}
class="w-fit"
>
<X class="h-4 w-4" /> <X class="h-4 w-4" />
</Button> </Button>
{/if} {/if}
<Button variant="outline" onclick={() => fetchLeaderboardData(searchOffset)} disabled={loading} class="w-fit"> <Button
variant="outline"
onclick={() => fetchLeaderboardData(searchOffset)}
disabled={loading}
class="w-fit"
>
<RefreshCw class="h-4 w-4" /> <RefreshCw class="h-4 w-4" />
</Button> </Button>
</div> </div>
@ -287,7 +315,7 @@
<div class="flex h-96 items-center justify-center"> <div class="flex h-96 items-center justify-center">
<div class="text-center"> <div class="text-center">
<div class="text-muted-foreground mb-4 text-lg md:text-xl">Failed to load leaderboard</div> <div class="text-muted-foreground mb-4 text-lg md:text-xl">Failed to load leaderboard</div>
<Button onclick={fetchLeaderboardData}>Try Again</Button> <Button onclick={() => fetchLeaderboardData()}>Try Again</Button>
</div> </div>
</div> </div>
{:else} {:else}
@ -295,81 +323,103 @@
{#if searchQueryValue} {#if searchQueryValue}
{#if leaderboardData.results.length > 0} {#if leaderboardData.results.length > 0}
<div class="flex flex-col xl:col-span-2"> <div class="flex flex-col xl:col-span-2">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{#each leaderboardData.results as user} {#each leaderboardData.results as user}
<a href={`/user/${user.username}`} class="flex flex-col bg-muted rounded-md border border-border hover:bg-muted/50 transition ease duration-200"> <Card.Root
<div class="flex items-start gap-4 p-4"> class="hover:bg-muted/50 cursor-pointer gap-1 transition duration-200"
<Avatar.Root class="h-12 w-12 shrink-0"> onclick={() => goto(`/user/${user.username}`)}
<Avatar.Image src={getPublicUrl(user.image)} alt={user.name} /> >
<Avatar.Fallback class="text-sm">{user.name?.charAt(0) || '?'}</Avatar.Fallback> <Card.Content>
</Avatar.Root> <div class="flex items-start gap-4">
<div class="flex flex-col flex-grow"> <Avatar.Root class="h-12 w-12 shrink-0">
<div class="flex items-center gap-2"> <Avatar.Image src={getPublicUrl(user.image)} alt={user.name} />
<h4 class="max-w-[150px] truncate text-sm font-semibold sm:max-w-[200px]"> <Avatar.Fallback class="text-sm"
{user.name} >{user.name?.charAt(0) || '?'}</Avatar.Fallback
</h4> >
<ProfileBadges user={user} showId={true} size="sm"/> </Avatar.Root>
</div> <div class="flex flex-grow flex-col">
<p class="text-muted-foreground text-sm">@{user.username}</p> <div class="flex items-center gap-2">
<div class="flex flex-col gap-1 mt-2"> <h4 class="max-w-[150px] truncate text-sm font-semibold sm:max-w-[200px]">
<div class="flex items-center justify-between"> {user.name}
<span class="text-muted-foreground flex items-center gap-2 text-xs"> </h4>
<Wallet class="h-3 w-3" /> <ProfileBadges {user} showId={true} size="sm" />
Portfolio
</span>
<span class="font-mono text-sm font-medium">
{formatValue(user.totalPortfolioValue)}
</span>
</div> </div>
<p class="text-muted-foreground text-sm">@{user.username}</p>
<div class="flex items-center justify-between"> <div class="mt-2 flex flex-col gap-1">
<span class="text-muted-foreground flex items-center gap-2 text-xs"> <div class="flex items-center justify-between">
<Wallet class="h-3 w-3" /> <span class="text-muted-foreground flex items-center gap-2 text-xs">
Cash <Wallet class="h-3 w-3" />
</span> Portfolio
<span class="text-success font-mono text-sm font-medium"> </span>
{formatValue(user.baseCurrencyBalance)} <span class="font-mono text-sm font-medium">
</span> {formatValue(user.totalPortfolioValue)}
</span>
</div>
<div class="flex items-center justify-between">
<span class="text-muted-foreground flex items-center gap-2 text-xs">
<Wallet class="h-3 w-3" />
Cash
</span>
<span class="text-success font-mono text-sm font-medium">
{formatValue(user.baseCurrencyBalance)}
</span>
</div>
</div>
<div class="mt-2 flex items-center gap-2">
<Calendar class="text-muted-foreground h-4 w-4" />
<p class="text-muted-foreground text-xs">
Joined {new Date(user.createdAt).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long'
})}
</p>
</div> </div>
</div>
<div class="flex items-center gap-2 mt-2">
<Calendar class="h-4 w-4 text-muted-foreground" />
<p class="text-muted-foreground text-xs">Joined {new Date(user.createdAt).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long'
})}</p>
</div> </div>
</div> </div>
</div> </Card.Content>
</a> </Card.Root>
{/each} {/each}
</div> </div>
</div> </div>
<div class="flex flex-col gap-2 items-center justify-between xl:col-span-2 lg:flex-row"> <div class="flex flex-col items-center justify-between gap-2 lg:flex-row xl:col-span-2">
<h2 class="text-sm text-muted-foreground">Showing {1 + searchOffset} - {Math.min(leaderboardData.results.length, 9) + searchOffset} of {leaderboardData.total} results</h2> <h2 class="text-muted-foreground text-sm">
<div class="flex items-center w-full lg:w-auto justify-center lg:justify-end flex-grow gap-2 overflow-x-auto"> Showing {1 + searchOffset} - {Math.min(leaderboardData.results.length, 9) +
searchOffset} of {leaderboardData.total} results
</h2>
<div
class="flex w-full flex-grow items-center justify-center gap-2 overflow-x-auto lg:w-auto lg:justify-end"
>
{#each Array(Math.ceil(leaderboardData.total / 9)) as _, index} {#each Array(Math.ceil(leaderboardData.total / 9)) as _, index}
<Button variant={searchOffset === index * 9 ? 'outline' : 'ghost'} onclick={() => { <Button
if(searchOffset === index * 9) return; variant={searchOffset === index * 9 ? 'outline' : 'ghost'}
if(index * 9 > leaderboardData.total) return; onclick={() => {
if (searchOffset === index * 9) return;
if (index * 9 > leaderboardData.total) return;
searchOffset = index * 9; searchOffset = index * 9;
fetchLeaderboardData(searchOffset); fetchLeaderboardData(searchOffset);
}}> }}
<h2 class="text-sm">{ index + 1 }</h2> >
<h2 class="text-sm">{index + 1}</h2>
</Button> </Button>
{/each} {/each}
</div> </div>
</div> </div>
{:else} {:else}
<div class="flex flex-col xl:col-span-2 items-center justify-center h-60"> <div class="flex h-60 flex-col items-center justify-center xl:col-span-2">
<h2 class="mb-4 text-xl">No users found</h2> <h2 class="mb-4 text-xl">No users found</h2>
<p class="text-muted-foreground text-sm md:text-base mb-4">No users match your search "{searchQueryValue}"</p> <p class="text-muted-foreground mb-4 text-sm md:text-base">
<Button variant="outline" onclick={() => { No users match your search "{searchQueryValue}"
searchQuery = ''; </p>
searchQueryValue = ''; <Button
fetchLeaderboardData(); variant="outline"
}}> onclick={() => {
searchQuery = '';
searchQueryValue = '';
fetchLeaderboardData();
}}
>
<h2 class="text-sm">Clear search</h2> <h2 class="text-sm">Clear search</h2>
</Button> </Button>
</div> </div>