Compare commits

..

38 Commits

Author SHA1 Message Date
nik8
69dc2530ce Schrift in Kasten 2025-03-16 17:48:31 +01:00
nik8
e0bfda0dee Impressum_Datenschutzerklärung_ansprechend 2025-03-16 17:41:11 +01:00
nik8
23675bc69e Merge branch '36-verwalten-mit-konto-mit-der-bibliothek-verbinden' into 'master'
Resolve "Verwalten mit Erstellbutton ersetzen und Verknüpfung herstellen"

Closes #36

See merge request enrichment-2024/qivip!16
2025-03-16 16:04:05 +00:00
nik8
94659791e6 fix#36 2025-03-16 17:03:26 +01:00
nik8
17cc0af02d Merge branch '40-offentliche-bibliothek' into 'master'
Resolve "Öffentliche Bibliothek"

Closes #40

See merge request enrichment-2024/qivip!14
2025-03-16 15:30:41 +00:00
nik8
f28878e1b0 small_changes_Datenbank 2025-03-16 16:27:50 +01:00
nik8
2a8b0b813c öffentliche_Library_und_Datenbankchanges 2025-03-16 14:51:27 +01:00
Juhn-Vitus Saß
f18ed83f9d Merge branch '41-quiz-id-updaten-wenn-participant-ein-anderes-quiz-beitritt' into 'master'
Resolve "Quiz ID updaten wenn Participant einem anderen Quiz beitritt"

Closes #41

See merge request enrichment-2024/qivip!13
2025-03-16 12:18:35 +00:00
juhnsa
d1cca38595 Quiz Titel anzeigen 2025-03-16 11:54:00 +01:00
juhnsa
1f1344522f Quiz Instanz von Teilnehmer updaten, wenn dieser einem anderen Quiz beitritt 2025-03-16 09:55:36 +01:00
Juhn-Vitus Saß
7cad46063e Merge branch '31-projektordner-core-umbenennen-in-z-b-django-2' into 'master'
Resolve "Projektordner "core" umbenennen in z.B. Django"

Closes #31

See merge request enrichment-2024/qivip!12
2025-03-16 08:23:23 +00:00
juhnsa
55750bcf1f Django Projektordner von 'core' in ''django' umbenennen 2025-03-15 21:48:35 +01:00
Juhn-Vitus Saß
230944cbcd Merge branch '35-seite-fur-namen-und-avatar-auswahl-erstellen' into 'master'
Resolve "Seite für Namen und Avatar Auswahl erstellen"

Closes #35

See merge request enrichment-2024/qivip!11
2025-03-15 20:16:28 +00:00
Juhn-Vitus Saß
2f47ff8523 Merge branch '34-der-code-soll-nur-aus-zahlen-bestehen' into 'master'
Resolve "Der Code soll nur aus Zahlen bestehen!"

Closes #34

See merge request enrichment-2024/qivip!10
2025-03-15 20:15:57 +00:00
juhnsa
3ef7d61da6 Teilnehmlogik grundlegend fertiggestellt 2025-03-15 20:30:49 +01:00
juhnsa
7fe29c5bd4 Warnmeldung beheben 2025-03-15 19:41:13 +01:00
Juhn-Vitus Saß
5775e53bb8 Fix template name 2025-03-15 16:30:31 +01:00
Juhn-Vitus Saß
57682d4607 Fix routing issue 2025-03-15 16:30:09 +01:00
ben8
915552ac4a fix#37 2025-03-15 16:18:28 +01:00
861e89ae4d Merge branch '35-seite-fur-namen-und-avatar-auswahl-erstellen' of edugit.org:enrichment-2024/qivip into 35-seite-fur-namen-und-avatar-auswahl-erstellen 2025-03-15 16:13:44 +01:00
97fd326561 Create initialize_participant.html 2025-03-15 16:11:35 +01:00
Juhn-Vitus Saß
34a9ab6a43 View erstellen 2025-03-15 16:10:34 +01:00
ben8
4c597473b7 Merge branch 'master' of edugit.org:enrichment-2024/qivip 2025-03-15 15:53:19 +01:00
ben8
8119f202d3 design_changes 2025-03-15 15:53:16 +01:00
6beccc772a Add initialize_participant.html 2025-03-15 15:50:41 +01:00
Frank Poetzsch-Heffter
4797a74111 almost 6 digits 2025-03-15 15:42:21 +01:00
Juhn-Vitus Saß
30b5cd9731 Fix Button 2025-03-15 15:31:52 +01:00
Juhn-Vitus Saß
6b83f26fc0 Merge branch '21-host-und-spiel-app-erstellen-und-grundlegende-templates-erstellen' into 'master'
Resolve "Host und Spiel App erstellen und Grundlegende Templates erstellen"

