swap improvised styling for cards
This commit is contained in:
parent
63c6bea2b9
commit
63081f4c07
2 changed files with 145 additions and 87 deletions
|
|
@ -7,7 +7,19 @@
|
|||
import { onMount } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
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 Input from '$lib/components/ui/input/input.svelte';
|
||||
import UserProfilePreview from '$lib/components/self/UserProfilePreview.svelte';
|
||||
|
|
@ -100,7 +112,7 @@
|
|||
username: row.username
|
||||
})
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
const rugpullersColumns = [
|
||||
{
|
||||
|
|
@ -258,20 +270,36 @@
|
|||
<p class="text-muted-foreground text-sm md:text-base">Top performers and market activity</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center relative flex-grow">
|
||||
<Search size={16} class="absolute left-3 pointer-events-none"></Search>
|
||||
<Input type="text" placeholder="Search by username..." class="pl-10 flex-grow" bind:value={searchQuery} onkeydown={handleSearchKeydown} />
|
||||
<div class="relative flex flex-grow items-center">
|
||||
<Search size={16} class="pointer-events-none absolute left-3"></Search>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search by username..."
|
||||
class="flex-grow pl-10"
|
||||
bind:value={searchQuery}
|
||||
onkeydown={handleSearchKeydown}
|
||||
/>
|
||||
</div>
|
||||
{#if searchQueryValue}
|
||||
<Button variant="outline" onclick={() => {
|
||||
searchQuery = '';
|
||||
searchQueryValue = '';
|
||||
fetchLeaderboardData();
|
||||
}} disabled={loading} class="w-fit">
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => {
|
||||
searchQuery = '';
|
||||
searchQueryValue = '';
|
||||
fetchLeaderboardData();
|
||||
}}
|
||||
disabled={loading}
|
||||
class="w-fit"
|
||||
>
|
||||
<X class="h-4 w-4" />
|
||||
</Button>
|
||||
{/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" />
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -287,7 +315,7 @@
|
|||
<div class="flex h-96 items-center justify-center">
|
||||
<div class="text-center">
|
||||
<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>
|
||||
{:else}
|
||||
|
|
@ -295,81 +323,103 @@
|
|||
{#if searchQueryValue}
|
||||
{#if leaderboardData.results.length > 0}
|
||||
<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}
|
||||
<a href={`/user/${user.username}`} class="flex flex-col bg-muted rounded-md border border-border hover:bg-muted/50 transition ease duration-200">
|
||||
<div class="flex items-start gap-4 p-4">
|
||||
<Avatar.Root class="h-12 w-12 shrink-0">
|
||||
<Avatar.Image src={getPublicUrl(user.image)} alt={user.name} />
|
||||
<Avatar.Fallback class="text-sm">{user.name?.charAt(0) || '?'}</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div class="flex flex-col flex-grow">
|
||||
<div class="flex items-center gap-2">
|
||||
<h4 class="max-w-[150px] truncate text-sm font-semibold sm:max-w-[200px]">
|
||||
{user.name}
|
||||
</h4>
|
||||
<ProfileBadges user={user} showId={true} size="sm"/>
|
||||
</div>
|
||||
<p class="text-muted-foreground text-sm">@{user.username}</p>
|
||||
<div class="flex flex-col gap-1 mt-2">
|
||||
<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" />
|
||||
Portfolio
|
||||
</span>
|
||||
<span class="font-mono text-sm font-medium">
|
||||
{formatValue(user.totalPortfolioValue)}
|
||||
</span>
|
||||
<Card.Root
|
||||
class="hover:bg-muted/50 cursor-pointer gap-1 transition duration-200"
|
||||
onclick={() => goto(`/user/${user.username}`)}
|
||||
>
|
||||
<Card.Content>
|
||||
<div class="flex items-start gap-4">
|
||||
<Avatar.Root class="h-12 w-12 shrink-0">
|
||||
<Avatar.Image src={getPublicUrl(user.image)} alt={user.name} />
|
||||
<Avatar.Fallback class="text-sm"
|
||||
>{user.name?.charAt(0) || '?'}</Avatar.Fallback
|
||||
>
|
||||
</Avatar.Root>
|
||||
<div class="flex flex-grow flex-col">
|
||||
<div class="flex items-center gap-2">
|
||||
<h4 class="max-w-[150px] truncate text-sm font-semibold sm:max-w-[200px]">
|
||||
{user.name}
|
||||
</h4>
|
||||
<ProfileBadges {user} showId={true} size="sm" />
|
||||
</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>
|
||||
<p class="text-muted-foreground text-sm">@{user.username}</p>
|
||||
<div class="mt-2 flex flex-col gap-1">
|
||||
<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" />
|
||||
Portfolio
|
||||
</span>
|
||||
<span class="font-mono text-sm font-medium">
|
||||
{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 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>
|
||||
</a>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 items-center justify-between xl:col-span-2 lg:flex-row">
|
||||
<h2 class="text-sm text-muted-foreground">Showing {1 + searchOffset} - {Math.min(leaderboardData.results.length, 9) + searchOffset} of {leaderboardData.total} results</h2>
|
||||
<div class="flex items-center w-full lg:w-auto justify-center lg:justify-end flex-grow gap-2 overflow-x-auto">
|
||||
<div class="flex flex-col items-center justify-between gap-2 lg:flex-row xl:col-span-2">
|
||||
<h2 class="text-muted-foreground text-sm">
|
||||
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}
|
||||
<Button variant={searchOffset === index * 9 ? 'outline' : 'ghost'} onclick={() => {
|
||||
if(searchOffset === index * 9) return;
|
||||
if(index * 9 > leaderboardData.total) return;
|
||||
<Button
|
||||
variant={searchOffset === index * 9 ? 'outline' : 'ghost'}
|
||||
onclick={() => {
|
||||
if (searchOffset === index * 9) return;
|
||||
if (index * 9 > leaderboardData.total) return;
|
||||
|
||||
searchOffset = index * 9;
|
||||
fetchLeaderboardData(searchOffset);
|
||||
}}>
|
||||
<h2 class="text-sm">{ index + 1 }</h2>
|
||||
searchOffset = index * 9;
|
||||
fetchLeaderboardData(searchOffset);
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm">{index + 1}</h2>
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{: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>
|
||||
<p class="text-muted-foreground text-sm md:text-base mb-4">No users match your search "{searchQueryValue}"</p>
|
||||
<Button variant="outline" onclick={() => {
|
||||
searchQuery = '';
|
||||
searchQueryValue = '';
|
||||
fetchLeaderboardData();
|
||||
}}>
|
||||
<p class="text-muted-foreground mb-4 text-sm md:text-base">
|
||||
No users match your search "{searchQueryValue}"
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => {
|
||||
searchQuery = '';
|
||||
searchQueryValue = '';
|
||||
fetchLeaderboardData();
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm">Clear search</h2>
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
Reference in a new issue