mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-04-05 17:33:31 -04:00
Move UI controls to bottom and require authentication for stats
- Moved stats button, auth buttons, and debug info to bottom of main page - Added authentication requirement for /stats route - Show login prompt for unauthenticated users accessing stats - Include AuthModal for sign in/sign up from stats page 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -453,43 +453,6 @@
|
||||
<span class="big-text"
|
||||
>{isDev ? "Dev Edition | " : ""}{currentDate}</span
|
||||
>
|
||||
<div class="mt-4 flex flex-col items-center gap-3">
|
||||
<div class="flex gap-3">
|
||||
<a
|
||||
href="/stats?{user ? `userId=${user.id}` : `anonymousId=${anonymousId}`}"
|
||||
class="inline-flex items-center px-4 py-2 bg-amber-600 text-white rounded-lg hover:bg-amber-700 transition-colors text-sm font-medium shadow-md"
|
||||
>
|
||||
📊 View Stats
|
||||
</a>
|
||||
|
||||
{#if user}
|
||||
<form method="POST" action="/auth/logout" use:enhance>
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors text-sm font-medium shadow-md"
|
||||
>
|
||||
🚪 Sign Out
|
||||
</button>
|
||||
</form>
|
||||
{:else}
|
||||
<button
|
||||
onclick={() => authModalOpen = true}
|
||||
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm font-medium shadow-md"
|
||||
>
|
||||
🔐 Sign In
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if isDev}
|
||||
<div class="text-xs text-gray-600 bg-gray-100 px-3 py-2 rounded border">
|
||||
<div><strong>Debug Info:</strong></div>
|
||||
<div>User: {user ? `${user.email} (ID: ${user.id})` : 'Not signed in'}</div>
|
||||
<div>Session: {session ? `Expires ${session.expiresAt.toLocaleDateString()}` : 'No session'}</div>
|
||||
<div>Anonymous ID: {anonymousId || 'Not set'}</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-6">
|
||||
@@ -532,9 +495,44 @@
|
||||
<Credits />
|
||||
{/if}
|
||||
</div>
|
||||
{#if isDev}
|
||||
<DevButtons />
|
||||
{/if}
|
||||
<div class="mt-8 flex flex-col items-center gap-3">
|
||||
<div class="flex gap-3">
|
||||
<a
|
||||
href="/stats?{user ? `userId=${user.id}` : `anonymousId=${anonymousId}`}"
|
||||
class="inline-flex items-center px-4 py-2 bg-amber-600 text-white rounded-lg hover:bg-amber-700 transition-colors text-sm font-medium shadow-md"
|
||||
>
|
||||
📊 View Stats
|
||||
</a>
|
||||
|
||||
{#if user}
|
||||
<form method="POST" action="/auth/logout" use:enhance>
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors text-sm font-medium shadow-md"
|
||||
>
|
||||
🚪 Sign Out
|
||||
</button>
|
||||
</form>
|
||||
{:else}
|
||||
<button
|
||||
onclick={() => authModalOpen = true}
|
||||
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm font-medium shadow-md"
|
||||
>
|
||||
🔐 Sign In
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if isDev}
|
||||
<div class="text-xs text-gray-600 bg-gray-100 px-3 py-2 rounded border">
|
||||
<div><strong>Debug Info:</strong></div>
|
||||
<div>User: {user ? `${user.email} (ID: ${user.id})` : 'Not signed in'}</div>
|
||||
<div>Session: {session ? `Expires ${session.expiresAt.toLocaleDateString()}` : 'No session'}</div>
|
||||
<div>Anonymous ID: {anonymousId || 'Not set'}</div>
|
||||
</div>
|
||||
<DevButtons />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,13 +4,20 @@ import { eq, desc } from 'drizzle-orm';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load: PageServerLoad = async ({ url, locals }) => {
|
||||
const userId = url.searchParams.get('userId');
|
||||
const anonymousId = url.searchParams.get('anonymousId');
|
||||
// Check if user is authenticated
|
||||
if (!locals.user) {
|
||||
return {
|
||||
stats: null,
|
||||
error: null,
|
||||
user: null,
|
||||
session: null,
|
||||
requiresAuth: true
|
||||
};
|
||||
}
|
||||
|
||||
// Prioritize userId (authenticated user) over anonymousId
|
||||
const targetId = userId || anonymousId;
|
||||
const userId = locals.user.id;
|
||||
|
||||
if (!targetId) {
|
||||
if (!userId) {
|
||||
return {
|
||||
stats: null,
|
||||
error: 'No user ID provided',
|
||||
@@ -24,7 +31,7 @@ export const load: PageServerLoad = async ({ url, locals }) => {
|
||||
const completions = await db
|
||||
.select()
|
||||
.from(dailyCompletions)
|
||||
.where(eq(dailyCompletions.anonymousId, targetId))
|
||||
.where(eq(dailyCompletions.anonymousId, userId))
|
||||
.orderBy(desc(dailyCompletions.date));
|
||||
|
||||
if (completions.length === 0) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import { browser } from "$app/environment";
|
||||
import { goto } from "$app/navigation";
|
||||
import { onMount } from "svelte";
|
||||
import { enhance } from '$app/forms';
|
||||
import AuthModal from "$lib/components/AuthModal.svelte";
|
||||
import {
|
||||
getGradeColor,
|
||||
formatDate,
|
||||
@@ -15,9 +17,11 @@
|
||||
error?: string;
|
||||
user?: any;
|
||||
session?: any;
|
||||
requiresAuth?: boolean;
|
||||
}
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
let authModalOpen = $state(false);
|
||||
|
||||
let loading = $state(true);
|
||||
|
||||
@@ -33,28 +37,6 @@
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
const url = new URL(window.location.href);
|
||||
const hasUserId = url.searchParams.get('userId');
|
||||
|
||||
// If user is authenticated, no need to check for anonymousId
|
||||
if (data.user || hasUserId) {
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// For anonymous users, ensure anonymousId is in URL
|
||||
const anonymousId = getOrCreateAnonymousId();
|
||||
if (!anonymousId) {
|
||||
goto("/");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!url.searchParams.get('anonymousId')) {
|
||||
url.searchParams.set('anonymousId', anonymousId);
|
||||
goto(url.pathname + url.search);
|
||||
return;
|
||||
}
|
||||
|
||||
loading = false;
|
||||
});
|
||||
|
||||
@@ -91,6 +73,27 @@
|
||||
<div class="inline-block w-8 h-8 border-4 border-amber-600 border-t-transparent rounded-full animate-spin"></div>
|
||||
<p class="mt-4 text-gray-600">Loading your stats...</p>
|
||||
</div>
|
||||
{:else if data.requiresAuth}
|
||||
<div class="text-center py-12">
|
||||
<div class="bg-blue-100 border border-blue-300 rounded-lg p-8 max-w-md mx-auto">
|
||||
<h2 class="text-2xl font-bold text-blue-800 mb-4">Authentication Required</h2>
|
||||
<p class="text-blue-700 mb-6">You must be logged in to see your stats.</p>
|
||||
<div class="flex flex-col gap-3">
|
||||
<button
|
||||
onclick={() => authModalOpen = true}
|
||||
class="inline-flex items-center justify-center px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
||||
>
|
||||
🔐 Sign In / Sign Up
|
||||
</button>
|
||||
<a
|
||||
href="/"
|
||||
class="inline-flex items-center justify-center px-6 py-3 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors font-medium"
|
||||
>
|
||||
← Back to Game
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if data.error}
|
||||
<div class="text-center py-12">
|
||||
<div class="bg-red-100 border border-red-300 rounded-lg p-6 max-w-md mx-auto">
|
||||
@@ -213,4 +216,6 @@
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AuthModal bind:isOpen={authModalOpen} anonymousId={""} />
|
||||
Reference in New Issue
Block a user