Closes #21

See merge request enrichment-2024/qivip!9
2025-03-15 14:26:45 +00:00
Juhn-Vitus Saß
346ac69046 Join Mechanik erstellen 2025-03-15 15:25:39 +01:00
Juhn-Vitus Saß
838eaab9be Fix Code generation 2025-03-15 14:59:53 +01:00
Juhn-Vitus Saß
eba34fa2d4 Merge branch '21-host-und-spiel-app-erstellen-und-grundlegende-templates-erstellen' of edugit.org:enrichment-2024/qivip into 21-host-und-spiel-app-erstellen-und-grundlegende-templates-erstellen 2025-03-15 14:53:00 +01:00
Juhn-Vitus Saß
9f51def959 Fix Admin 2025-03-15 14:52:58 +01:00
c8bf38dd36 Structure Installed Apps 2025-03-15 14:51:36 +01:00
Juhn-Vitus Saß
7120dcd317 DB Model für Quiz Spiele 2025-03-15 14:50:57 +01:00
ben8
da419533e9 Merge branch 'master' of edugit.org:enrichment-2024/qivip 2025-03-15 14:14:43 +01:00
ben8
bd0aa651af Teilnhame_verbunden 2025-03-15 14:14:40 +01:00
Juhn-Vitus Saß
d1b11a0c51 Update Gitignore 2025-03-15 13:46:34 +01:00
Juhn-Vitus Saß
5dc849f551 Update Gitignore 2025-03-15 13:45:49 +01:00
31 changed files with 470 additions and 63 deletions

3
.gitignore vendored
View File

@@ -2,4 +2,5 @@ __pycache__
.venv .venv
db.sqlite3 db.sqlite3
tailwindcss.exe tailwindcss.exe
t-style.css t-style.css
dump.rdb

View File

@@ -35,7 +35,7 @@ INSTALLED_APPS = [
'homepage.apps.HomepageConfig', 'homepage.apps.HomepageConfig',
'components.apps.ComponentsConfig', 'components.apps.ComponentsConfig',
'accounts.apps.AccountsConfig', 'accounts.apps.AccountsConfig',
'play.apps.PlayConfig', 'play',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-16 12:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0015_alter_qivipquiz_difficulty'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='status',
field=models.CharField(choices=[('öffentlich', 'Öffentlich'), ('versteckt', 'Versteckt'), ('privat', 'Privat')], default='öffentlich', max_length=10),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-16 12:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0016_alter_qivipquiz_status'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='difficulty',
field=models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('nicht gesetzt', 'nicht gesetzt')], default='nicht gesetzt', help_text='1: niedrigste Schwierigkeit und 5: höchste Schwierigkeit', max_length=13),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-16 13:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0017_alter_qivipquiz_difficulty'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(blank=True, default='In dem Quiz geht es um ...', max_length=150, null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-16 13:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0018_alter_qivipquiz_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(default='In dem Quiz geht es um ...', max_length=255, null=True),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1.7 on 2025-03-16 13:08
import library.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0019_alter_qivipquiz_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(default='In dem Quiz geht es um ...', max_length=255, validators=[library.models.QivipQuiz.validate_description]),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-16 14:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0020_alter_qivipquiz_description'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='name',
field=models.CharField(max_length=100),
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 5.1.7 on 2025-03-16 14:35
import library.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0021_alter_qivipquiz_name'),
]
operations = [
migrations.AlterField(
model_name='qivipquiz',
name='description',
field=models.TextField(default='In dem Quiz geht es um ...', max_length=200, validators=[library.models.QivipQuiz.validate_description]),
),
migrations.AlterField(
model_name='qivipquiz',
name='name',
field=models.CharField(max_length=75),
),
]

