Implement user authentication and management system with FastAPI
- Added authentication routes for login, registration, password reset, and account deletion in `auth.py`. - Implemented user profile management, including updating user information and changing passwords. - Created dashboard views to display user-specific information and recent activity in `dashboard.py`. - Developed leaderboard views to show team rankings and points in `leaderboard.py`. - Added QR code generation and redemption functionality in `qr.py` and `redeem.py`. - Implemented team management features, allowing users to create and join teams in `teams.py`. - Introduced session debugging utility to assist with session-related issues in `debug_session.py`. - Configured Docker Compose for MySQL database and FastAPI application with environment variables. - Updated requirements.txt to include necessary dependencies for the application.
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4">
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-4">About LeagueLedger</h1>
|
||||
<p class="mb-4">
|
||||
LeagueLedger is your ultimate companion for tracking pub quiz team achievements. We aim to provide a fun and engaging platform for quiz enthusiasts to connect, compete, and celebrate their knowledge.
|
||||
</p>
|
||||
<p class="mb-4">
|
||||
As a pub quiz master, you can generate printout QR codes for your top-ranking teams, distribute them, and let teams redeem them for points on our website.
|
||||
</p>
|
||||
<p class="mb-4">
|
||||
As a pub quiz team member, you can redeem QR codes, create a team name, invite other members, and use social logins for easy access.
|
||||
</p>
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Our Mission</h2>
|
||||
<p class="mb-4">
|
||||
To enhance the pub quiz experience by providing a seamless and intuitive platform for tracking team progress, fostering friendly competition, and celebrating the spirit of trivia.
|
||||
</p>
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Our Team</h2>
|
||||
<p class="mb-4">
|
||||
LeagueLedger is an Open-Source initiative and part of the KaufDeinQuiz platform. It is brought to you by Christian Louis IT Beratung und Medienproduktion, a team of dedicated quiz enthusiasts and software developers passionate about creating innovative solutions for the pub quiz community.
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,99 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<!-- Header -->
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-6">
|
||||
{% if is_new %}Create{% else %}Edit{% endif %} {{ display_name[:-1] if display_name.endswith('s') else display_name }}
|
||||
</h1>
|
||||
|
||||
<!-- Form -->
|
||||
<form method="post" action="/admin/{{ model_name }}{% if is_new %}/new{% else %}/{{ record.id }}{% endif %}" class="space-y-6">
|
||||
{% for column_name, column_info in columns_info.items() %}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 items-center">
|
||||
<label for="{{ column_name }}" class="block text-sm font-medium text-gray-700">
|
||||
{{ column_name|replace('_', ' ')|title }}
|
||||
{% if column_info.primary_key %}
|
||||
<span class="ml-1 text-xs text-irish-green">(Primary Key)</span>
|
||||
{% endif %}
|
||||
{% if column_info.foreign_key %}
|
||||
<span class="ml-1 text-xs text-blue-600">(Foreign Key)</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
<div class="md:col-span-2">
|
||||
{% if column_info.primary_key and is_new %}
|
||||
<!-- For new records, primary key is often auto-generated -->
|
||||
<input type="text" id="{{ column_name }}" name="{{ column_name }}" placeholder="Auto-generated"
|
||||
class="bg-gray-100 border border-gray-300 text-gray-500 rounded-md px-3 py-2 w-full"
|
||||
{% if not column_info.nullable %}disabled{% endif %}>
|
||||
|
||||
{% elif column_info.foreign_key and column_name in foreign_key_options %}
|
||||
<!-- Foreign key dropdown -->
|
||||
<select id="{{ column_name }}" name="{{ column_name }}"
|
||||
class="border border-gray-300 rounded-md px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-irish-green"
|
||||
{% if not column_info.nullable %}required{% endif %}>
|
||||
|
||||
{% if column_info.nullable %}
|
||||
<option value="">-- None --</option>
|
||||
{% endif %}
|
||||
|
||||
{% for value, label in foreign_key_options[column_name] %}
|
||||
<option value="{{ value }}" {% if record and record[column_name] == value %}selected{% endif %}>
|
||||
{{ label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
{% elif column_info.type.startswith('BOOLEAN') %}
|
||||
<!-- Boolean field -->
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox" id="{{ column_name }}" name="{{ column_name }}" class="h-5 w-5"
|
||||
value="True" {% if record and record[column_name] %}checked{% endif %}>
|
||||
<span class="ml-2 text-sm text-gray-600">Yes</span>
|
||||
</div>
|
||||
|
||||
{% elif 'text' in column_info.type.lower() %}
|
||||
<!-- Text area for longer text -->
|
||||
<textarea id="{{ column_name }}" name="{{ column_name }}" rows="4"
|
||||
class="border border-gray-300 rounded-md px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-irish-green"
|
||||
{% if not column_info.nullable %}required{% endif %}>{{ record[column_name] if record else '' }}</textarea>
|
||||
|
||||
{% else %}
|
||||
<!-- Standard input field -->
|
||||
<input type="{{ 'number' if 'int' in column_info.type.lower() else 'text' }}"
|
||||
id="{{ column_name }}" name="{{ column_name }}"
|
||||
value="{{ record[column_name] if record and record[column_name] is not none else '' }}"
|
||||
class="border border-gray-300 rounded-md px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-irish-green"
|
||||
{% if not column_info.nullable and not column_info.primary_key %}required{% endif %}>
|
||||
{% endif %}
|
||||
|
||||
{% if column_info.nullable %}
|
||||
<p class="text-xs text-gray-500 mt-1">Optional field</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Form buttons -->
|
||||
<div class="flex justify-end space-x-4 pt-6 border-t">
|
||||
<a href="/admin/{{ model_name }}" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50 transition">
|
||||
Cancel
|
||||
</a>
|
||||
<button type="submit" class="px-4 py-2 bg-irish-green text-white rounded-md hover:bg-opacity-90 transition">
|
||||
{% if is_new %}Create{% else %}Update{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Back links -->
|
||||
<div class="mt-6 text-center">
|
||||
<a href="/admin/{{ model_name }}" class="text-irish-green hover:underline mr-4">
|
||||
<i class="fas fa-list mr-1"></i> Back to {{ display_name }}
|
||||
</a>
|
||||
<a href="/admin/" class="text-irish-green hover:underline">
|
||||
<i class="fas fa-tachometer-alt mr-1"></i> Back to Admin Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,36 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-5xl mx-auto">
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-6">Admin Dashboard</h1>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{% for model_key, display_name in models %}
|
||||
<div class="bg-cream-white rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow">
|
||||
<h3 class="text-xl font-bold text-irish-green mb-3">{{ display_name }}</h3>
|
||||
<div class="flex items-center justify-between mt-4">
|
||||
<a href="/admin/{{ model_key }}" class="bg-irish-green text-white px-4 py-2 rounded-md hover:bg-opacity-90 transition">
|
||||
Manage
|
||||
</a>
|
||||
<a href="/admin/{{ model_key }}/new" class="text-irish-green hover:underline">
|
||||
<i class="fas fa-plus"></i> Add New
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="mt-10 pt-6 border-t border-gray-200">
|
||||
<h2 class="text-xl font-bold text-irish-green mb-4">Quick Actions</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<a href="/qr/generate/10" class="bg-white border border-irish-green text-irish-green hover:bg-irish-green hover:text-white px-4 py-3 rounded-md transition flex items-center">
|
||||
<i class="fas fa-qrcode mr-2"></i> Generate QR Code (10 points)
|
||||
</a>
|
||||
<a href="/teams/" class="bg-white border border-irish-green text-irish-green hover:bg-irish-green hover:text-white px-4 py-3 rounded-md transition flex items-center">
|
||||
<i class="fas fa-users mr-2"></i> View Teams
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,122 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-irish-green">{{ display_name }}</h1>
|
||||
<p class="text-gray-600">Manage your {{ display_name.lower() }} data</p>
|
||||
</div>
|
||||
<div class="mt-4 md:mt-0">
|
||||
<a href="/admin/{{ model_name }}/new" class="bg-irish-green hover:bg-opacity-90 text-white font-medium py-2 px-4 rounded-md transition">
|
||||
<i class="fas fa-plus mr-1"></i> Create New
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Records Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full border-collapse">
|
||||
<thead>
|
||||
<tr class="bg-irish-green text-white">
|
||||
<th class="p-3 text-left">ID</th>
|
||||
{% for column in columns %}
|
||||
{% if column != 'id' %}
|
||||
<th class="p-3 text-left">{{ column|replace('_', ' ')|title }}</th>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<th class="p-3 text-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{% for record in records %}
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="p-3 font-medium">{{ record.id }}</td>
|
||||
{% for column in columns %}
|
||||
{% if column != 'id' %}
|
||||
<td class="p-3">
|
||||
{% if columns_info[column].foreign_key %}
|
||||
<span class="bg-gray-100 text-gray-800 px-2 py-1 rounded text-xs">FK: {{ record[column] }}</span>
|
||||
{% elif record[column] is none %}
|
||||
<span class="text-gray-400 italic">NULL</span>
|
||||
{% elif columns_info[column].type.startswith('BOOLEAN') %}
|
||||
{% if record[column] %}
|
||||
<span class="bg-green-100 text-green-800 px-2 py-1 rounded-full text-xs">Yes</span>
|
||||
{% else %}
|
||||
<span class="bg-red-100 text-red-800 px-2 py-1 rounded-full text-xs">No</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ record[column]|string|truncate(50) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<td class="p-3 text-center">
|
||||
<div class="flex justify-center space-x-2">
|
||||
<a href="/admin/{{ model_name }}/{{ record.id }}" class="text-irish-green hover:text-opacity-70" title="Edit">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a href="/admin/{{ model_name }}/{{ record.id }}/delete" class="text-guinness-red hover:text-opacity-70" title="Delete"
|
||||
onclick="return confirm('Are you sure you want to delete this record?');">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{% if not records %}
|
||||
<tr>
|
||||
<td colspan="{{ columns|length + 1 }}" class="p-4 text-center text-gray-500">
|
||||
No records found
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
{% if total_pages > 1 %}
|
||||
<div class="mt-6 flex justify-between items-center">
|
||||
<div class="text-gray-600 text-sm">
|
||||
Showing {{ (page - 1) * per_page + 1 }}-{{ [page * per_page, total_records]|min }} of {{ total_records }} records
|
||||
</div>
|
||||
<div class="flex space-x-1">
|
||||
{% if page > 1 %}
|
||||
<a href="?page={{ page - 1 }}&per_page={{ per_page }}" class="px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded">
|
||||
« Prev
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% for p in range(1, total_pages + 1) %}
|
||||
{% if p == page %}
|
||||
<span class="px-3 py-1 bg-irish-green text-white rounded">{{ p }}</span>
|
||||
{% elif p <= 3 or p >= total_pages - 2 or (p >= page - 1 and p <= page + 1) %}
|
||||
<a href="?page={{ p }}&per_page={{ per_page }}" class="px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded">
|
||||
{{ p }}
|
||||
</a>
|
||||
{% elif p == 4 and page > 5 or p == total_pages - 3 and page < total_pages - 4 %}
|
||||
<span class="px-3 py-1">...</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page < total_pages %}
|
||||
<a href="?page={{ page + 1 }}&per_page={{ per_page }}" class="px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded">
|
||||
Next »
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Back to Admin -->
|
||||
<div class="mt-6 text-center">
|
||||
<a href="/admin/" class="text-irish-green hover:underline">
|
||||
<i class="fas fa-arrow-left mr-1"></i> Back to Admin Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,93 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-md mx-auto my-8">
|
||||
<div class="bg-white p-8 rounded-lg shadow-md">
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-6 text-center">Log In</h1>
|
||||
|
||||
<!-- Messages/Alerts -->
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="mb-4 p-3 rounded {% if message.type == 'error' %}bg-red-100 text-red-700{% else %}bg-green-100 text-green-700{% endif %}">
|
||||
{{ message.text }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<form action="/auth/login" method="post" class="space-y-4">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
|
||||
<div>
|
||||
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username or Email</label>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
value="{{ username or '' }}"
|
||||
required
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-irish-green focus:border-irish-green"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
required
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-irish-green focus:border-irish-green"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Debug info -->
|
||||
<div class="text-xs text-gray-500">
|
||||
<p>Having trouble logging in? Make sure your browser accepts cookies.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="remember"
|
||||
name="remember"
|
||||
class="h-4 w-4 text-irish-green focus:ring-irish-green border-gray-300 rounded"
|
||||
>
|
||||
<label for="remember" class="ml-2 block text-sm text-gray-700">Remember me</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full bg-irish-green text-white py-2 px-4 rounded-md hover:bg-opacity-90 transition focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-irish-green"
|
||||
>
|
||||
Log In
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-sm mt-4">
|
||||
<a href="/auth/forgot-password" class="text-irish-green hover:text-opacity-80">Forgot password?</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-6 pt-6 border-t border-gray-200 text-center">
|
||||
<p class="text-gray-600">Don't have an account?</p>
|
||||
<a href="/auth/register" class="block mt-2 bg-cream-white text-irish-green border border-irish-green py-2 px-4 rounded-md hover:bg-irish-green hover:text-white transition">
|
||||
Create an account
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- OAuth login options (to be implemented later) -->
|
||||
<div class="mt-6 pt-6 border-t border-gray-200">
|
||||
<p class="text-center text-gray-600 mb-4">Or sign in with</p>
|
||||
<div class="flex justify-center space-x-4">
|
||||
<button class="bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-opacity-90 transition w-full disabled:opacity-50" disabled>
|
||||
<i class="fab fa-google mr-2"></i> Google
|
||||
</button>
|
||||
<button class="bg-gray-800 text-white py-2 px-4 rounded-md hover:bg-opacity-90 transition w-full disabled:opacity-50" disabled>
|
||||
<i class="fab fa-github mr-2"></i> GitHub
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-center text-gray-500 text-xs mt-2">OAuth login coming soon</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,188 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
|
||||
<!-- Profile Header -->
|
||||
<div class="bg-irish-green text-white p-6">
|
||||
<div class="flex flex-col sm:flex-row items-center">
|
||||
<div class="w-24 h-24 rounded-full bg-white border-4 border-white overflow-hidden mb-4 sm:mb-0 sm:mr-6">
|
||||
<img src="https://via.placeholder.com/150" alt="Profile" class="w-full h-full object-cover">
|
||||
</div>
|
||||
<div class="text-center sm:text-left">
|
||||
<h1 class="text-2xl font-bold">{{ user.username }}</h1>
|
||||
<p class="text-green-100">Member since {{ user.created_at.strftime('%B %Y') }}</p>
|
||||
<p class="mt-2">
|
||||
{% if user.is_verified %}
|
||||
<span class="bg-golden-ale text-black-stout text-xs px-2 py-1 rounded-full font-medium mr-1">Verified</span>
|
||||
{% endif %}
|
||||
<span class="bg-white text-irish-green text-xs px-2 py-1 rounded-full font-medium">{{ user.memberships|length }} Teams</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Messages/Alerts -->
|
||||
{% if messages %}
|
||||
<div class="p-4">
|
||||
{% for message in messages %}
|
||||
<div class="mb-4 p-3 rounded {% if message.type == 'error' %}bg-red-100 text-red-700{% else %}bg-green-100 text-green-700{% endif %}">
|
||||
{{ message.text }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Profile Content -->
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<!-- Left Column: Personal Info -->
|
||||
<div class="md:col-span-1">
|
||||
<h2 class="text-xl font-semibold text-irish-green mb-4">Personal Information</h2>
|
||||
|
||||
<form action="/auth/update-profile" method="post" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-600">Display Name</label>
|
||||
<div class="mt-1">
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
value="{{ user.username }}"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-irish-green"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-600">Email</label>
|
||||
<div class="mt-1">
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value="{{ user.email }}"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-irish-green"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-4">
|
||||
<button type="submit" class="w-full bg-irish-green hover:bg-opacity-90 text-white font-medium py-2 px-4 rounded-md transition">
|
||||
Update Profile
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-6 pt-6 border-t">
|
||||
<h3 class="text-lg font-medium text-irish-green mb-3">Change Password</h3>
|
||||
<form action="/auth/change-password" method="post" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-600">Current Password</label>
|
||||
<div class="mt-1">
|
||||
<input
|
||||
type="password"
|
||||
name="current_password"
|
||||
required
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-irish-green"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-600">New Password</label>
|
||||
<div class="mt-1">
|
||||
<input
|
||||
type="password"
|
||||
name="new_password"
|
||||
required
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-irish-green"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-600">Confirm New Password</label>
|
||||
<div class="mt-1">
|
||||
<input
|
||||
type="password"
|
||||
name="confirm_password"
|
||||
required
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-irish-green"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full bg-golden-ale hover:bg-opacity-90 text-black-stout font-medium py-2 px-4 rounded-md transition">
|
||||
Change Password
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Stats & Teams -->
|
||||
<div class="md:col-span-2">
|
||||
<h2 class="text-xl font-semibold text-irish-green mb-4">Your Statistics</h2>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div class="bg-cream-white p-4 rounded-lg text-center">
|
||||
<p class="text-sm text-gray-600">Total Points</p>
|
||||
<p class="text-2xl font-bold text-irish-green">378</p>
|
||||
</div>
|
||||
<div class="bg-cream-white p-4 rounded-lg text-center">
|
||||
<p class="text-sm text-gray-600">QR Codes Redeemed</p>
|
||||
<p class="text-2xl font-bold text-irish-green">24</p>
|
||||
</div>
|
||||
<div class="bg-cream-white p-4 rounded-lg text-center">
|
||||
<p class="text-sm text-gray-600">Teams Joined</p>
|
||||
<p class="text-2xl font-bold text-irish-green">{{ user.memberships|length }}</p>
|
||||
</div>
|
||||
<div class="bg-cream-white p-4 rounded-lg text-center">
|
||||
<p class="text-sm text-gray-600">Best Position</p>
|
||||
<p class="text-2xl font-bold text-irish-green">#2</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="text-xl font-semibold text-irish-green mb-4">Your Teams</h2>
|
||||
<div class="space-y-4">
|
||||
{% for team_info in user_teams %}
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<div class="bg-cream-white px-4 py-3 flex justify-between items-center">
|
||||
<div class="font-medium">{{ team_info.team.name }}</div>
|
||||
{% if team_info.is_admin %}
|
||||
<span class="bg-golden-ale text-black-stout text-xs px-2 py-1 rounded-full">Captain</span>
|
||||
{% else %}
|
||||
<span class="bg-gray-200 text-gray-700 text-xs px-2 py-1 rounded-full">Member</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="px-4 py-3 flex justify-between items-center">
|
||||
<div>
|
||||
<p class="text-sm">Current Points: <span class="font-bold">187</span></p>
|
||||
<p class="text-sm text-gray-600">Current Rank: #4</p>
|
||||
</div>
|
||||
<a href="/teams/{{ team_info.team.id }}" class="text-irish-green hover:underline text-sm">View Team</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center p-4 bg-gray-50 rounded-lg">
|
||||
<p class="text-gray-600">You haven't joined any teams yet.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<a href="/teams/" class="block text-center text-irish-green hover:underline text-sm">
|
||||
<i class="fas fa-plus mr-1"></i> Join or Create Another Team
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Account Management -->
|
||||
<div class="mt-8 pt-6 border-t">
|
||||
<h3 class="text-xl text-red-600 font-semibold mb-4">Danger Zone</h3>
|
||||
<p class="text-gray-700 mb-4">These actions cannot be undone. Please be certain.</p>
|
||||
|
||||
<a href="/auth/delete-account" class="inline-block bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-md">
|
||||
Delete Account
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,94 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-md mx-auto my-8">
|
||||
<div class="bg-white p-8 rounded-lg shadow-md">
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-6 text-center">Create an Account</h1>
|
||||
|
||||
<!-- Messages/Alerts -->
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="mb-4 p-3 rounded {% if message.type == 'error' %}bg-red-100 text-red-700{% else %}bg-green-100 text-green-700{% endif %}">
|
||||
{{ message.text }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<form action="/auth/register" method="post" class="space-y-4">
|
||||
<div>
|
||||
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
value="{{ username or '' }}"
|
||||
required
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-irish-green focus:border-irish-green"
|
||||
>
|
||||
<p class="text-xs text-gray-500 mt-1">Choose a unique username (3-30 characters, letters, numbers and underscores only)</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value="{{ email or '' }}"
|
||||
required
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-irish-green focus:border-irish-green"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
required
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-irish-green focus:border-irish-green"
|
||||
>
|
||||
<p class="text-xs text-gray-500 mt-1">At least 8 characters</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="confirm_password" class="block text-sm font-medium text-gray-700 mb-1">Confirm Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="confirm_password"
|
||||
name="confirm_password"
|
||||
required
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-irish-green focus:border-irish-green"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="terms"
|
||||
name="terms"
|
||||
required
|
||||
class="h-4 w-4 mt-1 text-irish-green focus:ring-irish-green border-gray-300 rounded"
|
||||
>
|
||||
<label for="terms" class="ml-2 block text-sm text-gray-700">
|
||||
I agree to the <a href="/terms" class="text-irish-green hover:underline" target="_blank">Terms of Service</a> and <a href="/privacy" class="text-irish-green hover:underline" target="_blank">Privacy Policy</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full bg-irish-green text-white py-2 px-4 rounded-md hover:bg-opacity-90 transition focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-irish-green"
|
||||
>
|
||||
Create Account
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-6 pt-6 border-t border-gray-200 text-center">
|
||||
<p class="text-gray-600">Already have an account?</p>
|
||||
<a href="/auth/login" class="text-irish-green hover:underline">Log in</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-md mx-auto my-12">
|
||||
<div class="bg-white p-8 rounded-lg shadow-md text-center">
|
||||
<div class="mb-6">
|
||||
<div class="inline-block p-4 rounded-full bg-green-100">
|
||||
<i class="fas fa-check-circle text-irish-green text-5xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-4">Registration Successful!</h1>
|
||||
<p class="text-gray-600 mb-6">Your account has been created and you're now logged in.</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<a href="/dashboard" class="block w-full bg-irish-green hover:bg-opacity-90 text-white font-medium py-2 px-4 rounded-md transition">
|
||||
Go to Dashboard
|
||||
</a>
|
||||
<a href="/teams/" class="block w-full bg-cream-white border border-irish-green text-irish-green hover:bg-irish-green hover:text-white font-medium py-2 px-4 rounded-md transition">
|
||||
Join a Team
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 pt-6 border-t border-gray-200">
|
||||
<h2 class="font-semibold mb-2">What's Next?</h2>
|
||||
<ul class="text-left text-sm space-y-2">
|
||||
<li><i class="fas fa-check-circle text-irish-green mr-2"></i> Explore the pub quiz leaderboards</li>
|
||||
<li><i class="fas fa-check-circle text-irish-green mr-2"></i> Join an existing team or create your own</li>
|
||||
<li><i class="fas fa-check-circle text-irish-green mr-2"></i> Scan QR codes at quiz events to earn points</li>
|
||||
<li><i class="fas fa-check-circle text-irish-green mr-2"></i> Complete your profile information</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,182 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-full">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>LeagueLedger</title>
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=EB+Garamond:wght@400;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<!-- Font Awesome Icons -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'irish-green': '#006837',
|
||||
'golden-ale': '#FFB400',
|
||||
'cream-white': '#F5F0E1',
|
||||
'black-stout': '#1A1A1A',
|
||||
'guinness-red': '#B22222',
|
||||
},
|
||||
fontFamily: {
|
||||
'garamond': ['"EB Garamond"', 'serif'],
|
||||
'inter': ['"Inter"', 'sans-serif'],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'EB Garamond', serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="flex flex-col min-h-screen bg-cream-white text-black-stout">
|
||||
<header class="bg-irish-green text-cream-white shadow-md">
|
||||
<div class="container mx-auto px-4">
|
||||
<!-- Desktop Navigation -->
|
||||
<div class="flex justify-between items-center py-4">
|
||||
<div class="flex items-center space-x-3">
|
||||
<img src="https://via.placeholder.com/40x40" alt="LeagueLedger Logo" class="h-10 w-10">
|
||||
<h1 class="text-2xl font-bold font-garamond">LeagueLedger</h1>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Navigation Links -->
|
||||
<nav class="hidden md:flex items-center space-x-6">
|
||||
<a href="/" class="hover:text-golden-ale transition-colors duration-200">
|
||||
<i class="fas fa-home"></i> Home
|
||||
</a>
|
||||
<a href="/teams/" class="hover:text-golden-ale transition-colors duration-200">
|
||||
<i class="fas fa-users"></i> Teams
|
||||
</a>
|
||||
<a href="/leaderboard" class="hover:text-golden-ale transition-colors duration-200">
|
||||
<i class="fas fa-trophy"></i> Leaderboard
|
||||
</a>
|
||||
<a href="/dashboard" class="hover:text-golden-ale transition-colors duration-200">
|
||||
<i class="fas fa-tachometer-alt"></i> Dashboard
|
||||
</a>
|
||||
<a href="/about" class="hover:text-golden-ale transition-colors duration-200">
|
||||
About
|
||||
</a>
|
||||
<a href="/contact" class="hover:text-golden-ale transition-colors duration-200">
|
||||
Contact
|
||||
</a>
|
||||
|
||||
<!-- User Authentication -->
|
||||
{% if current_user %}
|
||||
<div class="relative group">
|
||||
<button class="flex items-center focus:outline-none">
|
||||
<span class="mr-1">{{ current_user.username }}</span>
|
||||
<i class="fas fa-chevron-down text-xs"></i>
|
||||
</button>
|
||||
<div class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg overflow-hidden z-20 hidden group-hover:block">
|
||||
<a href="/auth/profile" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a>
|
||||
<a href="/dashboard/" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Dashboard</a>
|
||||
<div class="border-t border-gray-100"></div>
|
||||
<a href="/auth/logout" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="/auth/login" class="bg-white text-irish-green px-4 py-1 rounded-md hover:bg-cream-white transition-colors duration-200">
|
||||
Log In
|
||||
</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<button id="mobile-menu-button" class="md:hidden text-cream-white focus:outline-none">
|
||||
<i class="fas fa-bars text-2xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Navigation Menu -->
|
||||
<div id="mobile-menu" class="md:hidden hidden pb-4">
|
||||
<nav class="flex flex-col space-y-3">
|
||||
<a href="/" class="hover:bg-green-800 py-2 px-3 rounded-md transition-colors duration-200">
|
||||
<i class="fas fa-home"></i> Home
|
||||
</a>
|
||||
<a href="/teams/" class="hover:bg-green-800 py-2 px-3 rounded-md transition-colors duration-200">
|
||||
<i class="fas fa-users"></i> Teams
|
||||
</a>
|
||||
<a href="/leaderboard" class="hover:bg-green-800 py-2 px-3 rounded-md transition-colors duration-200">
|
||||
<i class="fas fa-trophy"></i> Leaderboard
|
||||
</a>
|
||||
<a href="/dashboard" class="hover:bg-green-800 py-2 px-3 rounded-md transition-colors duration-200">
|
||||
<i class="fas fa-tachometer-alt"></i> Dashboard
|
||||
</a>
|
||||
<a href="/about" class="hover:bg-green-800 py-2 px-3 rounded-md transition-colors duration-200">
|
||||
About
|
||||
</a>
|
||||
<a href="/contact" class="hover:bg-green-800 py-2 px-3 rounded-md transition-colors duration-200">
|
||||
Contact
|
||||
</a>
|
||||
|
||||
<!-- Mobile Auth Links -->
|
||||
{% if current_user %}
|
||||
<a href="/auth/profile" class="hover:bg-green-800 py-2 px-3 rounded-md transition-colors duration-200">
|
||||
<i class="fas fa-user"></i> {{ current_user.username }}
|
||||
</a>
|
||||
<a href="/auth/logout" class="hover:bg-green-800 py-2 px-3 rounded-md transition-colors duration-200">
|
||||
<i class="fas fa-sign-out-alt"></i> Logout
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="/auth/login" class="bg-white text-irish-green py-2 px-3 rounded-md">
|
||||
Log In
|
||||
</a>
|
||||
<a href="/auth/register" class="border border-white text-white py-2 px-3 rounded-md">
|
||||
Sign Up
|
||||
</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-grow container mx-auto px-4 py-6">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-black-stout text-cream-white mt-auto">
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<div class="mb-4 md:mb-0">
|
||||
<h3 class="text-xl font-garamond">LeagueLedger</h3>
|
||||
<p class="text-sm text-cream-white opacity-70">Track Your Triumphs. Celebrate the Quiz.</p>
|
||||
</div>
|
||||
|
||||
<div class="footer-links">
|
||||
<a href="/about" class="text-cream-white hover:text-golden-ale transition-colors duration-200">About</a>
|
||||
<a href="/contact" class="text-cream-white hover:text-golden-ale transition-colors duration-200">Contact</a>
|
||||
<a href="/privacy" class="text-cream-white hover:text-golden-ale transition-colors duration-200">Privacy</a>
|
||||
<a href="/terms" class="text-cream-white hover:text-golden-ale transition-colors duration-200">Terms</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-gray-700 mt-6 pt-6 text-center text-sm text-cream-white opacity-70">
|
||||
© {{ now().year }} LeagueLedger. All rights reserved.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Mobile menu toggle
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const menuButton = document.getElementById('mobile-menu-button');
|
||||
const mobileMenu = document.getElementById('mobile-menu');
|
||||
|
||||
menuButton.addEventListener('click', function() {
|
||||
mobileMenu.classList.toggle('hidden');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4">
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-4">Contact Us</h1>
|
||||
<p class="mb-4">
|
||||
Have questions, suggestions, or feedback? We'd love to hear from you!
|
||||
</p>
|
||||
<div class="mb-6">
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Contact Information</h2>
|
||||
<p><strong>Christian Louis IT Beratung</strong></p>
|
||||
<p>Alter Steinweg 3</p>
|
||||
<p>20459 Hamburg</p>
|
||||
<p>Deutschland</p>
|
||||
<p><strong>Phone:</strong> +49 179 5183732</p>
|
||||
<p><strong>Email:</strong> <a href="mailto:quizarium@kaufdeinquiz.com">quizarium@kaufdeinquiz.com</a></p>
|
||||
<p><strong>Fax:</strong> +49 40 97074609</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Connect With Us</h2>
|
||||
<p>This project is part of the KaufDeinQuiz platform and is operated as an Open-Source initiative. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,137 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="space-y-6">
|
||||
<!-- User Welcome -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<h1 class="text-2xl md:text-3xl font-garamond text-irish-green mb-2">Welcome, {{ user.username }}!</h1>
|
||||
<p class="text-gray-600">Here's your quiz league status</p>
|
||||
</div>
|
||||
|
||||
<!-- Stats Overview -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="bg-white p-6 rounded-lg shadow-md text-center">
|
||||
<i class="fas fa-users text-irish-green text-2xl mb-2"></i>
|
||||
<h3 class="font-semibold text-lg mb-1">Your Teams</h3>
|
||||
<p class="text-3xl font-bold">{{ team_count }}</p>
|
||||
</div>
|
||||
<div class="bg-white p-6 rounded-lg shadow-md text-center">
|
||||
<i class="fas fa-star text-golden-ale text-2xl mb-2"></i>
|
||||
<h3 class="font-semibold text-lg mb-1">Total Points</h3>
|
||||
<p class="text-3xl font-bold">{{ total_points }}</p>
|
||||
</div>
|
||||
<div class="bg-white p-6 rounded-lg shadow-md text-center">
|
||||
<i class="fas fa-trophy text-irish-green text-2xl mb-2"></i>
|
||||
<h3 class="font-semibold text-lg mb-1">Best Ranking</h3>
|
||||
<p class="text-3xl font-bold">{% if best_rank %}#{{ best_rank }}{% else %}-{% endif %}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h2 class="text-xl md:text-2xl font-garamond font-semibold text-irish-green mb-4">Recent Activity</h2>
|
||||
{% if recent_activity %}
|
||||
<div class="space-y-3">
|
||||
{% for activity in recent_activity %}
|
||||
<div class="flex items-center border-b pb-3">
|
||||
{% if activity.type == 'qr_redeem' %}
|
||||
<div class="bg-green-100 p-2 rounded-full mr-3">
|
||||
<i class="fas fa-qrcode text-irish-green"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">You redeemed a QR code for {{ activity.points }} points</p>
|
||||
<p class="text-sm text-gray-600">Team: {{ activity.team_name }} • {{ activity.date }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500 italic">No recent activity to show</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h2 class="text-xl font-garamond font-semibold text-irish-green mb-4">Your Teams</h2>
|
||||
{% if teams %}
|
||||
<div class="space-y-3">
|
||||
{% for team in teams %}
|
||||
<div class="flex justify-between items-center border-b pb-2">
|
||||
<div>
|
||||
<p class="font-medium">{{ team.name }}</p>
|
||||
<p class="text-sm text-gray-600">{{ team_points[team.id].points }} points • Rank #{{ team_points[team.id].rank }}</p>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
{% if team.id in admin_team_ids %}
|
||||
<span class="bg-golden-ale text-black-stout text-xs px-2 py-1 rounded-full mr-3">Admin</span>
|
||||
{% endif %}
|
||||
<a href="/teams/{{ team.id }}" class="text-irish-green hover:underline">View</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500 italic">You haven't joined any teams yet</p>
|
||||
{% endif %}
|
||||
<div class="mt-4">
|
||||
<a href="/teams/" class="inline-block bg-irish-green text-white px-4 py-2 rounded-md text-sm">
|
||||
{% if teams %}Manage Teams{% else %}Join a Team{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h2 class="text-xl font-garamond font-semibold text-irish-green mb-4">Redeem Code</h2>
|
||||
<p class="mb-4">Have a QR code from your quiz master? Scan or enter it here to claim points!</p>
|
||||
<div class="flex flex-col space-y-3">
|
||||
<a href="/dashboard/scan" class="bg-irish-green text-white px-4 py-2 rounded-md flex items-center justify-center">
|
||||
<i class="fas fa-camera mr-2"></i> Scan QR Code
|
||||
</a>
|
||||
<form action="/redeem/manual" method="post" class="flex">
|
||||
<input type="text" name="code" placeholder="Or enter code manually"
|
||||
class="flex-grow border border-gray-300 rounded-l-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-irish-green">
|
||||
<button type="submit" class="bg-golden-ale text-black-stout px-4 py-2 rounded-r-md">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leaderboard Preview -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-garamond font-semibold text-irish-green">Leaderboard Preview</h2>
|
||||
<a href="/leaderboard" class="text-irish-green hover:underline">View Full Leaderboard</a>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full">
|
||||
<thead class="bg-irish-green text-white">
|
||||
<tr>
|
||||
<th class="py-2 px-4 text-left rounded-tl-lg">Rank</th>
|
||||
<th class="py-2 px-4 text-left">Team</th>
|
||||
<th class="py-2 px-4 text-right rounded-tr-lg">Points</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
<!-- This would be populated with real data from the backend -->
|
||||
<tr>
|
||||
<td class="py-2 px-4 font-medium">1</td>
|
||||
<td class="py-2 px-4">Quiz Masters</td>
|
||||
<td class="py-2 px-4 text-right font-medium">287</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-2 px-4 font-medium">2</td>
|
||||
<td class="py-2 px-4">Trivia Titans</td>
|
||||
<td class="py-2 px-4 text-right font-medium">215</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-2 px-4 font-medium">3</td>
|
||||
<td class="py-2 px-4">Beer Brainiacs</td>
|
||||
<td class="py-2 px-4 text-right font-medium">194</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-2xl mx-auto mt-8 p-8 bg-white rounded-lg shadow-md">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-circle text-red-600 text-5xl mb-4"></i>
|
||||
<h1 class="text-2xl font-bold mb-4">{{ error_title|default("Error") }}</h1>
|
||||
<p class="text-gray-700 mb-6">{{ error_message|default("An error occurred. Please try again.") }}</p>
|
||||
|
||||
<div class="mt-8">
|
||||
<a href="/" class="text-irish-green hover:underline mr-6">
|
||||
<i class="fas fa-home mr-1"></i> Go to Home
|
||||
</a>
|
||||
<a href="javascript:history.back()" class="text-irish-green hover:underline">
|
||||
<i class="fas fa-arrow-left mr-1"></i> Go Back
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,53 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="flex flex-col items-center">
|
||||
<!-- Hero Section -->
|
||||
<section class="w-full py-8 md:py-16 text-center">
|
||||
<h1 class="text-4xl md:text-6xl font-bold text-irish-green mb-4">PubQuiz League Tracker</h1>
|
||||
<p class="text-xl md:text-2xl mb-8 max-w-3xl mx-auto">Track your team's triumphs across quiz nights and climb the leaderboard.</p>
|
||||
<div class="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<a href="/teams/" class="bg-irish-green text-white font-semibold px-6 py-3 rounded-md shadow-md hover:bg-opacity-90 transition">Join a Team</a>
|
||||
<a href="/dashboard" class="bg-golden-ale text-black-stout font-semibold px-6 py-3 rounded-md shadow-md hover:bg-opacity-90 transition">My Dashboard</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features -->
|
||||
<section class="w-full py-12 bg-white rounded-lg shadow-md my-8">
|
||||
<div class="container mx-auto">
|
||||
<h2 class="text-3xl font-bold text-irish-green text-center mb-10">How It Works</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 px-4">
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<div class="bg-cream-white p-4 rounded-full mb-4">
|
||||
<i class="fas fa-users text-irish-green text-3xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-2">Create or Join Teams</h3>
|
||||
<p>Join existing quiz teams or create your own to start competing.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<div class="bg-cream-white p-4 rounded-full mb-4">
|
||||
<i class="fas fa-qrcode text-irish-green text-3xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-2">Scan QR Codes</h3>
|
||||
<p>Earn points by scanning QR codes distributed by quiz masters.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<div class="bg-cream-white p-4 rounded-full mb-4">
|
||||
<i class="fas fa-trophy text-irish-green text-3xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-2">Climb the Leaderboard</h3>
|
||||
<p>Track your progress and compete to be the top team in the league.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Call to Action -->
|
||||
<section class="w-full py-12 bg-irish-green rounded-lg shadow-md my-8 text-center">
|
||||
<h2 class="text-3xl font-bold text-white mb-4">Ready to Join the League?</h2>
|
||||
<p class="text-white text-xl mb-6">Get started now and track your pub quiz achievements!</p>
|
||||
<a href="/register" class="bg-golden-ale text-black-stout font-semibold px-8 py-3 rounded-md shadow-md hover:bg-opacity-90 transition">Sign Up Now</a>
|
||||
</section>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,117 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="space-y-6">
|
||||
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl md:text-3xl font-garamond font-bold text-irish-green">League Leaderboard</h1>
|
||||
<p class="text-gray-600">See how your team ranks against the competition</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 md:mt-0">
|
||||
<form method="get" action="/leaderboard/">
|
||||
<select name="timeframe"
|
||||
onchange="this.form.submit()"
|
||||
class="border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-irish-green bg-white">
|
||||
<option value="all" {% if timeframe == "all" %}selected{% endif %}>All Time</option>
|
||||
<option value="month" {% if timeframe == "month" %}selected{% endif %}>This Month</option>
|
||||
<option value="week" {% if timeframe == "week" %}selected{% endif %}>This Week</option>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Podium (on larger screens) -->
|
||||
<div class="hidden md:flex justify-center items-end space-x-8 mb-12">
|
||||
<!-- 2nd Place -->
|
||||
{% if top_teams[1] %}
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="w-20 h-20 rounded-full bg-white shadow-md flex items-center justify-center mb-4">
|
||||
<span class="text-4xl font-bold text-gray-500">2</span>
|
||||
</div>
|
||||
<div class="bg-gray-100 w-32 h-32 flex flex-col items-center justify-center rounded-t-lg">
|
||||
<p class="font-bold text-lg">{{ top_teams[1].name }}</p>
|
||||
<p class="text-irish-green font-bold">{{ top_teams[1].points }} pts</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- 1st Place -->
|
||||
{% if top_teams[0] %}
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="w-24 h-24 rounded-full bg-golden-ale shadow-md flex items-center justify-center mb-4">
|
||||
<span class="text-5xl font-bold text-white">1</span>
|
||||
</div>
|
||||
<div class="bg-gray-100 w-36 h-40 flex flex-col items-center justify-center rounded-t-lg shadow-md">
|
||||
<p class="font-bold text-xl">{{ top_teams[0].name }}</p>
|
||||
<p class="text-irish-green font-bold text-xl">{{ top_teams[0].points }} pts</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- 3rd Place -->
|
||||
{% if top_teams[2] %}
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="w-16 h-16 rounded-full bg-[#CD7F32] shadow-md flex items-center justify-center mb-4">
|
||||
<span class="text-3xl font-bold text-white">3</span>
|
||||
</div>
|
||||
<div class="bg-gray-100 w-28 h-28 flex flex-col items-center justify-center rounded-t-lg">
|
||||
<p class="font-bold">{{ top_teams[2].name }}</p>
|
||||
<p class="text-irish-green font-bold">{{ top_teams[2].points }} pts</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Timeframe Indicator -->
|
||||
<div class="bg-irish-green bg-opacity-10 text-irish-green px-4 py-3 rounded-md text-center font-bold mb-6">
|
||||
Showing {{ time_label }} Rankings
|
||||
</div>
|
||||
|
||||
<!-- Leaderboard Table -->
|
||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||
<table class="min-w-full">
|
||||
<thead class="bg-irish-green text-white">
|
||||
<tr>
|
||||
<th class="py-3 px-4 text-left">Rank</th>
|
||||
<th class="py-3 px-4 text-left">Team</th>
|
||||
<th class="py-3 px-4 text-right">Points</th>
|
||||
<th class="py-3 px-4 text-center">Change</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
{% for team in teams %}
|
||||
<tr class="{% if team.rank == 1 %}bg-golden-ale bg-opacity-10{% endif %} hover:bg-gray-50">
|
||||
<td class="py-3 px-4 font-bold">{{ team.rank }}</td>
|
||||
<td class="py-3 px-4">{{ team.name }}</td>
|
||||
<td class="py-3 px-4 text-right font-bold">{{ team.points }}</td>
|
||||
<td class="py-3 px-4 text-center">
|
||||
{% if team.change > 0 %}
|
||||
<span class="text-green-600"><i class="fas fa-arrow-up"></i> {{ team.change }}</span>
|
||||
{% elif team.change < 0 %}
|
||||
<span class="text-red-600"><i class="fas fa-arrow-down"></i> {{ team.change|abs }}</span>
|
||||
{% else %}
|
||||
<span class="text-gray-400">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{% if not teams %}
|
||||
<tr>
|
||||
<td colspan="4" class="py-8 text-center text-gray-500">No teams found. Start a quiz league to see rankings here!</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Team Formation CTA -->
|
||||
<div class="bg-irish-green rounded-lg shadow-md p-6 text-center text-white">
|
||||
<h3 class="text-xl font-garamond font-bold mb-2">Ready to join the rankings?</h3>
|
||||
<p class="mb-4">Create or join a team and start climbing the leaderboard today!</p>
|
||||
<a href="/teams/" class="inline-block bg-white text-irish-green font-bold px-6 py-2 rounded-md hover:bg-gray-100 transition">
|
||||
Join a Team
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4">
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-4">Privacy Policy</h1>
|
||||
<p class="mb-4">
|
||||
Your privacy is important to us. This Privacy Policy outlines how LeagueLedger collects, uses, and protects your information.
|
||||
</p>
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Information We Collect</h2>
|
||||
<ul class="list-disc ml-6 mb-4">
|
||||
<li>Email addresses</li>
|
||||
<li>Usernames</li>
|
||||
<li>Team names</li>
|
||||
<li>Quiz scores and points</li>
|
||||
</ul>
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">How We Use Your Information</h2>
|
||||
<p class="mb-4">
|
||||
We use your information to:
|
||||
</p>
|
||||
<ul class="list-disc ml-6 mb-4">
|
||||
<li>Track leaderboard rankings</li>
|
||||
<li>Send notifications about team activities</li>
|
||||
<li>Improve our services and user experience</li>
|
||||
</ul>
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Data Sharing</h2>
|
||||
<p class="mb-4">
|
||||
We do not share your personal information with third parties except as required by law.
|
||||
</p>
|
||||
<p class="mb-4">
|
||||
Christian Louis IT Beratung und Medienproduktion, as the operator of this platform, takes data privacy seriously and implements measures to protect your personal information.
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,144 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
|
||||
<!-- Profile Header -->
|
||||
<div class="bg-irish-green text-white p-6">
|
||||
<div class="flex flex-col sm:flex-row items-center">
|
||||
<div class="w-24 h-24 rounded-full bg-white border-4 border-white overflow-hidden mb-4 sm:mb-0 sm:mr-6">
|
||||
<img src="https://via.placeholder.com/150" alt="Profile" class="w-full h-full object-cover">
|
||||
</div>
|
||||
<div class="text-center sm:text-left">
|
||||
<h1 class="text-2xl font-bold">John Quizmaster</h1>
|
||||
<p class="text-green-100">Member since October 2022</p>
|
||||
<p class="mt-2">
|
||||
<span class="bg-golden-ale text-black-stout text-xs px-2 py-1 rounded-full font-medium mr-1">Quiz Master</span>
|
||||
<span class="bg-white text-irish-green text-xs px-2 py-1 rounded-full font-medium">Team Captain</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Profile Content -->
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<!-- Left Column: Personal Info -->
|
||||
<div class="md:col-span-1">
|
||||
<h2 class="text-xl font-semibold text-irish-green mb-4">Personal Information</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-600">Display Name</label>
|
||||
<div class="mt-1 flex">
|
||||
<input type="text" value="John Quizmaster" readonly class="flex-grow bg-gray-100 border border-gray-300 rounded-l-md px-3 py-2">
|
||||
<button class="bg-irish-green text-white px-3 py-2 rounded-r-md">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-600">Email</label>
|
||||
<div class="mt-1 flex">
|
||||
<input type="email" value="john@example.com" readonly class="flex-grow bg-gray-100 border border-gray-300 rounded-l-md px-3 py-2">
|
||||
<button class="bg-irish-green text-white px-3 py-2 rounded-r-md">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-600">Password</label>
|
||||
<div class="mt-1">
|
||||
<button class="w-full bg-golden-ale hover:bg-opacity-90 text-black font-medium py-2 px-4 rounded-md transition">
|
||||
Change Password
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-4">
|
||||
<button class="w-full bg-irish-green hover:bg-opacity-90 text-white font-medium py-2 px-4 rounded-md transition">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Stats & Teams -->
|
||||
<div class="md:col-span-2">
|
||||
<h2 class="text-xl font-semibold text-irish-green mb-4">Your Statistics</h2>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div class="bg-cream-white p-4 rounded-lg text-center">
|
||||
<p class="text-sm text-gray-600">Total Points</p>
|
||||
<p class="text-2xl font-bold text-irish-green">378</p>
|
||||
</div>
|
||||
<div class="bg-cream-white p-4 rounded-lg text-center">
|
||||
<p class="text-sm text-gray-600">QR Codes Redeemed</p>
|
||||
<p class="text-2xl font-bold text-irish-green">24</p>
|
||||
</div>
|
||||
<div class="bg-cream-white p-4 rounded-lg text-center">
|
||||
<p class="text-sm text-gray-600">Teams Joined</p>
|
||||
<p class="text-2xl font-bold text-irish-green">3</p>
|
||||
</div>
|
||||
<div class="bg-cream-white p-4 rounded-lg text-center">
|
||||
<p class="text-sm text-gray-600">Best Position</p>
|
||||
<p class="text-2xl font-bold text-irish-green">#2</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="text-xl font-semibold text-irish-green mb-4">Your Teams</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<div class="bg-cream-white px-4 py-3 flex justify-between items-center">
|
||||
<div class="font-medium">Quiz Wizards</div>
|
||||
<span class="bg-golden-ale text-black-stout text-xs px-2 py-1 rounded-full">Captain</span>
|
||||
</div>
|
||||
<div class="px-4 py-3 flex justify-between items-center">
|
||||
<div>
|
||||
<p class="text-sm">Current Points: <span class="font-bold">187</span></p>
|
||||
<p class="text-sm text-gray-600">Current Rank: #4</p>
|
||||
</div>
|
||||
<a href="/teams/1" class="text-irish-green hover:underline text-sm">View Team</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<div class="bg-cream-white px-4 py-3 flex justify-between items-center">
|
||||
<div class="font-medium">Trivia Titans</div>
|
||||
<span class="bg-gray-200 text-gray-700 text-xs px-2 py-1 rounded-full">Member</span>
|
||||
</div>
|
||||
<div class="px-4 py-3 flex justify-between items-center">
|
||||
<div>
|
||||
<p class="text-sm">Current Points: <span class="font-bold">215</span></p>
|
||||
<p class="text-sm text-gray-600">Current Rank: #2</p>
|
||||
</div>
|
||||
<a href="/teams/2" class="text-irish-green hover:underline text-sm">View Team</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="/teams/" class="block text-center text-irish-green hover:underline text-sm">
|
||||
<i class="fas fa-plus mr-1"></i> Join or Create Another Team
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Danger Zone -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<h3 class="text-xl font-semibold text-red-600 mb-4">Danger Zone</h3>
|
||||
<p class="text-gray-600 mb-4">The following actions are irreversible. Please proceed with caution.</p>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4">
|
||||
<button class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-md transition">
|
||||
Delete Account
|
||||
</button>
|
||||
<button class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-md transition">
|
||||
Leave All Teams
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,77 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-lg mx-auto">
|
||||
<!-- QR Code Redemption Box -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
|
||||
<div class="text-center mb-6">
|
||||
<i class="fas fa-qrcode text-irish-green text-5xl mb-4"></i>
|
||||
<h1 class="text-2xl font-bold text-irish-green">QR Code Redemption</h1>
|
||||
<p class="text-gray-600">Collect your points from the quiz master!</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-8 p-4 bg-irish-green bg-opacity-10 rounded-lg border border-irish-green text-center">
|
||||
<p class="font-bold text-irish-green mb-1">Congratulations!</p>
|
||||
<p class="text-lg">You've earned <span class="font-bold">{{ ticket.points }}</span> points</p>
|
||||
<p class="text-sm text-gray-600 mt-2">Code: {{ ticket.code }}</p>
|
||||
</div>
|
||||
|
||||
<form action="/redeem/apply/{{ ticket.code }}" method="post">
|
||||
<div class="mb-6">
|
||||
<label for="team_id" class="block text-sm font-medium text-gray-700 mb-2">Select Team to Award Points:</label>
|
||||
<select name="team_id" id="team_id" required class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-irish-green">
|
||||
<option value="">-- Select a team --</option>
|
||||
{% for team in user_teams %}
|
||||
<option value="{{ team.id }}">{{ team.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full bg-irish-green hover:bg-opacity-90 text-white font-bold py-3 px-4 rounded-md transition">
|
||||
Redeem Points
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-6 text-center">
|
||||
<a href="/teams/" class="text-irish-green hover:underline text-sm">
|
||||
<i class="fas fa-users mr-1"></i> Create a New Team
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- How It Works -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<h3 class="text-xl font-semibold text-irish-green mb-4">How It Works</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-start">
|
||||
<div class="bg-cream-white p-2 rounded-full mr-3 mt-1">
|
||||
<span class="w-6 h-6 flex items-center justify-center text-irish-green font-bold">1</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">Scan QR Code</p>
|
||||
<p class="text-gray-600 text-sm">Scan the QR code provided by your quiz master.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start">
|
||||
<div class="bg-cream-white p-2 rounded-full mr-3 mt-1">
|
||||
<span class="w-6 h-6 flex items-center justify-center text-irish-green font-bold">2</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">Select Your Team</p>
|
||||
<p class="text-gray-600 text-sm">Choose which team should receive these points.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start">
|
||||
<div class="bg-cream-white p-2 rounded-full mr-3 mt-1">
|
||||
<span class="w-6 h-6 flex items-center justify-center text-irish-green font-bold">3</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">Climb the Leaderboard</p>
|
||||
<p class="text-gray-600 text-sm">Watch your team rise in the rankings!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,43 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-lg mx-auto">
|
||||
<div class="bg-white rounded-lg shadow-md p-6 mb-8 text-center">
|
||||
<div class="mb-6">
|
||||
<div class="inline-block p-4 rounded-full bg-green-100">
|
||||
<i class="fas fa-check-circle text-irish-green text-5xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-4">Success!</h1>
|
||||
<p class="text-xl mb-2">You've earned <span class="font-bold">{{ points }}</span> points</p>
|
||||
<p class="text-gray-600 mb-6">Points have been added to <span class="font-semibold">{{ team.name }}</span></p>
|
||||
|
||||
<div class="p-4 bg-irish-green bg-opacity-10 rounded-lg border border-irish-green text-left mb-6">
|
||||
<h3 class="font-bold text-irish-green mb-2">What's next?</h3>
|
||||
<ul class="list-disc ml-5 text-gray-700">
|
||||
<li>Check your team's position on the <a href="/leaderboard" class="text-irish-green hover:underline">leaderboard</a></li>
|
||||
<li>Scan another QR code to earn more points</li>
|
||||
<li>Invite friends to your team</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:flex-row justify-center space-y-3 sm:space-y-0 sm:space-x-4">
|
||||
<a href="/dashboard" class="bg-irish-green hover:bg-opacity-90 text-white font-medium py-2 px-4 rounded-md transition">
|
||||
Go to Dashboard
|
||||
</a>
|
||||
<a href="/dashboard/scan" class="bg-cream-white border border-irish-green text-irish-green font-medium py-2 px-4 rounded-md hover:bg-irish-green hover:text-white transition">
|
||||
Scan Another Code
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-gray-600">
|
||||
<p>Share your achievement!</p>
|
||||
<div class="flex justify-center space-x-4 mt-2">
|
||||
<a href="#" class="text-blue-600 hover:text-opacity-80"><i class="fab fa-facebook fa-lg"></i></a>
|
||||
<a href="#" class="text-blue-400 hover:text-opacity-80"><i class="fab fa-twitter fa-lg"></i></a>
|
||||
<a href="#" class="text-green-600 hover:text-opacity-80"><i class="fab fa-whatsapp fa-lg"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,127 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-lg mx-auto space-y-6">
|
||||
<div class="bg-white rounded-lg shadow-md p-6 text-center">
|
||||
<h1 class="text-2xl font-garamond font-bold text-irish-green mb-3">Scan QR Code</h1>
|
||||
<p class="text-gray-600 mb-6">Position the QR code from your quiz master in the camera view</p>
|
||||
|
||||
<!-- QR Scanner Container -->
|
||||
<div class="relative bg-black rounded-lg overflow-hidden" style="height: 300px;">
|
||||
<div id="qr-reader" class="w-full h-full"></div>
|
||||
<div id="scanner-overlay" class="absolute inset-0 flex items-center justify-center">
|
||||
<div class="text-white">
|
||||
<i class="fas fa-camera text-4xl mb-2 opacity-70"></i>
|
||||
<p>Camera loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 text-sm text-gray-500">
|
||||
Make sure the QR code is well-lit and clearly visible
|
||||
</div>
|
||||
|
||||
<!-- Manual Entry Fallback -->
|
||||
<div class="mt-8 border-t pt-6">
|
||||
<h3 class="font-bold mb-3">Or enter code manually</h3>
|
||||
<form action="/redeem/manual" method="post" class="flex">
|
||||
<input type="text" name="code" placeholder="Enter code here" required
|
||||
class="flex-grow border border-gray-300 rounded-l-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-irish-green">
|
||||
<button type="submit" class="bg-irish-green text-white px-4 py-2 rounded-r-md">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- How It Works -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<h3 class="text-xl font-semibold font-garamond text-irish-green mb-4">How It Works</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-start">
|
||||
<div class="bg-cream-white p-2 rounded-full mr-3 mt-1">
|
||||
<span class="w-6 h-6 flex items-center justify-center text-irish-green font-bold">1</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">Scan QR Code</p>
|
||||
<p class="text-gray-600 text-sm">Scan the QR code provided by your quiz master.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start">
|
||||
<div class="bg-cream-white p-2 rounded-full mr-3 mt-1">
|
||||
<span class="w-6 h-6 flex items-center justify-center text-irish-green font-bold">2</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">Select Your Team</p>
|
||||
<p class="text-gray-600 text-sm">Choose which team should receive these points.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start">
|
||||
<div class="bg-cream-white p-2 rounded-full mr-3 mt-1">
|
||||
<span class="w-6 h-6 flex items-center justify-center text-irish-green font-bold">3</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">Climb the Leaderboard</p>
|
||||
<p class="text-gray-600 text-sm">Watch your team rise in the rankings!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Back Link -->
|
||||
<div class="text-center">
|
||||
<a href="/dashboard" class="text-irish-green hover:underline">
|
||||
<i class="fas fa-arrow-left mr-1"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HTML5 QR Code Scanner Library -->
|
||||
<script src="https://unpkg.com/html5-qrcode"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const html5QrCode = new Html5Qrcode("qr-reader");
|
||||
const qrOverlay = document.getElementById('scanner-overlay');
|
||||
|
||||
// Config
|
||||
const config = { fps: 10, qrbox: 250 };
|
||||
|
||||
// Success function
|
||||
function onScanSuccess(decodedText, decodedResult) {
|
||||
// Stop scanning
|
||||
html5QrCode.stop();
|
||||
|
||||
// Show loading message
|
||||
qrOverlay.innerHTML = '<div class="text-white text-center"><i class="fas fa-circle-notch fa-spin text-3xl mb-3"></i><p>Code detected!</p><p>Redirecting...</p></div>';
|
||||
qrOverlay.style.backgroundColor = 'rgba(0, 104, 55, 0.8)'; // Irish green with opacity
|
||||
|
||||
// Redirect to redeem page
|
||||
window.location.href = '/redeem/' + decodedText;
|
||||
}
|
||||
|
||||
// Start scanner
|
||||
html5QrCode.start(
|
||||
{ facingMode: "environment" },
|
||||
config,
|
||||
onScanSuccess,
|
||||
(errorMessage) => {
|
||||
// Handle error if needed
|
||||
console.log(errorMessage);
|
||||
})
|
||||
.then(() => {
|
||||
// Scanner started successfully
|
||||
qrOverlay.style.display = 'none';
|
||||
})
|
||||
.catch((err) => {
|
||||
// Update overlay with error message
|
||||
qrOverlay.innerHTML = `
|
||||
<div class="text-white text-center">
|
||||
<i class="fas fa-exclamation-triangle text-4xl mb-3"></i>
|
||||
<p>Camera access denied or not available</p>
|
||||
<p class="text-sm mt-2">Please use manual entry below</p>
|
||||
</div>
|
||||
`;
|
||||
qrOverlay.style.backgroundColor = 'rgba(178, 34, 34, 0.8)'; // Guinness red with opacity
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,189 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="space-y-6">
|
||||
<!-- Team Header -->
|
||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||
<div class="bg-irish-green p-6">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<div class="mb-4 md:mb-0">
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-white">{{ team.name }}</h1>
|
||||
<div class="flex items-center space-x-2 text-green-100">
|
||||
<span><i class="fas fa-trophy mr-1"></i> Rank #{{ team_rank }}</span>
|
||||
<span class="hidden md:inline">•</span>
|
||||
<span><i class="fas fa-star mr-1"></i> {{ total_points }} Points</span>
|
||||
<span class="hidden md:inline">•</span>
|
||||
<span><i class="fas fa-users mr-1"></i> {{ team_members|length }} Members</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="bg-white text-irish-green font-medium py-2 px-4 rounded-md hover:bg-opacity-90">
|
||||
<i class="fas fa-share-alt mr-1"></i> Share Team
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Team Stats -->
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<h3 class="text-sm font-medium text-gray-500 mb-1">POINTS THIS MONTH</h3>
|
||||
<p class="text-2xl font-bold">{{ points_this_month }}</p>
|
||||
<p class="text-sm {% if point_change_positive %}text-green-600{% else %}text-red-600{% endif %}">
|
||||
<i class="fas {% if point_change_positive %}fa-arrow-up{% else %}fa-arrow-down{% endif %} mr-1"></i>
|
||||
{{ point_change|abs }} from last month
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm font-medium text-gray-500 mb-1">BEST PERFORMANCE</h3>
|
||||
<p class="text-2xl font-bold">1st Place</p>
|
||||
<p class="text-sm text-gray-600">August 12, 2023</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm font-medium text-gray-500 mb-1">TEAM FOUNDED</h3>
|
||||
<p class="text-2xl font-bold">{{ days_ago }} days ago</p>
|
||||
<p class="text-sm text-gray-600">{{ founded_date }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Team Members & Performance -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Team Members -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-semibold text-irish-green">Team Members</h2>
|
||||
<button class="text-irish-green hover:text-opacity-80">
|
||||
<i class="fas fa-user-plus"></i> Invite
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
{% for member in team_members %}
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<div class="w-10 h-10 rounded-full bg-gray-200 mr-3 overflow-hidden">
|
||||
<img src="https://via.placeholder.com/40" alt="User" class="w-full h-full object-cover">
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">{{ member.user.username }}</p>
|
||||
<p class="text-xs text-gray-500">Joined {{ member.joined }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if member.is_admin %}
|
||||
<span class="bg-golden-ale text-black-stout text-xs px-2 py-1 rounded-full">Captain</span>
|
||||
{% else %}
|
||||
<span class="bg-gray-200 text-gray-700 text-xs px-2 py-1 rounded-full">Member</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Performance Chart -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<h2 class="text-xl font-semibold text-irish-green mb-4">Team Performance</h2>
|
||||
|
||||
<!-- Placeholder for a chart - in real app, use Chart.js or similar -->
|
||||
<div class="bg-gray-100 p-4 rounded-lg h-64 flex items-center justify-center">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-chart-line text-5xl text-irish-green mb-2"></i>
|
||||
<p class="text-gray-500">Performance chart would appear here</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 space-y-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<p>Last quiz night</p>
|
||||
<p class="font-medium">{{ performance.last_quiz }}</p>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<p>Average per quiz</p>
|
||||
<p class="font-medium">{{ performance.average }}</p>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<p>Best streak</p>
|
||||
<p class="font-medium">{{ performance.best_streak }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<h2 class="text-xl font-semibold text-irish-green mb-4">Recent Activity</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
{% for activity in activities %}
|
||||
{% if activity.type == 'points' %}
|
||||
<div class="flex items-start">
|
||||
<div class="bg-green-100 p-2 rounded-full mr-3">
|
||||
<i class="fas fa-trophy text-irish-green"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">Earned {{ activity.points }} points in "{{ activity.event }}"</p>
|
||||
<p class="text-sm text-gray-600">{{ activity.date }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% elif activity.type == 'join' %}
|
||||
<div class="flex items-start">
|
||||
<div class="bg-blue-100 p-2 rounded-full mr-3">
|
||||
<i class="fas fa-user-plus text-blue-700"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">{{ activity.user }} joined the team</p>
|
||||
<p class="text-sm text-gray-600">{{ activity.date }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% elif activity.type == 'achievement' %}
|
||||
<div class="flex items-start">
|
||||
<div class="bg-golden-ale bg-opacity-20 p-2 rounded-full mr-3">
|
||||
<i class="fas fa-medal text-golden-ale"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium">Achieved {{ activity.achievement }} in "{{ activity.event }}"</p>
|
||||
<p class="text-sm text-gray-600">{{ activity.date }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="mt-6 text-center">
|
||||
<a href="#" class="text-irish-green hover:underline">View Full History</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Team Management -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<h2 class="text-xl font-semibold text-irish-green mb-4">Team Management</h2>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Team Name</label>
|
||||
<div class="flex">
|
||||
<input type="text" value="{{ team.name }}" class="flex-grow border border-gray-300 rounded-l-md px-3 py-2" {% if not is_user_admin %}disabled{% endif %}>
|
||||
<button class="bg-irish-green text-white px-4 py-2 rounded-r-md" {% if not is_user_admin %}disabled{% endif %}>Save</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Team Privacy</label>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox" id="public-team" name="is_public" class="mr-2"
|
||||
{% if team.is_public %}checked{% endif %}
|
||||
{% if not is_user_admin %}disabled{% endif %}>
|
||||
<label for="public-team">Make team publicly joinable (no invitation needed)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-4 border-t">
|
||||
<button class="bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-md">
|
||||
Leave Team
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,54 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h2 class="text-2xl font-bold mb-6">Teams</h2>
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-semibold mb-4" style="color: var(--irish-green);">Available Teams</h3>
|
||||
{% if teams %}
|
||||
<ul class="space-y-3">
|
||||
{% for team in teams %}
|
||||
<li class="border-b pb-2 flex justify-between items-center">
|
||||
<span class="font-medium">{{ team.name }}</span>
|
||||
<form action="/teams/join/{{ team.id }}" method="post" class="inline">
|
||||
<button type="submit"
|
||||
class="px-3 py-1 text-sm rounded-md"
|
||||
style="background-color: var(--golden-ale); color: var(--black-stout);">
|
||||
Join Team
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="italic text-gray-500">No teams available yet.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-semibold mb-4" style="color: var(--irish-green);">Create New Team</h3>
|
||||
<form action="/teams/create" method="post" class="mt-4">
|
||||
<div class="mb-4">
|
||||
<label for="name" class="block text-sm font-medium mb-1">Team Name</label>
|
||||
<input type="text" name="name" id="name" placeholder="Enter team name" required
|
||||
class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2"
|
||||
style="border-color: var(--irish-green); focus:ring-color: var(--irish-green);">
|
||||
</div>
|
||||
<button type="submit"
|
||||
class="w-full px-4 py-2 text-white rounded-md font-medium"
|
||||
style="background-color: var(--irish-green);">
|
||||
Create Team
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-10 bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-semibold mb-4" style="color: var(--irish-green);">About Teams</h3>
|
||||
<p class="mb-4">
|
||||
Teams are the heart of LeagueLedger. Join an existing team or create your own to start tracking your pub quiz triumphs!
|
||||
</p>
|
||||
<p class="italic">
|
||||
Every point counts in the journey to becoming pub quiz champions.
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,27 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4">
|
||||
<h1 class="text-2xl font-bold text-irish-green mb-4">Terms of Service</h1>
|
||||
<p class="mb-4">
|
||||
Welcome to LeagueLedger! By using our platform, you agree to comply with the following terms and conditions.
|
||||
</p>
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Acceptable Use</h2>
|
||||
<ul class="list-disc ml-6 mb-4">
|
||||
<li>No cheating or unfair practices</li>
|
||||
<li>Respectful communication with other users</li>
|
||||
<li>Compliance with all applicable laws and regulations</li>
|
||||
</ul>
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Liability Disclaimer</h2>
|
||||
<p class="mb-4">
|
||||
LeagueLedger is provided "as is" without any warranties. We are not liable for any damages arising from your use of the platform.
|
||||
</p>
|
||||
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Governing Law</h2>
|
||||
<p class="mb-4">
|
||||
These terms shall be governed by and construed in accordance with the laws of Germany.
|
||||
</p>
|
||||
<p class="mb-4">
|
||||
This project is part of the KaufDeinQuiz platform and is operated as an Open-Source initiative. All rights reserved.
|
||||
</p>
|
||||
<p>Christian Louis IT Beratung und Medienproduktion is responsible for the operation of this platform.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user