feat: add daily history tab and fetch daily character history for user profile
All checks were successful
Build Docker Image / build (push) Successful in 1m11s

This commit is contained in:
2026-03-05 22:08:36 +01:00
parent b4601a5caf
commit 08f5d620be
2 changed files with 100 additions and 5 deletions

View File

@@ -2,8 +2,8 @@ import { fail, redirect } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
import { auth } from '$lib/server/auth'; import { auth } from '$lib/server/auth';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { session } from '$lib/server/db/auth.schema'; import { session, userCharacterHistory, characterHistory, character } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm'; import { eq, desc } from 'drizzle-orm';
import { APIError } from 'better-auth/api'; import { APIError } from 'better-auth/api';
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
@@ -17,9 +17,27 @@ export const load: PageServerLoad = async (event) => {
.from(session) .from(session)
.where(eq(session.userId, event.locals.user.id)); .where(eq(session.userId, event.locals.user.id));
// Fetch daily history for this user
const dailyHistory = await db
.select({
id: userCharacterHistory.id,
characterId: characterHistory.characterId,
date: characterHistory.date,
tryCount: userCharacterHistory.tryCount,
won: characterHistory.won,
characterName: character.name,
characterImage: character.pictureUrl
})
.from(userCharacterHistory)
.innerJoin(characterHistory, eq(userCharacterHistory.characterHistoryId, characterHistory.id))
.innerJoin(character, eq(characterHistory.characterId, character.id))
.where(eq(userCharacterHistory.userId, event.locals.user.id))
.orderBy(desc(characterHistory.date));
return { return {
user: event.locals.user, user: event.locals.user,
sessions: userSessions sessions: userSessions,
dailyHistory: dailyHistory
}; };
}; };

View File

@@ -10,13 +10,14 @@
let { data, form }: Props = $props(); let { data, form }: Props = $props();
let isLoading = $state(false); let isLoading = $state(false);
let activeTab = $state<'profile' | 'password' | 'sessions'>('profile'); let activeTab = $state<'profile' | 'password' | 'sessions' | 'daily'>('profile');
let name = $state(''); let name = $state('');
let showSuccess = $state(false); let showSuccess = $state(false);
let oldPassword = $state(''); let oldPassword = $state('');
let newPassword = $state(''); let newPassword = $state('');
let confirmPassword = $state(''); let confirmPassword = $state('');
let sessions = $state<any[]>([]); let sessions = $state<any[]>([]);
let dailyHistory = $state<any[]>([]);
let tabsElement: HTMLDivElement | undefined; let tabsElement: HTMLDivElement | undefined;
$effect(() => { $effect(() => {
@@ -27,6 +28,10 @@
sessions = (data as any).sessions || []; sessions = (data as any).sessions || [];
}); });
$effect(() => {
dailyHistory = (data as any).dailyHistory || [];
});
$effect(() => { $effect(() => {
if (form && form.success === true) { if (form && form.success === true) {
showSuccess = true; showSuccess = true;
@@ -36,7 +41,7 @@
} }
}); });
const handleTabChange = (tab: 'profile' | 'password' | 'sessions') => { const handleTabChange = (tab: 'profile' | 'password' | 'sessions' | 'daily') => {
activeTab = tab; activeTab = tab;
}; };
@@ -83,6 +88,14 @@
> >
Mot de passe Mot de passe
</button> </button>
<button
onclick={() => handleTabChange('daily')}
class="px-4 py-3 font-semibold uppercase tracking-[0.1em] transition {activeTab === 'daily'
? 'border-b-2 border-amber-300 text-amber-100'
: 'text-slate-400 hover:text-slate-100'}"
>
Historique Daily
</button>
<button <button
onclick={() => handleTabChange('sessions')} onclick={() => handleTabChange('sessions')}
class="px-4 py-3 font-semibold uppercase tracking-[0.1em] transition {activeTab === 'sessions' class="px-4 py-3 font-semibold uppercase tracking-[0.1em] transition {activeTab === 'sessions'
@@ -268,6 +281,70 @@
</div> </div>
{/if} {/if}
<!-- Daily History Tab -->
{#if activeTab === 'daily'}
<div class="rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur sm:p-8">
<h2 class="mb-6 text-2xl font-bold uppercase tracking-[0.2em] text-amber-50">
Historique des Daily
</h2>
{#if dailyHistory.length === 0}
<p class="text-center text-slate-400">Aucun historique disponible</p>
{:else}
<div class="space-y-4">
{#each dailyHistory as day}
<div class="flex items-center gap-4 rounded-lg border border-white/10 bg-white/5 p-4">
<!-- Character Image -->
<div class="flex-shrink-0">
{#if day.characterImage}
<img
src={day.characterImage}
alt={day.characterName}
class="h-16 w-16 rounded-lg border border-white/20 object-cover"
/>
{:else}
<div class="flex h-16 w-16 items-center justify-center rounded-lg border border-white/20 bg-slate-700">
<span class="text-xs text-slate-400">N/A</span>
</div>
{/if}
</div>
<!-- Character Info -->
<div class="flex-1">
<p class="font-semibold text-white">{day.characterName}</p>
<p class="text-xs text-slate-400">
{new Date(day.date).toLocaleDateString('fr-FR', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</p>
</div>
<!-- Status and Tries -->
<div class="flex flex-col items-end gap-2">
<div class="flex items-center gap-2">
{#if day.won === 1}
<span class="rounded-full bg-green-900/30 px-3 py-1 text-xs font-semibold text-green-300">
✓ Gagné
</span>
{:else}
<span class="rounded-full bg-red-900/30 px-3 py-1 text-xs font-semibold text-red-300">
✗ Perdu
</span>
{/if}
</div>
<p class="text-xs text-slate-400">
{day.tryCount} {day.tryCount === 1 ? 'tentative' : 'tentatives'}
</p>
</div>
</div>
{/each}
</div>
{/if}
</div>
{/if}
<!-- Sessions Tab --> <!-- Sessions Tab -->
{#if activeTab === 'sessions'} {#if activeTab === 'sessions'}
<div class="rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur sm:p-8"> <div class="rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur sm:p-8">