View File

@@ -2,12 +2,13 @@ from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.text import slugify from django.utils.text import slugify
import uuid import uuid
from django.core.exceptions import ValidationError
class QivipQuiz(models.Model): class QivipQuiz(models.Model):
STATUS_VALUES = { STATUS_VALUES = {
"public": "Öffentlich", "öffentlich": "Öffentlich",
"hidden": "Versteckt", "versteckt": "Versteckt",
"private": "Privat", "privat": "Privat",
} }
DIFFICULTY_VALUES = { DIFFICULTY_VALUES = {
"1": "1", "1": "1",
@@ -15,20 +16,25 @@ class QivipQuiz(models.Model):
"3": "3", "3": "3",
"4": "4", "4": "4",
"5": "5", "5": "5",
"not defined":"nicht gesetzt", "nicht gesetzt":"nicht gesetzt",
} }
def validate_description(value):
if value.strip() == "In dem Quiz geht es um ...":
raise ValidationError("Bitte gib eine aussagekräftige Beschreibung ein.")
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name='quiz') user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name='quiz')
creation_date = models.DateTimeField(auto_now_add=True) creation_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True) update_date = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=list(STATUS_VALUES.items()), default="public") status = models.CharField(max_length=10, choices=list(STATUS_VALUES.items()), default="öffentlich")
category = models.ForeignKey('QuizCategory', related_name='quiz', on_delete=models.CASCADE) category = models.ForeignKey('QuizCategory', related_name='quiz', on_delete=models.CASCADE)
tags = models.ManyToManyField('Tag', blank=True) tags = models.ManyToManyField('Tag', blank=True)
name = models.CharField(max_length=255) name = models.CharField(max_length=75)
description = models.TextField(blank=True, null=True, default="In dem Quiz geht es um ...") description = models.TextField(validators=[validate_description],max_length=200,blank=False, default="In dem Quiz geht es um ...")
difficulty= models.CharField(max_length=11, choices=list(DIFFICULTY_VALUES.items()), default="not defined", help_text="1: niedrigste Schwierigkeit und 5: höchste Schwierigkeit") difficulty= models.CharField(max_length=13, choices=list(DIFFICULTY_VALUES.items()), default="nicht gesetzt", help_text="1: niedrigste Schwierigkeit und 5: höchste Schwierigkeit")
def __str__(self): def __str__(self):
return self.name return self.name

View File

@@ -6,12 +6,18 @@ from django.contrib import messages
from .models import QivipQuiz, QivipQuestion from .models import QivipQuiz, QivipQuestion
from .forms import QuizForm, QuestionForm from .forms import QuizForm, QuestionForm
import json import json
from django.core.paginator import Paginator
# Übersicht aller Quizze # Übersicht aller Quizze
@login_required
def overview_quiz(request): def overview_quiz(request):
quizzes = QivipQuiz.objects.filter(user_id=request.user) try:
return render(request, 'library/overview_quiz.html', {'quizzes': quizzes}) quizzes = QivipQuiz.objects.filter(user_id=request.user)
all_quizzes = QivipQuiz.objects.filter(status='öffentlich').exclude(user_id=request.user).filter(question__isnull=False).distinct()
return render(request, 'library/overview_quiz.html', {'quizzes': quizzes, 'all_quizzes': all_quizzes})
except:
all_quizzes = QivipQuiz.objects.filter(status='öffentlich').filter(question__isnull=False).distinct()
return render(request, 'library/overview_quiz.html', {'quizzes': None, 'all_quizzes': all_quizzes})
# Neues Quiz erstellen # Neues Quiz erstellen
@login_required @login_required

