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"
|
<span class="big-text"
|
||||||
>{isDev ? "Dev Edition | " : ""}{currentDate}</span
|
>{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>
|
||||||
|
|
||||||
<div class="flex flex-col gap-6">
|
<div class="flex flex-col gap-6">
|
||||||
@@ -532,9 +495,44 @@
|
|||||||
<Credits />
|
<Credits />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if isDev}
|
<div class="mt-8 flex flex-col items-center gap-3">
|
||||||
<DevButtons />
|
<div class="flex gap-3">
|
||||||
{/if}
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,20 @@ import { eq, desc } from 'drizzle-orm';
|
|||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ url, locals }) => {
|
export const load: PageServerLoad = async ({ url, locals }) => {
|
||||||
const userId = url.searchParams.get('userId');
|
// Check if user is authenticated
|
||||||
const anonymousId = url.searchParams.get('anonymousId');
|
if (!locals.user) {
|
||||||
|
return {
|
||||||
|
stats: null,
|
||||||
|
error: null,
|
||||||
|
user: null,
|
||||||
|
session: null,
|
||||||
|
requiresAuth: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Prioritize userId (authenticated user) over anonymousId
|
const userId = locals.user.id;
|
||||||
const targetId = userId || anonymousId;
|
|
||||||
|
|
||||||
if (!targetId) {
|
if (!userId) {
|
||||||
return {
|
return {
|
||||||
stats: null,
|
stats: null,
|
||||||
error: 'No user ID provided',
|
error: 'No user ID provided',
|
||||||
@@ -24,7 +31,7 @@ export const load: PageServerLoad = async ({ url, locals }) => {
|
|||||||
const completions = await db
|
const completions = await db
|
||||||
.select()
|
.select()
|
||||||
.from(dailyCompletions)
|
.from(dailyCompletions)
|
||||||
.where(eq(dailyCompletions.anonymousId, targetId))
|
.where(eq(dailyCompletions.anonymousId, userId))
|
||||||
.orderBy(desc(dailyCompletions.date));
|
.orderBy(desc(dailyCompletions.date));
|
||||||
|
|
||||||
if (completions.length === 0) {
|
if (completions.length === 0) {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
import { enhance } from '$app/forms';
|
||||||
|
import AuthModal from "$lib/components/AuthModal.svelte";
|
||||||
import {
|
import {
|
||||||
getGradeColor,
|
getGradeColor,
|
||||||
formatDate,
|
formatDate,
|
||||||
@@ -15,9 +17,11 @@
|
|||||||
error?: string;
|
error?: string;
|
||||||
user?: any;
|
user?: any;
|
||||||
session?: any;
|
session?: any;
|
||||||
|
requiresAuth?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { data }: { data: PageData } = $props();
|
let { data }: { data: PageData } = $props();
|
||||||
|
let authModalOpen = $state(false);
|
||||||
|
|
||||||
let loading = $state(true);
|
let loading = $state(true);
|
||||||
|
|
||||||
@@ -33,28 +37,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
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;
|
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>
|
<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>
|
<p class="mt-4 text-gray-600">Loading your stats...</p>
|
||||||
</div>
|
</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}
|
{:else if data.error}
|
||||||
<div class="text-center py-12">
|
<div class="text-center py-12">
|
||||||
<div class="bg-red-100 border border-red-300 rounded-lg p-6 max-w-md mx-auto">
|
<div class="bg-red-100 border border-red-300 rounded-lg p-6 max-w-md mx-auto">
|
||||||
@@ -213,4 +216,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AuthModal bind:isOpen={authModalOpen} anonymousId={""} />
|
||||||
Reference in New Issue
Block a user