- Backend: extend filters with dateFrom/dateTo, won, roundsMin/Max and colors; implement color identity using jsonb and order by date with limit/offset. - Frontend: adjust slideshow spacing in index.html and include new images; bump version to 2.0.5.
273 lines
8.9 KiB
HTML
273 lines
8.9 KiB
HTML
<!doctype html>
|
|
<html lang="en" class="h-full bg-gray-50">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>EDH Stats Tracker</title>
|
|
<meta
|
|
name="description"
|
|
content="Track your Magic: The Gathering EDH/Commander games and statistics"
|
|
/>
|
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link rel="stylesheet" href="/css/styles.css" />
|
|
</head>
|
|
<body class="min-h-full flex flex-col py-12 px-4 sm:px-6 lg:px-8">
|
|
<div class="flex items-center justify-center flex-1">
|
|
<div class="w-full space-y-8 text-center" x-data="indexApp()">
|
|
<div class="max-w-md mx-auto">
|
|
<h1 class="text-4xl font-bold font-mtg text-edh-primary mb-4">
|
|
EDH Stats
|
|
</h1>
|
|
<p class="text-xl text-gray-600 mb-8">
|
|
Track your Commander games and statistics
|
|
</p>
|
|
|
|
<div class="space-y-4">
|
|
<a href="/login.html" class="btn btn-primary w-full">
|
|
🎮 Login to Track Games
|
|
</a>
|
|
<a
|
|
x-show="allowRegistration"
|
|
href="/register.html"
|
|
class="btn btn-secondary w-full"
|
|
>
|
|
📝 Create New Account
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Preview Slideshow Section -->
|
|
<div class="mt-12 max-w-5xl mx-auto">
|
|
<div
|
|
class="bg-white rounded-lg shadow-lg overflow-hidden"
|
|
x-data="slideshow()"
|
|
>
|
|
<!-- Slides Container with Arrow Navigation -->
|
|
<div class="relative bg-gray-100 pt-9 pb-9 overflow-hidden">
|
|
<!-- Image Container -->
|
|
<div
|
|
class="relative aspect-video flex items-center justify-center"
|
|
>
|
|
<template x-for="(slide, index) in slides" :key="index">
|
|
<img
|
|
:src="slide.src"
|
|
:alt="slide.alt"
|
|
x-show="currentSlide === index"
|
|
x-transition:fade.duration.500ms
|
|
class="w-full h-full object-contain absolute inset-0"
|
|
/>
|
|
</template>
|
|
|
|
<!-- Loading Spinner -->
|
|
<div
|
|
x-show="loading"
|
|
class="absolute inset-0 flex items-center justify-center bg-gray-200"
|
|
>
|
|
<div
|
|
class="animate-spin rounded-full h-12 w-12 border-b-2 border-edh-primary"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Left Arrow Button -->
|
|
<button
|
|
@click="previousSlide()"
|
|
class="absolute left-2 top-1/2 transform -translate-y-1/2 text-gray-600 hover:text-gray-800 hover:bg-gray-200 rounded-full p-2 transition-colors"
|
|
title="Previous slide"
|
|
>
|
|
<svg
|
|
class="w-6 h-6"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M15 19l-7-7 7-7"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
|
|
<!-- Right Arrow Button -->
|
|
<button
|
|
@click="nextSlide()"
|
|
class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-600 hover:text-gray-800 hover:bg-gray-200 rounded-full p-2 transition-colors"
|
|
title="Next slide"
|
|
>
|
|
<svg
|
|
class="w-6 h-6"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M9 5l7 7-7 7"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
|
|
<!-- Play/Pause Button -->
|
|
<button
|
|
@click="toggleAutoPlay()"
|
|
:class="autoPlay ? 'bg-edh-primary text-white' : 'bg-gray-300 text-gray-700'"
|
|
class="absolute top-4 right-4 px-3 py-1 rounded text-sm font-medium hover:opacity-80 transition-opacity"
|
|
:title="autoPlay ? 'Pause slideshow' : 'Play slideshow'"
|
|
>
|
|
<span x-show="autoPlay">⏸</span>
|
|
<span x-show="!autoPlay">▶</span>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Slide Indicators (Dots) -->
|
|
<div class="bg-white px-6 py-4 flex justify-center gap-2">
|
|
<template x-for="(slide, index) in slides" :key="index">
|
|
<button
|
|
@click="goToSlide(index)"
|
|
:class="currentSlide === index ? 'bg-edh-primary' : 'bg-gray-300 hover:bg-gray-400'"
|
|
class="w-2 h-2 rounded-full transition-colors"
|
|
:title="`Go to slide ${index + 1}: ${slide.title}`"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="text-gray-600 mt-4 text-sm text-center">
|
|
Explore all the features of EDH Stats Tracker - track games, manage
|
|
commanders, view statistics, and use the round timer
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script
|
|
defer
|
|
src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"
|
|
></script>
|
|
<script>
|
|
function indexApp() {
|
|
return {
|
|
allowRegistration: true,
|
|
|
|
async init() {
|
|
await this.checkRegistrationConfig()
|
|
},
|
|
|
|
async checkRegistrationConfig() {
|
|
try {
|
|
const response = await fetch('/api/auth/config')
|
|
if (response.ok) {
|
|
const data = await response.json()
|
|
this.allowRegistration = data.allowRegistration
|
|
} else {
|
|
// Default to true if endpoint fails
|
|
this.allowRegistration = true
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to check registration config:', error)
|
|
// Default to true if request fails
|
|
this.allowRegistration = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function slideshow() {
|
|
return {
|
|
currentSlide: 0,
|
|
autoPlay: true,
|
|
autoPlayInterval: null,
|
|
loading: false,
|
|
slides: [
|
|
{
|
|
src: '/images/commanders.png',
|
|
alt: 'Commander management interface for adding and editing decks with color selection',
|
|
title: 'Commanders'
|
|
},
|
|
{
|
|
src: '/images/logs.png',
|
|
alt: 'Game logging form for recording game results, player count, and outcomes',
|
|
title: 'Log Games'
|
|
},
|
|
{
|
|
src: '/images/round_timer.png',
|
|
alt: 'Real-time round counter with automatic game timing and elapsed time tracking',
|
|
title: 'Round Timer'
|
|
},
|
|
{
|
|
src: '/images/stats.png',
|
|
alt: 'Detailed statistics showing win rates by color, player count, and commander performance',
|
|
title: 'Statistics'
|
|
}
|
|
],
|
|
|
|
init() {
|
|
this.startAutoPlay()
|
|
},
|
|
|
|
nextSlide() {
|
|
this.currentSlide = (this.currentSlide + 1) % this.slides.length
|
|
this.restartAutoPlay()
|
|
},
|
|
|
|
previousSlide() {
|
|
this.currentSlide =
|
|
(this.currentSlide - 1 + this.slides.length) % this.slides.length
|
|
this.restartAutoPlay()
|
|
},
|
|
|
|
goToSlide(index) {
|
|
this.currentSlide = index
|
|
this.restartAutoPlay()
|
|
},
|
|
|
|
toggleAutoPlay() {
|
|
this.autoPlay = !this.autoPlay
|
|
if (this.autoPlay) {
|
|
this.startAutoPlay()
|
|
} else {
|
|
this.stopAutoPlay()
|
|
}
|
|
},
|
|
|
|
startAutoPlay() {
|
|
this.autoPlayInterval = setInterval(() => {
|
|
this.currentSlide = (this.currentSlide + 1) % this.slides.length
|
|
}, 5000)
|
|
},
|
|
|
|
stopAutoPlay() {
|
|
if (this.autoPlayInterval) {
|
|
clearInterval(this.autoPlayInterval)
|
|
this.autoPlayInterval = null
|
|
}
|
|
},
|
|
|
|
restartAutoPlay() {
|
|
this.stopAutoPlay()
|
|
if (this.autoPlay) {
|
|
this.startAutoPlay()
|
|
}
|
|
},
|
|
|
|
destroy() {
|
|
this.stopAutoPlay()
|
|
}
|
|
}
|
|
}
|
|
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data('indexApp', indexApp)
|
|
Alpine.data('slideshow', slideshow)
|
|
})
|
|
</script>
|
|
|
|
<script src="/js/footer-loader.js"></script>
|
|
</body>
|
|
</html>
|