View File

@@ -1,3 +1,6 @@
from django.contrib import admin from django.contrib import admin
from .models import QuizGame, QuizGameParticipant
# Register your models here. # Register your models here.
admin.site.register(QuizGame)
admin.site.register(QuizGameParticipant)

View File

@@ -0,0 +1,37 @@
# Generated by Django 5.1.7 on 2025-03-15 13:48
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('library', '0015_alter_qivipquiz_difficulty'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='QuizGame',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('join_code', models.CharField(max_length=6, unique=True)),
('host_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='host_user', to=settings.AUTH_USER_MODEL)),
('quiz_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='library.qivipquiz')),
],
),
migrations.CreateModel(
name='QuizGameParticipant',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('display_name', models.CharField(max_length=15, verbose_name='Anzeigename')),
('score', models.IntegerField(max_length=10, verbose_name='Punkte')),
('avatar', models.CharField(max_length=200)),
('quiz_game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participant', to='play.quizgame')),
],
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-15 13:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('play', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='quizgame',
name='join_code',
field=models.CharField(blank=True, max_length=6, unique=True),
),
]

View File

@@ -0,0 +1,29 @@
# Generated by Django 5.1.7 on 2025-03-15 19:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('play', '0002_alter_quizgame_join_code'),
]
operations = [
migrations.AddField(
model_name='quizgameparticipant',
name='participant_id',
field=models.CharField(default=1, max_length=200, unique=True),
preserve_default=False,
),
migrations.AlterField(
model_name='quizgameparticipant',
name='avatar',
field=models.CharField(blank=True, default='', max_length=200),
),
migrations.AlterField(
model_name='quizgameparticipant',
name='score',
field=models.IntegerField(default=0, verbose_name='Punkte'),
),
]

View File

@@ -1,3 +1,40 @@
from django.db import models from django.db import models
from django.contrib.auth.models import User
from library.models import QivipQuiz
from django.http import response
import random, string
# Create your models here. # Create your models here.
class QuizGame(models.Model):
host_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='host_user')
join_code = models.CharField(max_length=6, unique=True, blank=True)
quiz_id = models.ForeignKey(QivipQuiz, on_delete=models.CASCADE)
def save(self, *args, **kwargs):
if not self.join_code:
self.join_code = self.generate_unique_code()
super().save(*args, **kwargs)
def generate_unique_code(self):
for i in range(10):
new_code = ''.join(random.choices(string.digits, k=6))
if not QuizGame.objects.filter(join_code=new_code).exists():
return new_code
class QuizGameParticipant(models.Model):
participant_id = models.CharField(max_length=200, unique=True)
display_name = models.CharField(verbose_name="Anzeigename", max_length=15)
quiz_game = models.ForeignKey(QuizGame, on_delete=models.CASCADE, related_name="participant")
score = models.IntegerField(verbose_name="Punkte", default=0)
avatar = models.CharField(max_length=200, blank=True, default="")
def save(self, *args, **kwargs):
if not self.participant_id:
self.participant_id = self.generate_unique_id()
super().save(*args, **kwargs)
def generate_unique_id(self):
for i in range(10):
new_id = ''.join(random.choices(string.digits + string.ascii_lowercase, k=60))
if not QuizGameParticipant.objects.filter(participant_id=new_id).exists():
return new_id

View File

@@ -4,6 +4,7 @@ from . import views
app_name = 'play' app_name = 'play'
urlpatterns = [ urlpatterns = [
path('lobby', views.lobby, name='lobby'), path('lobby/<str:join_code>', views.lobby, name='lobby'),
path('lobby/<str:join_code>/participate', views.create_participant, name='create_participant'),
path('join', views.join_game, name='join_game'), path('join', views.join_game, name='join_game'),
] ]

