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
All checks were successful
Build Docker Image / build (push) Successful in 1m11s
This commit is contained in:
@@ -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
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user