style: Redesign players page with neon esports aesthetic

Convert the players page and related components from DaisyUI to the
custom neon design system, matching the landing page and matches page
visual style. Adds decorative blur orbs, neon glow effects, and
consistent color semantics across PlayerCard, RecentPlayers, and
TrackPlayerModal components.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-07 17:18:01 +01:00
parent 6dc12f0c35
commit 8de8f1696f
4 changed files with 146 additions and 108 deletions

View File

@@ -1,6 +1,5 @@
<script lang="ts">
import { User, TrendingUp, Target } from 'lucide-svelte';
import Badge from '$lib/components/ui/Badge.svelte';
import type { PlayerMeta } from '$lib/types';
interface Props {
@@ -10,26 +9,28 @@
let { player, showStats = true }: Props = $props();
const kd =
const killDeathRatio =
player.avg_deaths > 0
? (player.avg_kills / player.avg_deaths).toFixed(2)
: player.avg_kills.toFixed(2);
const winRate = (player.win_rate * 100).toFixed(1);
const winRatePercentage = (player.win_rate * 100).toFixed(1);
</script>
<a
href={`/player/${player.id}`}
class="block overflow-hidden rounded-lg border border-base-300 bg-base-100 shadow-md transition-all hover:scale-[1.02] hover:shadow-xl"
class="block overflow-hidden rounded-lg border border-l-4 border-white/10 border-l-neon-blue bg-void-light transition-all duration-300 hover:scale-[1.02] hover:border-neon-blue/50 hover:shadow-[0_0_20px_rgba(0,212,255,0.1)]"
>
<!-- Header -->
<div class="bg-gradient-to-r from-primary/20 to-secondary/20 p-4">
<div class="bg-gradient-to-r from-neon-blue/20 to-neon-purple/20 p-4">
<div class="flex items-center gap-3">
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-base-100">
<User class="h-6 w-6 text-primary" />
<div
class="flex h-12 w-12 items-center justify-center rounded-full border border-neon-blue/30 bg-void"
>
<User class="h-6 w-6 text-neon-blue" />
</div>
<div class="min-w-0 flex-1">
<h3 class="truncate text-lg font-bold text-base-content">{player.name}</h3>
<p class="text-sm text-base-content/60">ID: {player.id}</p>
<h3 class="truncate text-lg font-bold text-white">{player.name}</h3>
<p class="text-sm text-white/50">ID: {player.id}</p>
</div>
</div>
</div>
@@ -39,34 +40,38 @@
<div class="grid grid-cols-3 gap-4 p-4">
<div class="text-center">
<div class="mb-1 flex items-center justify-center">
<Target class="mr-1 h-4 w-4 text-primary" />
<Target class="mr-1 h-4 w-4 text-neon-gold" />
</div>
<div class="text-xl font-bold text-base-content">{kd}</div>
<div class="text-xs text-base-content/60">K/D</div>
<div class="font-mono text-xl font-bold text-white">{killDeathRatio}</div>
<div class="text-xs text-white/50">K/D</div>
</div>
<div class="text-center">
<div class="mb-1 flex items-center justify-center">
<TrendingUp class="mr-1 h-4 w-4 text-success" />
<TrendingUp class="mr-1 h-4 w-4 text-neon-green" />
</div>
<div class="text-xl font-bold text-base-content">{winRate}%</div>
<div class="text-xs text-base-content/60">Win Rate</div>
<div class="font-mono text-xl font-bold text-white">{winRatePercentage}%</div>
<div class="text-xs text-white/50">Win Rate</div>
</div>
<div class="text-center">
<div class="mb-1 flex items-center justify-center">
<User class="mr-1 h-4 w-4 text-info" />
<User class="mr-1 h-4 w-4 text-neon-blue" />
</div>
<div class="text-xl font-bold text-base-content">{player.recent_matches}</div>
<div class="text-xs text-base-content/60">Matches</div>
<div class="font-mono text-xl font-bold text-white">{player.recent_matches}</div>
<div class="text-xs text-white/50">Matches</div>
</div>
</div>
<!-- Footer -->
<div class="border-t border-base-300 bg-base-200 px-4 py-3">
<div class="border-t border-white/10 bg-void px-4 py-3">
<div class="flex items-center justify-between text-sm">
<span class="text-base-content/60">Avg KAST:</span>
<Badge variant="info" size="sm">{player.avg_kast.toFixed(1)}%</Badge>
<span class="text-white/50">Avg KAST:</span>
<span
class="rounded-full border border-neon-blue/30 bg-neon-blue/10 px-2.5 py-0.5 text-xs font-medium text-neon-blue"
>
{player.avg_kast.toFixed(1)}%
</span>
</div>
</div>
{/if}

View File

@@ -1,6 +1,5 @@
<script lang="ts">
import { Clock, X } from 'lucide-svelte';
import Card from '$lib/components/ui/Card.svelte';
import { onMount } from 'svelte';
import {
getRecentPlayers,
@@ -34,32 +33,32 @@
</script>
{#if recentPlayers.length > 0}
<Card padding="lg">
<div class="rounded-xl border border-white/10 bg-void-light p-6">
<div class="mb-4 flex items-center gap-2">
<Clock class="h-5 w-5 text-primary" />
<h2 class="text-xl font-bold text-base-content">Recently Visited Players</h2>
<Clock class="h-5 w-5 text-neon-blue" />
<h2 class="text-xl font-bold text-white">Recently Visited Players</h2>
</div>
<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{#each recentPlayers as player (player.id)}
<div
class="group relative rounded-lg border border-base-300 bg-base-200 p-3 transition-all hover:border-primary hover:shadow-lg"
class="group relative rounded-lg border border-white/10 bg-void p-3 transition-all duration-300 hover:border-neon-blue/50 hover:shadow-[0_0_15px_rgba(0,212,255,0.1)]"
>
<a href="/player/{player.id}" class="flex items-center gap-3">
<img
src={player.avatar}
alt={player.name}
class="h-12 w-12 rounded-full border-2 border-base-300"
class="h-12 w-12 rounded-full border-2 border-neon-blue/30"
/>
<div class="flex-1 overflow-hidden">
<div class="truncate font-medium text-base-content">{player.name}</div>
<div class="text-xs text-base-content/60">{formatTimeAgo(player.visitedAt)}</div>
<div class="truncate font-medium text-white">{player.name}</div>
<div class="text-xs text-white/50">{formatTimeAgo(player.visitedAt)}</div>
</div>
</a>
<!-- Remove button -->
<button
class="btn btn-circle btn-ghost btn-xs absolute right-1 top-1 opacity-0 transition-opacity group-hover:opacity-100"
class="absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full text-white/40 opacity-0 transition-all duration-200 hover:bg-neon-red/20 hover:text-neon-red group-hover:opacity-100"
onclick={(e) => {
e.preventDefault();
handleRemove(player.id);
@@ -72,10 +71,10 @@
{/each}
</div>
<div class="mt-4 text-center text-xs text-base-content/60">
<div class="mt-4 text-center text-xs text-white/40">
Showing up to {recentPlayers.length} recently visited player{recentPlayers.length !== 1
? 's'
: ''}
</div>
</Card>
</div>
{/if}

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { Info, AlertCircle, Loader2 } from 'lucide-svelte';
import Modal from '$lib/components/ui/Modal.svelte';
import { playersAPI } from '$lib/api/players';
import { toast } from '$lib/stores/toast';
@@ -78,26 +79,18 @@
<Modal bind:open onClose={handleClose} title={isTracked ? 'Untrack Player' : 'Track Player'}>
<div class="space-y-4">
<div class="alert alert-info">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="h-6 w-6 shrink-0 stroke-current"
>
<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"
></path>
</svg>
<div class="text-sm">
<!-- Info Alert -->
<div class="flex items-start gap-3 rounded-lg border border-neon-blue/30 bg-neon-blue/10 p-4">
<Info class="h-5 w-5 shrink-0 text-neon-blue" />
<div class="text-sm text-neon-blue">
{#if isTracked}
<p>Remove <strong>{playerName}</strong> from automatic match tracking.</p>
<p>
Remove <strong class="font-semibold">{playerName}</strong> from automatic match tracking.
</p>
{:else}
<p>
Add <strong>{playerName}</strong> to the tracking system to automatically fetch new matches.
Add <strong class="font-semibold">{playerName}</strong> to the tracking system to automatically
fetch new matches.
</p>
{/if}
</div>
@@ -105,73 +98,77 @@
<!-- Auth Code Input (only for tracking, untrack doesn't need auth) -->
{#if !isTracked}
<div class="form-control">
<label class="label" for="authCode">
<span class="label-text font-medium">Authentication Code *</span>
<div class="space-y-2">
<label class="block text-sm font-medium text-white" for="authCode">
Authentication Code *
</label>
<input
id="authCode"
type="text"
placeholder="Enter your auth code"
class="input input-bordered w-full"
class="w-full rounded-lg border border-neon-blue/30 bg-void px-4 py-2.5 text-white placeholder-white/40 transition-all duration-300 focus:border-neon-blue focus:outline-none focus:ring-1 focus:ring-neon-blue disabled:opacity-50"
bind:value={authCode}
disabled={isLoading}
required
/>
<div class="label">
<span class="label-text-alt text-base-content/60">
Required to verify ownership of this Steam account
</span>
</div>
<p class="text-xs text-white/50">Required to verify ownership of this Steam account</p>
</div>
{/if}
<!-- Error Message -->
{#if error}
<div class="alert alert-error">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span>{error}</span>
<div class="flex items-start gap-3 rounded-lg border border-neon-red/30 bg-neon-red/10 p-4">
<AlertCircle class="h-5 w-5 shrink-0 text-neon-red" />
<span class="text-sm text-neon-red">{error}</span>
</div>
{/if}
<!-- Help Text -->
<div class="text-sm text-base-content/70">
<p class="mb-2 font-medium">How to get your authentication code:</p>
<div class="text-sm text-white/60">
<p class="mb-2 font-medium text-white/70">How to get your authentication code:</p>
<ol class="list-inside list-decimal space-y-1">
<li>Open CS2 and go to Settings → Game</li>
<li>Enable the Developer Console</li>
<li>Press <kbd class="kbd kbd-sm">~</kbd> to open the console</li>
<li>Type: <code class="rounded bg-base-300 px-1">status</code></li>
<li>
Press <kbd class="rounded border border-white/20 bg-void px-2 py-0.5 font-mono text-xs"
>~</kbd
> to open the console
</li>
<li>
Type: <code class="rounded bg-void px-1.5 py-0.5 font-mono text-neon-blue">status</code>
</li>
<li>Copy the code shown next to "Account:"</li>
</ol>
</div>
</div>
{#snippet actions()}
<button class="btn" onclick={handleClose} disabled={isLoading}>Cancel</button>
<button
class="rounded-lg border border-white/20 px-4 py-2 text-sm font-medium text-white/70 transition-all duration-300 hover:bg-white/10 hover:text-white disabled:opacity-50"
onclick={handleClose}
disabled={isLoading}
>
Cancel
</button>
{#if isTracked}
<button class="btn btn-error" onclick={handleUntrack} disabled={isLoading}>
<button
class="flex items-center gap-2 rounded-lg bg-neon-red px-4 py-2 text-sm font-medium text-white transition-all duration-300 hover:shadow-[0_0_20px_rgba(255,51,102,0.4)] disabled:opacity-50"
onclick={handleUntrack}
disabled={isLoading}
>
{#if isLoading}
<span class="loading loading-spinner loading-sm"></span>
<Loader2 class="h-4 w-4 animate-spin" />
{/if}
Untrack Player
</button>
{:else}
<button class="btn btn-primary" onclick={handleTrack} disabled={isLoading}>
<button
class="flex items-center gap-2 rounded-lg bg-neon-blue px-4 py-2 text-sm font-medium text-void transition-all duration-300 hover:shadow-[0_0_20px_rgba(0,212,255,0.4)] disabled:opacity-50"
onclick={handleTrack}
disabled={isLoading}
>
{#if isLoading}
<span class="loading loading-spinner loading-sm"></span>
<Loader2 class="h-4 w-4 animate-spin" />
{/if}
Track Player
</button>

View File

@@ -1,41 +1,78 @@
<script lang="ts">
import { Search, TrendingUp } from 'lucide-svelte';
import Card from '$lib/components/ui/Card.svelte';
import Badge from '$lib/components/ui/Badge.svelte';
</script>
<svelte:head>
<title>Players - teamflash.rip</title>
</svelte:head>
<div class="container mx-auto px-4 py-8">
<div class="mb-8">
<h1 class="mb-2 text-4xl font-bold">Players</h1>
<p class="text-base-content/60">Search and browse player profiles</p>
<div class="relative min-h-screen bg-void">
<!-- Decorative Background -->
<div class="pointer-events-none absolute inset-0 overflow-hidden">
<!-- Blur orbs -->
<div class="absolute -left-40 top-20 h-80 w-80 rounded-full bg-neon-blue/10 blur-[100px]"></div>
<div
class="absolute -right-40 top-60 h-80 w-80 rounded-full bg-neon-purple/10 blur-[100px]"
></div>
<div
class="absolute bottom-40 left-1/3 h-60 w-60 rounded-full bg-neon-gold/5 blur-[80px]"
></div>
<!-- Grid pattern -->
<div
class="absolute inset-0 opacity-20"
style="background-image: linear-gradient(rgba(0, 212, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 212, 255, 0.03) 1px, transparent 1px); background-size: 60px 60px;"
></div>
</div>
<!-- Search -->
<Card padding="lg" class="mb-8">
<div class="relative">
<Search class="absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-base-content/40" />
<input
type="text"
placeholder="Search by Steam ID or player name..."
class="input input-bordered w-full pl-10"
/>
<!-- Content -->
<div class="container relative z-10 mx-auto px-4 py-8">
<!-- Page Header -->
<div class="mb-8">
<h1
class="mb-2 text-4xl font-bold text-white"
style="text-shadow: 0 0 30px rgba(0, 212, 255, 0.5);"
>
Players
</h1>
<p class="text-white/60">Search and browse player profiles</p>
</div>
</Card>
<!-- Coming Soon -->
<div
class="flex min-h-[400px] items-center justify-center rounded-lg border-2 border-dashed border-base-300 bg-base-200/50"
>
<div class="text-center">
<TrendingUp class="mx-auto mb-4 h-16 w-16 text-base-content/20" />
<h2 class="mb-2 text-2xl font-bold text-base-content">Coming Soon</h2>
<p class="text-base-content/60">Player search and profiles will be available in Phase 3</p>
<div class="mt-6">
<Badge variant="info">Phase 3 - In Development</Badge>
<!-- Search Section -->
<div class="mb-8 rounded-xl border border-white/10 bg-void-light p-6">
<div class="relative">
<Search class="absolute left-4 top-1/2 h-5 w-5 -translate-y-1/2 text-white/40" />
<input
type="text"
placeholder="Search by Steam ID or player name..."
class="w-full rounded-lg border border-neon-blue/30 bg-void px-4 py-3 pl-12 text-white placeholder-white/40 transition-all duration-300 focus:border-neon-blue focus:outline-none focus:ring-1 focus:ring-neon-blue"
/>
</div>
</div>
<!-- Coming Soon Section -->
<div
class="flex min-h-[400px] items-center justify-center rounded-xl border-2 border-dashed border-neon-blue/20 bg-void-light/50"
>
<div class="text-center">
<TrendingUp
class="mx-auto mb-4 h-16 w-16 text-neon-blue/30"
style="filter: drop-shadow(0 0 15px rgba(0, 212, 255, 0.3));"
/>
<h2
class="mb-2 text-2xl font-bold text-white"
style="text-shadow: 0 0 20px rgba(0, 212, 255, 0.3);"
>
Coming Soon
</h2>
<p class="text-white/60">Player search and profiles will be available in Phase 3</p>
<div class="mt-6">
<span
class="inline-flex items-center gap-2 rounded-full border border-neon-gold/30 bg-neon-gold/10 px-4 py-1.5 text-sm text-neon-gold"
>
<span class="h-1.5 w-1.5 animate-pulse rounded-full bg-neon-gold"></span>
Phase 3 - In Development
</span>
</div>
</div>
</div>
</div>