View File

@@ -1,8 +1,60 @@
from django.shortcuts import render from django.shortcuts import render
from django.shortcuts import render, redirect, get_object_or_404, reverse
from play.models import QuizGame, QuizGameParticipant
from library.models import QivipQuiz
from django.http import HttpResponseRedirect
# Create your views here. # Create your views here.
def lobby(request): def lobby(request, join_code):
return render(request, 'play/lobby.html') if not "participant_id" in request.COOKIES:
return redirect('play:create_participant', join_code=join_code)
participant_id = request.COOKIES['participant_id']
quiz_game = get_object_or_404(QuizGame, join_code=join_code)
quiz = get_object_or_404(QivipQuiz, pk=quiz_game.quiz_id.id)
participant = get_object_or_404(QuizGameParticipant, participant_id=participant_id)
if not participant.quiz_game.join_code == join_code:
participant.quiz_game = quiz_game
participant.save()
context = {
'debug': "test",
'participant': participant,
'quiz': quiz,
}
return render(request, 'play/lobby.html', context=context)
def create_participant(request, join_code):
if "participant_id" in request.COOKIES: # Umleiten, falls Teilnehmer bereits erstellt
return redirect('play:lobby', join_code=join_code)
if request.method == 'POST':
display_name = request.POST.get('display_name')
join_code = request.POST.get('join_code')
try:
quiz = QuizGame.objects.get(join_code=join_code)
participant = QuizGameParticipant()
participant.display_name = display_name
participant.quiz_game = quiz
participant.save()
response = HttpResponseRedirect(reverse('play:lobby', kwargs={'join_code': join_code}))
response.set_cookie('participant_id', participant.participant_id, max_age=3600)
return response
except QuizGame.DoesNotExist:
# TODO: Fehlermeldung fuer nicht-existierendes Quiz
pass
return render(request, 'play/initialize_participant.html', {'join_code': join_code})
def join_game(request): def join_game(request):
if request.method == 'POST':
join_code = request.POST.get('game_code')
try:
quiz = QuizGame.objects.get(join_code=join_code)
return redirect('play:lobby', join_code=join_code)
except QuizGame.DoesNotExist:
# TODO: Mit message eine Fehlermeldung weitergeben (und im entsprechenden Template Designen)
pass
return render(request, 'play/join_game.html') return render(request, 'play/join_game.html')

View File

@@ -7,9 +7,9 @@
<h3 class="italic font-extralight sm:text-2xl text-md">Interaktives Lernen neu definiert.</h3> <h3 class="italic font-extralight sm:text-2xl text-md">Interaktives Lernen neu definiert.</h3>
</div> </div>
<div class="grid sm:grid-cols-3 place-items-stretch text-center mt-8 gap-4 p-4 border-3 bg-blue-100 border-blue-100 rounded-md"> <div class="grid sm:grid-cols-3 place-items-stretch text-center mt-8 gap-4 p-4 border-3 bg-blue-100 border-blue-100 rounded-md">
<a class="qp-a-button bg-green-500 text-white" href="#">Teilnehmen</a> <a class="qp-a-button bg-green-500 text-white" href="{% url 'play:join_game' %}">Teilnehmen</a>
<a class="qp-a-button bg-indigo-500 text-white" href="#">Moderieren</a> <a class="qp-a-button bg-indigo-500 text-white" href="#">Moderieren</a>
<a class="qp-a-button bg-purple-500 text-white" href="#">Verwalten</a> <a class="qp-a-button bg-purple-500 text-white" href="{% url 'library:new_quiz' %}">Erstellen</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -1,22 +1,36 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content%} {% block content%}
<div>
<div> <div class="flex justify-center items-center mt-12 ">
<h1>Impressum</h1> <div class="input-group p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md">
<div class="grid place-content-center h-120">
<h1 class="text-center m-4 text-3xl lg:text-6xl">Impressum</b></h1>
Katharineum zu Lübeck <br>
Königsstraße 27-31 <br>
23552 Lübeck
</div> </div>
<div>
<div> <div class="flex justify-end">
<p> <span class="text-[clamp(0.6rem,5vw,1rem)] font-black overflow-x-auto mt-1 ml-0.75 text-blue-600 p-0.75 bg-white">qivip
Katharineum zu Lübeck </span>
</p> </div>
<p>
Königsstraße 27-31
</p>
<p>
23552 Lübeck
</p>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
</body>
</html>
</body> </body>
</html> </html>

View File

@@ -1,13 +1,23 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content%} {% block content%}
<div> <div class="flex justify-center items-center mt-12 ">
<p> <div class="input-group p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md">
Hallo, ich bin die Datenschutzerklärung! <div class="grid place-content-center h-120">
</p>
</div <h1 class="text-center m-4 text-3xl break-words lg:text-6xl">Datenschutz-<br>erklärung</b></h1>
{% endblock %} Hier muss die Datenschutzerklärung stehen! <br>
> <br>
</div>
<div class="flex justify-end">
<span class="text-[clamp(0.6rem,5vw,1rem)] font-black overflow-x-auto mt-1 ml-0.75 text-blue-600 p-0.75 bg-white">qivip
</span>
</div>
</div>
</div>
{% endblock %}
</div> </div>
</body> </body>
</html> </html>

View File

@@ -5,8 +5,8 @@
<div class="flex justify-between items-center mb-6"> <div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold">{{ quiz.name }}</h1> <h1 class="text-2xl font-bold">{{ quiz.name }}</h1>
<div class="flex space-x-2"> <div class="flex space-x-2">
<a href="{% url 'library:edit_quiz' quiz.id %}" class="bg-green-500 text-white px-4 py-2 rounded-md text-sm lg:text-lg hover:bg-green-600 transition-colors">Quiz bearbeiten</a> <a href="{% url 'library:edit_quiz' quiz.id %}" class=" text-white px-4 py-2 rounded-md text-sm lg:text-lg hover:scale-110 transition-colors">&#x270F</a>
<a href="{% url 'library:delete_quiz' quiz.id %}" class="bg-red-500 text-white px-4 py-2 rounded-md text-sm lg:text-lg hover:bg-red-600 transition-colors">Quiz löschen</a> <a href="{% url 'library:delete_quiz' quiz.id %}" class=" text-white px-4 py-2 rounded-md text-sm lg:text-lg hover:scale-110 transition-colors">&#x1F5D1</a>
</div> </div>
</div> </div>
@@ -49,7 +49,7 @@
</div> </div>
<div class="mt-2"> <div class="mt-2">
{% if question.data.type == 'multiple_choice' %} {% if question.data.type == 'multiple_choice' %}
<ul class="space-y-1"> <ul class="space-y-1 ">
{% for option in question.data.options %} {% for option in question.data.options %}
<li class="flex items-center text-sm"> <li class="flex items-center text-sm">
{% if option.is_correct %} {% if option.is_correct %}

View File

@@ -1,9 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<h1>Formular</h1> <div class="input-group border-blue-600 border-2 rounded-xl shadow-md mt-12">
<h2>{{ form.name.value }}</h2>
<div class="input-group border-blue-600 border-2 rounded-xl shadow-md">
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}

View File

@@ -1,16 +1,20 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<h1>Übersicht</h1> <div class="flex justify-end mr-2">
<div class="flex justify-end"> <a href="{% url 'library:new_quiz' %}" class="mt-2 p-2 shadow-md border-blue-600 border-2 rounded-md text-black mb-12">Neues Quiz erstellen</a>
<a href="{% url 'library:new_quiz' %}" class="text-white bg-blue-500 px-4 py-2 rounded-md mb-12">Neues Quiz erstellen</a>
</div> </div>
{% if quizzes %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 px-4 place-items-center">
<h1 class="font-bold mb-4 px-4 bg-blue-100">meine eigenen Quiz</h1></div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 px-4 place-items-center"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 px-4 place-items-center">
{% for quiz in quizzes %} {% for quiz in quizzes %}
<div class="bg-white w-full max-w-md rounded-lg p-4 shadow-md border-blue-600 border-2 rounded-xl"> <div class="bg-white w-full max-w-md rounded-lg p-4 shadow-md border-blue-600 border-2 rounded-xl h-80 ">
<h2 class="text-lg font-bold"><a href="{% url 'library:edit_quiz' quiz.id %}">{{ quiz.name }}</a></h2> <h2 class="text-lg font-bold break-words truncate"><a href="{% url 'library:edit_quiz' quiz.id %}">{{ quiz.name }}</a></h2>
<p class="text-sm text-gray-600 py-12">{{ quiz.description }}</p> <p class="text-sm text-gray-600 pt-11 h-54"><span class="break-words line-clamp-2">{{ quiz.description }} </span><br> Status: <span class="font-bold">{{ quiz.status }}</span>
<br> Schwierigkeit:<span class="font-bold"> {{ quiz.difficulty }}</span>
<br> Anzahl der Fragen:<span class="font-bold"> {{ quiz.question.count }}</span> </p>
<div> <div>
<div class="flex justify-between items-center gap-2"> <div class="flex justify-between items-center gap-2">
@@ -23,6 +27,35 @@
</div> </div>
</div> </div>
</div> </div>
{% endfor %}
{% endif %}
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 px-4 place-items-center">
<h1 class="font-bold px-4 bg-blue-100 mt-8 mb-4">Quiz von anderen Nutzern</h1></div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 px-4 place-items-center">
{% for quiz in all_quizzes %}
<div class="h-80 bg-white w-full max-w-md rounded-lg p-4 shadow-md border-blue-600 border-2 rounded-xl ">
<h2 class="text-lg font-bold break-words truncate">{{ quiz.name }}</h2>
<p class="text-sm text-gray-600 pt-11 h-54"><span class="break-words line-clamp-2">{{ quiz.description }} </span> <br> Schwierigkeit:<span class="font-bold ">
{{ quiz.difficulty }}</span><br> Anzahl der Fragen:<span class="font-bold"> {{ quiz.question.count }}</span>
<br>Erstmalig erstellt am:<span class="font-bold"> {{ quiz.creation_date }}</span>
</p>
<div>
<div class="flex justify-between items-center gap-2">
<a href="#" class="qp-a-button-small border-2 border-blue-600 text-black">Spiel starten</a>
<div class="flex gap-2">
<div class="qp-a-button-small text-gray-600 text-sm "> Quiz von {{ quiz.user_id }}</div>
</div>
</div>
</div>
</div>
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -10,7 +10,7 @@
</a> </a>
</div> </div>
<div class="bg-white rounded-lg shadow-md p-6"> <div class="bg-white rounded-lg shadow-md p-6 border-blue-600 border-2 rounded-xl shadow-md">
<form method="post" class="space-y-6"> <form method="post" class="space-y-6">
{% csrf_token %} {% csrf_token %}

View File

@@ -10,7 +10,7 @@
</a> </a>
</div> </div>
<div class="bg-white rounded-lg shadow-md p-6"> <div class="bg-white rounded-lg shadow-md p-6 border-blue-600 border-2 rounded-xl shadow-md">
<form method="post" class="space-y-6"> <form method="post" class="space-y-6">
{% csrf_token %} {% csrf_token %}

View File

@@ -0,0 +1,5 @@
<!-->
Spieler sehen ihre Punktzahl, Platzierung und Richtig/Falsch
Host: Scoreboard, Button: 'Weiter'
</!-->

View File

@@ -0,0 +1,5 @@
<!-->
Spieler: Countdown, automatische Weiterleitung
(Host: Frage)
</!-->

View File

@@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block content %}
<h1>User for a Game:</h1>
<div class="input-group border-blue-600 border-2 rounded-xl shadow-md">
<form method="post">
{% csrf_token %}
<input type="hidden" name="join_code" value="{{join_code}}">
<input type="text" name="display_name" placeholder="Anzeigename">
<button type="submit">Speichern</button>
</form>
</div>
{% endblock %}

View File

@@ -7,7 +7,7 @@
<form action="{% url 'play:join_game' %}" method="post" class="flex flex-col gap-4"> <form action="{% url 'play:join_game' %}" method="post" class="flex flex-col gap-4">
{% csrf_token %} {% csrf_token %}
<div class="flex gap-3 items-center justify-center"> <div class="flex gap-3 items-center justify-center">
<input type="text" name="game_code" placeholder="XXX-XXX" maxlength="7" pattern="[A-Za-z0-9]{3}-[A-Za-z0-9]{3}" class="w-32 p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black focus:outline-none focus:ring-2 focus:ring-blue-400 focus:bg-blue-100 text-center uppercase tracking-wider" oninput="let v = this.value.replace(/[^A-Za-z0-9]/g, '').slice(0,6).toUpperCase(); this.value = v.length > 3 ? v.slice(0,3) + '-' + v.slice(3) : v;" autofocus> <input type="text" name="game_code" placeholder="123456" maxlength="6" class="w-32 p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black focus:outline-none focus:ring-2 focus:ring-blue-400 focus:bg-blue-100 text-center tracking-wider" autofocus>
<button type="submit" class="h-12 px-6 rounded-full bg-blue-600 text-white font-black hover:scale-105 transition duration-200 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-blue-700 active:scale-95 group" disabled> <button type="submit" class="h-12 px-6 rounded-full bg-blue-600 text-white font-black hover:scale-105 transition duration-200 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-blue-700 active:scale-95 group" disabled>
<span class="hidden sm:block">Beitreten</span> <span class="hidden sm:block">Beitreten</span>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 transition-transform group-hover:translate-x-1"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 transition-transform group-hover:translate-x-1">
@@ -21,7 +21,7 @@
const button = document.querySelector('button[type="submit"]'); const button = document.querySelector('button[type="submit"]');
input.addEventListener('input', function(e) { input.addEventListener('input', function(e) {
button.disabled = !this.value.match(/^[A-Z0-9]{3}-[A-Z0-9]{3}$/); button.disabled = !this.value.match(/^[0-9]{6}$/);
}); });
input.addEventListener('keypress', function(e) { input.addEventListener('keypress', function(e) {

View File

@@ -2,14 +2,20 @@
{% block content %} {% block content %}
<div class="container mx-auto px-4"> <div class="container mx-auto px-4">
<h1>Bundeslaender Deutschland Quiz</h1> <h1>{{ quiz.name }}</h1>
<div class="mt-4 mb-4 text-center">
<h3>Sichtbar als <b>{{ participant.display_name }}</b></h3>
</div>
<div id="player-list"> <div id="player-list">
<ul> <ul>
<li>Spieler 1</li> <li>Noch keine Spieler gefunden.</li>
<li>Spieler 2</li>
<li>Spieler 3</li>
</ul> </ul>
{{debug}}
</div> </div>
<button class="w-full p-3 rounded-full bg-blue-600 text-white font-black hover:scale-105 transition duration-200" type="submit">Starten</button> <button class="w-full p-3 rounded-full bg-blue-600 text-white font-black hover:scale-105 transition duration-200" type="submit">Starten</button>
</div> </div>
<script>
// TODO: Websocket Verbindung aufbauen und Teilnehmerliste bei beitreten updaten
</script>
{% endblock %} {% endblock %}