Compare commits
1 Commits
111-fragen
...
fix_3_url_
| Author | SHA1 | Date | |
|---|---|---|---|
| f7f25b9f0c |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,9 +1,4 @@
|
||||
__pycache__
|
||||
.venv
|
||||
db.sqlite3
|
||||
tailwindcss.exe
|
||||
t-style.css
|
||||
dump.rdb
|
||||
media
|
||||
node_modules
|
||||
staticfiles
|
||||
tailwindcss.exe
|
||||
96
README.md
96
README.md
@@ -6,8 +6,6 @@
|
||||
2. [Starten des Servers](#starten-des-servers)
|
||||
3. [Superuser erstellen](#superuser-erstellen)
|
||||
4. [Tailwind-Nutzung](#tailwind-nutzung)
|
||||
5. [Issue - Merge Request - Merge](#issue-mergerequest-merge)
|
||||
6. [Konfiguration](#konfiguration)
|
||||
|
||||
## Initialisierung des Projekts
|
||||
|
||||
@@ -48,62 +46,16 @@ Um das Projekt erfolgreich zu starten, folge diesen Schritten:
|
||||
|
||||
### Entwicklungsserver starten
|
||||
|
||||
Für die Entwicklung mit WebSocket-Unterstützung verwenden wir Daphne:
|
||||
Um den Server zu starten, verwende einen der folgenden Befehle:
|
||||
|
||||
1. Aktiviere die virtuelle Umgebung:
|
||||
```sh
|
||||
source .venv/bin/activate # Linux
|
||||
.venv\Scripts\activate # Windows
|
||||
```
|
||||
|
||||
2. Starte den Daphne-Server:
|
||||
```sh
|
||||
# Im Verzeichnis 'django'
|
||||
daphne -b 127.0.0.1 -p 8000 core.asgi:application
|
||||
```
|
||||
|
||||
3. Starte den REDIS Server
|
||||
```sh
|
||||
redis-server
|
||||
```
|
||||
> **Hinweis:** Der Redis Server muss, wenn er nicht auf dem selben Gerät unter Standardeinstellungen läuft, in der Datei play/consumers/lobby.py und core/settings.py entsprechend umkonfiguriert werden!
|
||||
|
||||
|
||||
Alternativ kannst du den Django-Entwicklungsserver verwenden, wenn du keine WebSocket-Funktionalität benötigst:
|
||||
```sh
|
||||
python3 core/manage.py runserver
|
||||
```
|
||||
|
||||
### Produktionsserver starten
|
||||
|
||||
Für den Produktivbetrieb verwenden wir Daphne als ASGI-Server, der sowohl HTTP als auch WebSocket-Verbindungen unterstützt:
|
||||
|
||||
1. Aktiviere die virtuelle Umgebung wie oben beschrieben
|
||||
|
||||
2. Sammle die statischen Dateien:
|
||||
```sh
|
||||
python3 core/manage.py collectstatic
|
||||
```
|
||||
|
||||
3. Starte den Daphne-Server:
|
||||
```sh
|
||||
daphne -b 0.0.0.0 -p 8000 core.asgi:application
|
||||
```
|
||||
- `-b 0.0.0.0`: Bindet den Server an alle Netzwerk-Interfaces
|
||||
- `-p 8000`: Port (anpassbar)
|
||||
|
||||
4. Cronjob für Bereinigung der Spiele in der Datenbank
|
||||
- Zum bearbeiten der Cronjobs:
|
||||
```sh
|
||||
crontab -e
|
||||
```
|
||||
- Dann `0 0 * * * /path/to/venv/bin/python /path/to/your_project/manage.py cleanup_games` einfügen, damit um Mitternacht alle Spiele gelöscht werden
|
||||
|
||||
5. Starte den REDIS Server
|
||||
```sh
|
||||
redis-server
|
||||
```
|
||||
> **Hinweis:** Der Redis Server muss, wenn er nicht auf dem selben Gerät unter Standardeinstellungen läuft, in der Datei play/consumers/lobby.py und core/settings.py entsprechend umkonfiguriert werden!
|
||||
- Standardport (8000):
|
||||
```sh
|
||||
python3 core/manage.py runserver
|
||||
```
|
||||
- Bestimmten Port (z. B. 9000):
|
||||
```sh
|
||||
python3 core/manage.py runserver 9000
|
||||
```
|
||||
|
||||
Damit ist das Projekt erfolgreich eingerichtet und der Server kann gestartet werden!
|
||||
|
||||
@@ -131,11 +83,7 @@ npm install tailwindcss @tailwindcss/cli
|
||||
### Datei generieren
|
||||
|
||||
```sh
|
||||
# Bei Verwendung der Binärdatei:
|
||||
tailwindcss -i <INPUT-DATEI> -o <OUTPUT-DATEI> --watch --minify
|
||||
|
||||
# Bei Verwendung von NPM:
|
||||
npx @tailwindcss/cli -i <INPUT-DATEI> -o <OUTPUT-DATEI> --watch --minify
|
||||
```
|
||||
|
||||
- INPUT-DATEI: CSS Datei, welche die Einbindung und Konfiguration von Tailwind enthält
|
||||
@@ -146,27 +94,5 @@ npx @tailwindcss/cli -i <INPUT-DATEI> -o <OUTPUT-DATEI> --watch --minify
|
||||
**Beispiel:**
|
||||
|
||||
```sh
|
||||
# unter Linux mit Tailwind Binärdatei:
|
||||
npx tailwindcss -i static/css/t-input.css -o static/css/t-style.css --watch --minify
|
||||
|
||||
# unter Windows mit NPM (in bash mit '/' statt '\'):
|
||||
npx @tailwindcss/cli -i ./static/css/t-input.css -o ./static/css/t-style.css --watch --minify
|
||||
```
|
||||
|
||||
## Issue - Merge Request - Merge
|
||||
|
||||
Hier der vereinbarte Arbeitsablauf:
|
||||
|
||||
1. Issue erstellen - das geht am einfachsten über die Webseite. Der Titel beschreibt kurz und klar, was angestrebt wird.
|
||||
|
||||
2. Merge Request anlegen - dadurch wird der Branch automatisch angelegt. Dieser kann mit `git pull` geholt und mit `git checkout <branch>` bearbeitet werden.
|
||||
|
||||
3. Nach dem Hochladen kann der Merge in den Master durchgeführt werden.
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Die Konfiguration des Projekts befindet sich im `config/qivip_config.json`.
|
||||
|
||||
| Variable | Bedeutung | Optionen |
|
||||
| --- | --- | --- |
|
||||
| ENABLE_RATING_SYSTEM | Soll das Bewertungssystem aktiviert werden? | true [Standard], false |
|
||||
tailwindcss -i core/homepage/static/homepage/t-input.css -o core/homepage/static/homepage/t-style.css --watch --minify
|
||||
```
|
||||
21
core/accounts/static/accounts/css/base.css
Normal file
21
core/accounts/static/accounts/css/base.css
Normal file
@@ -0,0 +1,21 @@
|
||||
/*color vars*/
|
||||
:root {
|
||||
--main_bg: #1e1e1e;
|
||||
--main_text_color: #d3d3d3;
|
||||
--main_color: #3399cc;
|
||||
--hover_main_color: #3399ccb0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
body {
|
||||
background-color: var(--main_bg);
|
||||
color: var(--main_text_color);
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
128
core/accounts/static/accounts/css/login.css
Normal file
128
core/accounts/static/accounts/css/login.css
Normal file
@@ -0,0 +1,128 @@
|
||||
|
||||
.register_link {
|
||||
color:var(--main_text_color);
|
||||
background-color: #2e2e2e;
|
||||
}
|
||||
|
||||
.register_button {
|
||||
color:var(--main_text_color);
|
||||
background-color: #2e2e2e;
|
||||
border-radius: 5px;
|
||||
border:none;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.login-container {
|
||||
background-color: #2e2e2e;
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
width: 400px;
|
||||
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
|
||||
text-align: center;
|
||||
border: 2px solid var(--main_color);
|
||||
}
|
||||
|
||||
.login-header {
|
||||
color: var(--main_text_color);
|
||||
font-size: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
.input-group {
|
||||
width: 350px;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
|
||||
.input-group input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #555;
|
||||
border-radius: 15px;
|
||||
background-color: #4a4a4a;
|
||||
color: var(--main_text_color);
|
||||
outline: none;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
|
||||
.input-group input:focus {
|
||||
transform: scale(1.06);
|
||||
border-color: var(--main_color);
|
||||
}
|
||||
|
||||
|
||||
.login-button {
|
||||
width: 50%;
|
||||
padding: 13px;
|
||||
background-color: var(--main_color);
|
||||
color: var(--main_text_color);
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
|
||||
.login-button:hover {
|
||||
background-color: var(--hover_main_color);
|
||||
transform: scale(1.06);
|
||||
}
|
||||
|
||||
|
||||
.login-logo {
|
||||
width: 400px;
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.login-logo-mobile {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
|
||||
.login-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* MOBILE DEVICES */
|
||||
@media (max-width: 768px) {
|
||||
.login-container {
|
||||
width: 250px;
|
||||
padding: 10px;
|
||||
}
|
||||
.input-group {
|
||||
width: 200px;
|
||||
}
|
||||
.login-logo {
|
||||
display:none;
|
||||
}
|
||||
.login-button {
|
||||
height: auto;
|
||||
}
|
||||
.login-form {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* DESKTOP DEVICES*/
|
||||
@media (min-width: 768px) {
|
||||
.login-logo-mobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
16
core/accounts/templates/accounts/base.html
Normal file
16
core/accounts/templates/accounts/base.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{% static 'homepage/t-style.css' %}">
|
||||
</head>
|
||||
<body>
|
||||
{% include 'homepage/partials/_nav.html' %}
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
{% include 'homepage/partials/_footer.html' %}
|
||||
</body>
|
||||
</html>
|
||||
11
core/accounts/templates/accounts/home.html
Normal file
11
core/accounts/templates/accounts/home.html
Normal file
@@ -0,0 +1,11 @@
|
||||
{% extends 'accounts/base.html'%}
|
||||
{% block title %}Home{% endblock %}
|
||||
{% block content %}
|
||||
<div class="grid place-content-center h-120">
|
||||
<h1 class="text-center m-4 text-6xl">Moin moin!</h1>
|
||||
<form action="{% url 'accounts:logout' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="bg-red-600 text-white p-3 rounded-full font-black" type="submit">Abmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,13 +1,13 @@
|
||||
{% extends 'base.html' %}
|
||||
{% extends 'accounts/base.html' %}
|
||||
{% load static %}
|
||||
{% block title %}Anmeldung{% endblock %}
|
||||
{% block content %}
|
||||
<div class="grid place-content-center h-120 mt-12 mb-12 ">
|
||||
<div class="w-80 p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md lg:w-90">
|
||||
<div class="grid place-content-center h-120">
|
||||
<div class="p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md">
|
||||
<h2 class="text-2xl text-center p-4 font-black">Anmeldung</h2>
|
||||
<form class="space-y-4" method="post">
|
||||
{% if form.errors %}
|
||||
<div class=" items-center text-red-600 font-black overflow-x-auto break-words text-center">
|
||||
<div class="text-red-600 font-black overflow-x-auto break-words text-center">
|
||||
<ul>
|
||||
{% for field, errors in form.errors.items %}
|
||||
{% for error in errors %}
|
||||
@@ -18,11 +18,11 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% csrf_token %}
|
||||
<div class="items-center w-full">
|
||||
<input class=" w-full p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black" type="text" name="username" id="username" required placeholder="BENUTZERNAME">
|
||||
<div class="">
|
||||
<input class="p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black" type="text" name="username" id="username" required placeholder="BENUTZERNAME">
|
||||
</div>
|
||||
<div class="items-center w-full">
|
||||
<input class=" w-full p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black" type="password" name="password" id="password" required placeholder="PASSWORT">
|
||||
<div class="">
|
||||
<input class="p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black" type="password" name="password" id="password" required placeholder="PASSWORT">
|
||||
</div>
|
||||
<button class="w-full p-3 rounded-full bg-blue-600 text-white font-black hover:scale-105 transition duration-200" type="submit" class="login-button">Anmelden</button>
|
||||
</form>
|
||||
@@ -1,9 +1,8 @@
|
||||
{% extends 'base.html' %}
|
||||
{% extends 'accounts/base.html' %}
|
||||
{% block title %}Registrierung{% endblock %}
|
||||
{% block content %}
|
||||
<div class="grid place-content-center h-120 mt-12 mb-12">
|
||||
|
||||
<div class="w-80 p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md lg:w-90">
|
||||
<div class="grid place-content-center h-120">
|
||||
<div class=" p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md">
|
||||
<h2 class="text-2xl text-center p-4 font-black">Registrieren</h2>
|
||||
|
||||
<form class=" space-y-4" method="post">
|
||||
12
core/accounts/templates/core/partials/_nav.html
Normal file
12
core/accounts/templates/core/partials/_nav.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<nav class="flex justify-between bg-blue-600 h-12 px-4 sm:px-6 lg:px-8 rounded-lg m-2 shadow-md">
|
||||
<div class="flex items-center">
|
||||
<h2 class="text-xl font-bold text-white hover:scale-110 transition duration-200"><a href="#">qivip</a></h2>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<ul class="flex space-x-4 qp-nav-list">
|
||||
<li><a href="#">Start</a></li>
|
||||
<li><a href="#">Bibliothek</a></li>
|
||||
<li><a href="{% url 'login' %}">Konto</a></li> <!--Hier ist der Link erstmal auf das alte Login-Formular gesetzt! Zum Testen!-->
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -1,6 +1,5 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth import login, authenticate
|
||||
from .forms import RegisterForm
|
||||
|
||||
# Create your views here.
|
||||
@@ -12,9 +11,8 @@ def register(response):
|
||||
if response.method == "POST":
|
||||
form = RegisterForm(response.POST)
|
||||
if form.is_valid():
|
||||
user=form.save()
|
||||
login(response, user) #automatischer Login nach Registrierung
|
||||
return redirect("accounts:home")
|
||||
form.save()
|
||||
return redirect("home")
|
||||
else:
|
||||
form = RegisterForm()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{% load static %}
|
||||
{% block title %}Antwort{% endblock %}
|
||||
{% block content %}
|
||||
<link rel="stylesheet" href="{% static 'css/t-style.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'homepage/t-style.css' %}">
|
||||
|
||||
<!-- TODO Bedingung festlegen: Quiztyp unterscheiden % if <Bedingung> % -->
|
||||
{% if user.is_authenticated %}
|
||||
@@ -1,5 +1,5 @@
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'css/t-style.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'homepage/t-style.css' %}">
|
||||
<!doctype html>
|
||||
<html class="h-full">
|
||||
<head>
|
||||
16
core/core/asgi.py
Normal file
16
core/core/asgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for core project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
@@ -10,7 +10,6 @@ For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||
"""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
@@ -28,9 +27,6 @@ DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*']
|
||||
|
||||
# Konfigurationsdatei für Projekt
|
||||
with open(BASE_DIR / 'config' / 'qivip_config.json') as config_file:
|
||||
QIVIP_CONFIG = json.load(config_file)
|
||||
|
||||
# Application definition
|
||||
|
||||
@@ -39,20 +35,16 @@ INSTALLED_APPS = [
|
||||
'homepage.apps.HomepageConfig',
|
||||
'components.apps.ComponentsConfig',
|
||||
'accounts.apps.AccountsConfig',
|
||||
'play',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'channels',
|
||||
'django_cleanup',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
@@ -66,7 +58,7 @@ ROOT_URLCONF = 'core.urls'
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
@@ -80,17 +72,6 @@ TEMPLATES = [
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'core.wsgi.application'
|
||||
ASGI_APPLICATION = 'core.asgi.application'
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "channels_redis.core.RedisChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [("127.0.0.1", 6379)],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Database
|
||||
@@ -103,15 +84,6 @@ DATABASES = {
|
||||
}
|
||||
}
|
||||
|
||||
STORAGES = {
|
||||
"staticfiles": {
|
||||
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
|
||||
},
|
||||
"default": {
|
||||
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
||||
@@ -148,22 +120,11 @@ USE_TZ = True
|
||||
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = BASE_DIR / "staticfiles"
|
||||
STATICFILES_DIRS = [BASE_DIR / 'static']
|
||||
|
||||
# WhiteNoise configuration
|
||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage'
|
||||
WHITENOISE_SKIP_COMPRESS_EXTENSIONS = ['css', 'js']
|
||||
# Return 404 instead of 500 for missing files
|
||||
WHITENOISE_MISSING_FILE_ERRNO = None
|
||||
BASE_DIR='static' #von ben8 hinzugefügt
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
LOGIN_URL = '/account/login/'
|
||||
import os
|
||||
|
||||
MEDIA_URL = '/media/' # URL, um auf Medien-Dateien zuzugreifen
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Speicherort für hochgeladene Dateien
|
||||
LOGIN_URL = '/account/login/'
|
||||
@@ -14,19 +14,14 @@ Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf.urls.static import static
|
||||
from django.conf import settings
|
||||
from django.contrib import admin # type: ignore
|
||||
from django.urls import path, include
|
||||
from django.contrib.auth import login
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('account/', include('accounts.urls')),
|
||||
path('', include('homepage.urls')),
|
||||
path('play/', include('play.urls')),
|
||||
path('library/', include('library.urls')),
|
||||
path('components/', include('components.urls'))
|
||||
path('play/', include('components.urls')),
|
||||
path('library/', include('library.urls'),),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
46
core/homepage/static/homepage/t-input.css
Normal file
46
core/homepage/static/homepage/t-input.css
Normal file
@@ -0,0 +1,46 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
nav div ul.qp-nav-list li {
|
||||
@apply text-white hover:scale-110 hover:border-b-2 hover:border-white transition duration-200;
|
||||
}
|
||||
|
||||
a.qp-a-button {
|
||||
@apply p-6 rounded-md font-extrabold hover:scale-105 transition duration-300 shadow-md;
|
||||
}
|
||||
.register input{@apply p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black;}
|
||||
|
||||
.input-group {
|
||||
@apply flex flex-col gap-4 max-w-md mx-auto p-6 bg-white rounded-xl shadow-lg;
|
||||
}
|
||||
|
||||
.input-group form {
|
||||
@apply flex flex-col gap-4;
|
||||
}
|
||||
|
||||
.input-group form p {
|
||||
@apply flex flex-col gap-2;
|
||||
}
|
||||
|
||||
.input-group form p label {
|
||||
@apply text-gray-900 font-semibold text-sm;
|
||||
}
|
||||
|
||||
.input-group form p input {
|
||||
@apply 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;
|
||||
}
|
||||
|
||||
.input-group form p select {
|
||||
@apply p-3 rounded-md 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;
|
||||
}
|
||||
|
||||
.input-group form p textarea {
|
||||
@apply p-3 rounded-xl 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 min-h-[100px];
|
||||
}
|
||||
|
||||
.input-group form p button {
|
||||
@apply p-3 rounded-full bg-indigo-500 hover:bg-indigo-600 text-white focus:scale-105
|
||||
transition duration-200 font-black shadow-md hover:shadow-lg;
|
||||
}
|
||||
997
core/homepage/static/homepage/t-style.css
Normal file
997
core/homepage/static/homepage/t-style.css
Normal file
@@ -0,0 +1,997 @@
|
||||
/*! tailwindcss v4.0.9 | MIT License | https://tailwindcss.com */
|
||||
@layer theme, base, components, utilities;
|
||||
@layer theme {
|
||||
:root, :host {
|
||||
--font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||
monospace;
|
||||
--color-red-500: oklch(0.637 0.237 25.331);
|
||||
--color-red-600: oklch(0.577 0.245 27.325);
|
||||
--color-yellow-500: oklch(0.795 0.184 86.047);
|
||||
--color-green-500: oklch(0.723 0.219 149.579);
|
||||
--color-blue-100: oklch(0.932 0.032 255.585);
|
||||
--color-blue-400: oklch(0.707 0.165 254.624);
|
||||
--color-blue-500: oklch(0.623 0.214 259.815);
|
||||
--color-blue-600: oklch(0.546 0.245 262.881);
|
||||
--color-indigo-500: oklch(0.585 0.233 277.117);
|
||||
--color-indigo-600: oklch(0.511 0.262 276.966);
|
||||
--color-purple-500: oklch(0.627 0.265 303.9);
|
||||
--color-gray-200: oklch(0.928 0.006 264.531);
|
||||
--color-gray-500: oklch(0.551 0.027 264.364);
|
||||
--color-gray-600: oklch(0.446 0.03 256.802);
|
||||
--color-gray-900: oklch(0.21 0.034 264.665);
|
||||
--color-stone-900: oklch(0.216 0.006 56.043);
|
||||
--color-white: #fff;
|
||||
--spacing: 0.25rem;
|
||||
--breakpoint-lg: 64rem;
|
||||
--container-md: 28rem;
|
||||
--text-sm: 0.875rem;
|
||||
--text-sm--line-height: calc(1.25 / 0.875);
|
||||
--text-lg: 1.125rem;
|
||||
--text-lg--line-height: calc(1.75 / 1.125);
|
||||
--text-xl: 1.25rem;
|
||||
--text-xl--line-height: calc(1.75 / 1.25);
|
||||
--text-2xl: 1.5rem;
|
||||
--text-2xl--line-height: calc(2 / 1.5);
|
||||
--text-6xl: 3.75rem;
|
||||
--text-6xl--line-height: 1;
|
||||
--text-8xl: 6rem;
|
||||
--text-8xl--line-height: 1;
|
||||
--font-weight-extralight: 200;
|
||||
--font-weight-light: 300;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
--font-weight-extrabold: 800;
|
||||
--font-weight-black: 900;
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--radius-xl: 0.75rem;
|
||||
--default-transition-duration: 150ms;
|
||||
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--default-font-family: var(--font-sans);
|
||||
--default-font-feature-settings: var(--font-sans--font-feature-settings);
|
||||
--default-font-variation-settings: var(--font-sans--font-variation-settings);
|
||||
--default-mono-font-family: var(--font-mono);
|
||||
--default-mono-font-feature-settings: var(--font-mono--font-feature-settings);
|
||||
--default-mono-font-variation-settings: var(--font-mono--font-variation-settings);
|
||||
}
|
||||
}
|
||||
@layer base {
|
||||
*, ::after, ::before, ::backdrop, ::file-selector-button {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0 solid;
|
||||
}
|
||||
html, :host {
|
||||
line-height: 1.5;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
tab-size: 4;
|
||||
font-family: var( --default-font-family, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji' );
|
||||
font-feature-settings: var(--default-font-feature-settings, normal);
|
||||
font-variation-settings: var(--default-font-variation-settings, normal);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
body {
|
||||
line-height: inherit;
|
||||
}
|
||||
hr {
|
||||
height: 0;
|
||||
color: inherit;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
abbr:where([title]) {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
-webkit-text-decoration: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
b, strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
code, kbd, samp, pre {
|
||||
font-family: var( --default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace );
|
||||
font-feature-settings: var(--default-mono-font-feature-settings, normal);
|
||||
font-variation-settings: var(--default-mono-font-variation-settings, normal);
|
||||
font-size: 1em;
|
||||
}
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
sub, sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
table {
|
||||
text-indent: 0;
|
||||
border-color: inherit;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
ol, ul, menu {
|
||||
list-style: none;
|
||||
}
|
||||
img, svg, video, canvas, audio, iframe, embed, object {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
img, video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
button, input, select, optgroup, textarea, ::file-selector-button {
|
||||
font: inherit;
|
||||
font-feature-settings: inherit;
|
||||
font-variation-settings: inherit;
|
||||
letter-spacing: inherit;
|
||||
color: inherit;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
opacity: 1;
|
||||
}
|
||||
:where(select:is([multiple], [size])) optgroup {
|
||||
font-weight: bolder;
|
||||
}
|
||||
:where(select:is([multiple], [size])) optgroup option {
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
::file-selector-button {
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
::placeholder {
|
||||
opacity: 1;
|
||||
color: color-mix(in oklab, currentColor 50%, transparent);
|
||||
}
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
::-webkit-date-and-time-value {
|
||||
min-height: 1lh;
|
||||
text-align: inherit;
|
||||
}
|
||||
::-webkit-datetime-edit {
|
||||
display: inline-flex;
|
||||
}
|
||||
::-webkit-datetime-edit-fields-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
|
||||
padding-block: 0;
|
||||
}
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
button, input:where([type='button'], [type='reset'], [type='submit']), ::file-selector-button {
|
||||
appearance: button;
|
||||
}
|
||||
::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
[hidden]:where(:not([hidden='until-found'])) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
@layer utilities {
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
.static {
|
||||
position: static;
|
||||
}
|
||||
.top-0 {
|
||||
top: calc(var(--spacing) * 0);
|
||||
}
|
||||
.right-0 {
|
||||
right: calc(var(--spacing) * 0);
|
||||
}
|
||||
.bottom-0 {
|
||||
bottom: calc(var(--spacing) * 0);
|
||||
}
|
||||
.-z-1 {
|
||||
z-index: calc(1 * -1);
|
||||
}
|
||||
.-z-50 {
|
||||
z-index: calc(50 * -1);
|
||||
}
|
||||
.z-40 {
|
||||
z-index: 40;
|
||||
}
|
||||
.m-0 {
|
||||
margin: calc(var(--spacing) * 0);
|
||||
}
|
||||
.m-0\.75 {
|
||||
margin: calc(var(--spacing) * 0.75);
|
||||
}
|
||||
.m-2 {
|
||||
margin: calc(var(--spacing) * 2);
|
||||
}
|
||||
.m-4 {
|
||||
margin: calc(var(--spacing) * 4);
|
||||
}
|
||||
.mx-auto {
|
||||
margin-inline: auto;
|
||||
}
|
||||
.mt-1 {
|
||||
margin-top: calc(var(--spacing) * 1);
|
||||
}
|
||||
.mt-2 {
|
||||
margin-top: calc(var(--spacing) * 2);
|
||||
}
|
||||
.mt-3 {
|
||||
margin-top: calc(var(--spacing) * 3);
|
||||
}
|
||||
.mt-4 {
|
||||
margin-top: calc(var(--spacing) * 4);
|
||||
}
|
||||
.mt-8 {
|
||||
margin-top: calc(var(--spacing) * 8);
|
||||
}
|
||||
.mr-0\.75 {
|
||||
margin-right: calc(var(--spacing) * 0.75);
|
||||
}
|
||||
.mb-6 {
|
||||
margin-bottom: calc(var(--spacing) * 6);
|
||||
}
|
||||
.ml-0\.75 {
|
||||
margin-left: calc(var(--spacing) * 0.75);
|
||||
}
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.h-12 {
|
||||
height: calc(var(--spacing) * 12);
|
||||
}
|
||||
.h-20 {
|
||||
height: calc(var(--spacing) * 20);
|
||||
}
|
||||
.h-120 {
|
||||
height: calc(var(--spacing) * 120);
|
||||
}
|
||||
.h-150 {
|
||||
height: calc(var(--spacing) * 150);
|
||||
}
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
.max-w-screen-lg {
|
||||
max-width: var(--breakpoint-lg);
|
||||
}
|
||||
.grid-cols-1 {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
.grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
.place-content-center {
|
||||
place-content: center;
|
||||
}
|
||||
.place-items-stretch {
|
||||
place-items: stretch;
|
||||
}
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.justify-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.gap-2 {
|
||||
gap: calc(var(--spacing) * 2);
|
||||
}
|
||||
.gap-4 {
|
||||
gap: calc(var(--spacing) * 4);
|
||||
}
|
||||
.space-y-4 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.space-x-2 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-inline-start: calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));
|
||||
margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
}
|
||||
.space-x-4 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-inline-start: calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));
|
||||
margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
}
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
.overflow-x-auto {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.rounded-full {
|
||||
border-radius: calc(infinity * 1px);
|
||||
}
|
||||
.rounded-lg {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
.rounded-md {
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
.rounded-sm {
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
.rounded-xl {
|
||||
border-radius: var(--radius-xl);
|
||||
}
|
||||
.border-2 {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 2px;
|
||||
}
|
||||
.border-3 {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 3px;
|
||||
}
|
||||
.border-4 {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 4px;
|
||||
}
|
||||
.border-blue-100 {
|
||||
border-color: var(--color-blue-100);
|
||||
}
|
||||
.border-blue-600 {
|
||||
border-color: var(--color-blue-600);
|
||||
}
|
||||
.border-gray-500 {
|
||||
border-color: var(--color-gray-500);
|
||||
}
|
||||
.bg-blue-100 {
|
||||
background-color: var(--color-blue-100);
|
||||
}
|
||||
.bg-blue-400 {
|
||||
background-color: var(--color-blue-400);
|
||||
}
|
||||
.bg-blue-500 {
|
||||
background-color: var(--color-blue-500);
|
||||
}
|
||||
.bg-blue-600 {
|
||||
background-color: var(--color-blue-600);
|
||||
}
|
||||
.bg-gray-200 {
|
||||
background-color: var(--color-gray-200);
|
||||
}
|
||||
.bg-gray-500 {
|
||||
background-color: var(--color-gray-500);
|
||||
}
|
||||
.bg-green-500 {
|
||||
background-color: var(--color-green-500);
|
||||
}
|
||||
.bg-indigo-500 {
|
||||
background-color: var(--color-indigo-500);
|
||||
}
|
||||
.bg-purple-500 {
|
||||
background-color: var(--color-purple-500);
|
||||
}
|
||||
.bg-red-500 {
|
||||
background-color: var(--color-red-500);
|
||||
}
|
||||
.bg-red-600 {
|
||||
background-color: var(--color-red-600);
|
||||
}
|
||||
.bg-white {
|
||||
background-color: var(--color-white);
|
||||
}
|
||||
.bg-yellow-500 {
|
||||
background-color: var(--color-yellow-500);
|
||||
}
|
||||
.object-cover {
|
||||
object-fit: cover;
|
||||
}
|
||||
.p-0\.75 {
|
||||
padding: calc(var(--spacing) * 0.75);
|
||||
}
|
||||
.p-2 {
|
||||
padding: calc(var(--spacing) * 2);
|
||||
}
|
||||
.p-3 {
|
||||
padding: calc(var(--spacing) * 3);
|
||||
}
|
||||
.p-4 {
|
||||
padding: calc(var(--spacing) * 4);
|
||||
}
|
||||
.px-1 {
|
||||
padding-inline: calc(var(--spacing) * 1);
|
||||
}
|
||||
.px-4 {
|
||||
padding-inline: calc(var(--spacing) * 4);
|
||||
}
|
||||
.py-2 {
|
||||
padding-block: calc(var(--spacing) * 2);
|
||||
}
|
||||
.pb-4 {
|
||||
padding-bottom: calc(var(--spacing) * 4);
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.text-2xl {
|
||||
font-size: var(--text-2xl);
|
||||
line-height: var(--tw-leading, var(--text-2xl--line-height));
|
||||
}
|
||||
.text-6xl {
|
||||
font-size: var(--text-6xl);
|
||||
line-height: var(--tw-leading, var(--text-6xl--line-height));
|
||||
}
|
||||
.text-lg {
|
||||
font-size: var(--text-lg);
|
||||
line-height: var(--tw-leading, var(--text-lg--line-height));
|
||||
}
|
||||
.text-sm {
|
||||
font-size: var(--text-sm);
|
||||
line-height: var(--tw-leading, var(--text-sm--line-height));
|
||||
}
|
||||
.text-xl {
|
||||
font-size: var(--text-xl);
|
||||
line-height: var(--tw-leading, var(--text-xl--line-height));
|
||||
}
|
||||
.text-\[clamp\(0\.3rem\,5vw\,1rem\)\] {
|
||||
font-size: clamp(0.3rem, 5vw, 1rem);
|
||||
}
|
||||
.text-\[clamp\(0\.6rem\,5vw\,1rem\)\] {
|
||||
font-size: clamp(0.6rem, 5vw, 1rem);
|
||||
}
|
||||
.text-\[clamp\(0\.55rem\,5vw\,1\.5rem\)\] {
|
||||
font-size: clamp(0.55rem, 5vw, 1.5rem);
|
||||
}
|
||||
.text-\[clamp\(0\.75rem\,5vw\,1\.5rem\)\] {
|
||||
font-size: clamp(0.75rem, 5vw, 1.5rem);
|
||||
}
|
||||
.text-\[clamp\(0\.75rem\,5vw\,1\.25rem\)\] {
|
||||
font-size: clamp(0.75rem, 5vw, 1.25rem);
|
||||
}
|
||||
.font-black {
|
||||
--tw-font-weight: var(--font-weight-black);
|
||||
font-weight: var(--font-weight-black);
|
||||
}
|
||||
.font-bold {
|
||||
--tw-font-weight: var(--font-weight-bold);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
.font-extralight {
|
||||
--tw-font-weight: var(--font-weight-extralight);
|
||||
font-weight: var(--font-weight-extralight);
|
||||
}
|
||||
.font-light {
|
||||
--tw-font-weight: var(--font-weight-light);
|
||||
font-weight: var(--font-weight-light);
|
||||
}
|
||||
.font-semibold {
|
||||
--tw-font-weight: var(--font-weight-semibold);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
.break-words {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
.text-blue-600 {
|
||||
color: var(--color-blue-600);
|
||||
}
|
||||
.text-gray-600 {
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
.text-red-600 {
|
||||
color: var(--color-red-600);
|
||||
}
|
||||
.text-stone-900 {
|
||||
color: var(--color-stone-900);
|
||||
}
|
||||
.text-white {
|
||||
color: var(--color-white);
|
||||
}
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
.shadow-\[0_2px_2px_rgba\(0\,0\,0\,0\.1\)\] {
|
||||
--tw-shadow: 0 2px 2px var(--tw-shadow-color, rgba(0,0,0,0.1));
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
.shadow-md {
|
||||
--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
.filter {
|
||||
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
||||
}
|
||||
.transition {
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
}
|
||||
.duration-200 {
|
||||
--tw-duration: 200ms;
|
||||
transition-duration: 200ms;
|
||||
}
|
||||
.hover\:scale-105 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
--tw-scale-x: 105%;
|
||||
--tw-scale-y: 105%;
|
||||
--tw-scale-z: 105%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:scale-110 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
--tw-scale-x: 110%;
|
||||
--tw-scale-y: 110%;
|
||||
--tw-scale-z: 110%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
}
|
||||
}
|
||||
.focus\:scale-105 {
|
||||
&:focus {
|
||||
--tw-scale-x: 105%;
|
||||
--tw-scale-y: 105%;
|
||||
--tw-scale-z: 105%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
}
|
||||
.sm\:grid-cols-3 {
|
||||
@media (width >= 40rem) {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
.sm\:px-6 {
|
||||
@media (width >= 40rem) {
|
||||
padding-inline: calc(var(--spacing) * 6);
|
||||
}
|
||||
}
|
||||
.sm\:text-2xl {
|
||||
@media (width >= 40rem) {
|
||||
font-size: var(--text-2xl);
|
||||
line-height: var(--tw-leading, var(--text-2xl--line-height));
|
||||
}
|
||||
}
|
||||
.md\:mb-8 {
|
||||
@media (width >= 48rem) {
|
||||
margin-bottom: calc(var(--spacing) * 8);
|
||||
}
|
||||
}
|
||||
.md\:h-screen {
|
||||
@media (width >= 48rem) {
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
.md\:grid-cols-2 {
|
||||
@media (width >= 48rem) {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
.md\:text-8xl {
|
||||
@media (width >= 48rem) {
|
||||
font-size: var(--text-8xl);
|
||||
line-height: var(--tw-leading, var(--text-8xl--line-height));
|
||||
}
|
||||
}
|
||||
.lg\:grid-cols-3 {
|
||||
@media (width >= 64rem) {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
.lg\:px-8 {
|
||||
@media (width >= 64rem) {
|
||||
padding-inline: calc(var(--spacing) * 8);
|
||||
}
|
||||
}
|
||||
.dark\:bg-gray-900 {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--color-gray-900);
|
||||
}
|
||||
}
|
||||
}
|
||||
nav div ul.qp-nav-list li {
|
||||
color: var(--color-white);
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
--tw-duration: 200ms;
|
||||
transition-duration: 200ms;
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
--tw-scale-x: 110%;
|
||||
--tw-scale-y: 110%;
|
||||
--tw-scale-z: 110%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
border-bottom-style: var(--tw-border-style);
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
border-color: var(--color-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
a.qp-a-button {
|
||||
border-radius: var(--radius-md);
|
||||
padding: calc(var(--spacing) * 6);
|
||||
--tw-font-weight: var(--font-weight-extrabold);
|
||||
font-weight: var(--font-weight-extrabold);
|
||||
--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
--tw-duration: 300ms;
|
||||
transition-duration: 300ms;
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
--tw-scale-x: 105%;
|
||||
--tw-scale-y: 105%;
|
||||
--tw-scale-z: 105%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
}
|
||||
}
|
||||
.register input {
|
||||
border-radius: calc(infinity * 1px);
|
||||
background-color: var(--color-gray-200);
|
||||
padding: calc(var(--spacing) * 3);
|
||||
--tw-font-weight: var(--font-weight-black);
|
||||
font-weight: var(--font-weight-black);
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
--tw-duration: 200ms;
|
||||
transition-duration: 200ms;
|
||||
&:focus {
|
||||
--tw-scale-x: 105%;
|
||||
--tw-scale-y: 105%;
|
||||
--tw-scale-z: 105%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
}
|
||||
.input-group {
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
max-width: var(--container-md);
|
||||
flex-direction: column;
|
||||
gap: calc(var(--spacing) * 4);
|
||||
border-radius: var(--radius-xl);
|
||||
background-color: var(--color-white);
|
||||
padding: calc(var(--spacing) * 6);
|
||||
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
.input-group form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--spacing) * 4);
|
||||
}
|
||||
.input-group form p {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--spacing) * 2);
|
||||
}
|
||||
.input-group form p label {
|
||||
font-size: var(--text-sm);
|
||||
line-height: var(--tw-leading, var(--text-sm--line-height));
|
||||
--tw-font-weight: var(--font-weight-semibold);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
.input-group form p input {
|
||||
border-radius: calc(infinity * 1px);
|
||||
background-color: var(--color-gray-200);
|
||||
padding: calc(var(--spacing) * 3);
|
||||
--tw-font-weight: var(--font-weight-black);
|
||||
font-weight: var(--font-weight-black);
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
--tw-duration: 200ms;
|
||||
transition-duration: 200ms;
|
||||
&:focus {
|
||||
--tw-scale-x: 105%;
|
||||
--tw-scale-y: 105%;
|
||||
--tw-scale-z: 105%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
&:focus {
|
||||
background-color: var(--color-blue-100);
|
||||
}
|
||||
&:focus {
|
||||
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor);
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
&:focus {
|
||||
--tw-ring-color: var(--color-blue-400);
|
||||
}
|
||||
&:focus {
|
||||
--tw-outline-style: none;
|
||||
outline-style: none;
|
||||
}
|
||||
}
|
||||
.input-group form p select {
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--color-gray-200);
|
||||
padding: calc(var(--spacing) * 3);
|
||||
--tw-font-weight: var(--font-weight-black);
|
||||
font-weight: var(--font-weight-black);
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
--tw-duration: 200ms;
|
||||
transition-duration: 200ms;
|
||||
&:focus {
|
||||
--tw-scale-x: 105%;
|
||||
--tw-scale-y: 105%;
|
||||
--tw-scale-z: 105%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
&:focus {
|
||||
background-color: var(--color-blue-100);
|
||||
}
|
||||
&:focus {
|
||||
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor);
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
&:focus {
|
||||
--tw-ring-color: var(--color-blue-400);
|
||||
}
|
||||
&:focus {
|
||||
--tw-outline-style: none;
|
||||
outline-style: none;
|
||||
}
|
||||
}
|
||||
.input-group form p textarea {
|
||||
min-height: 100px;
|
||||
border-radius: var(--radius-xl);
|
||||
background-color: var(--color-gray-200);
|
||||
padding: calc(var(--spacing) * 3);
|
||||
--tw-font-weight: var(--font-weight-black);
|
||||
font-weight: var(--font-weight-black);
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
--tw-duration: 200ms;
|
||||
transition-duration: 200ms;
|
||||
&:focus {
|
||||
--tw-scale-x: 105%;
|
||||
--tw-scale-y: 105%;
|
||||
--tw-scale-z: 105%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
&:focus {
|
||||
background-color: var(--color-blue-100);
|
||||
}
|
||||
&:focus {
|
||||
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor);
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
&:focus {
|
||||
--tw-ring-color: var(--color-blue-400);
|
||||
}
|
||||
&:focus {
|
||||
--tw-outline-style: none;
|
||||
outline-style: none;
|
||||
}
|
||||
}
|
||||
.input-group form p button {
|
||||
border-radius: calc(infinity * 1px);
|
||||
background-color: var(--color-indigo-500);
|
||||
padding: calc(var(--spacing) * 3);
|
||||
--tw-font-weight: var(--font-weight-black);
|
||||
font-weight: var(--font-weight-black);
|
||||
color: var(--color-white);
|
||||
--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
--tw-duration: 200ms;
|
||||
transition-duration: 200ms;
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--color-indigo-600);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
}
|
||||
&:focus {
|
||||
--tw-scale-x: 105%;
|
||||
--tw-scale-y: 105%;
|
||||
--tw-scale-z: 105%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
}
|
||||
@property --tw-space-y-reverse {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-space-x-reverse {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-border-style {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: solid;
|
||||
}
|
||||
@property --tw-font-weight {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@property --tw-shadow-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-inset-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@property --tw-inset-shadow-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-ring-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-ring-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@property --tw-inset-ring-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-inset-ring-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@property --tw-ring-inset {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-ring-offset-width {
|
||||
syntax: "<length>";
|
||||
inherits: false;
|
||||
initial-value: 0px;
|
||||
}
|
||||
@property --tw-ring-offset-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: #fff;
|
||||
}
|
||||
@property --tw-ring-offset-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@property --tw-blur {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-brightness {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-contrast {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-grayscale {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-hue-rotate {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-invert {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-opacity {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-saturate {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-sepia {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-drop-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-duration {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-scale-x {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 1;
|
||||
}
|
||||
@property --tw-scale-y {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 1;
|
||||
}
|
||||
@property --tw-scale-z {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 1;
|
||||
}
|
||||
15
core/homepage/templates/homepage/base.html
Normal file
15
core/homepage/templates/homepage/base.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="{% static 'homepage/t-style.css' %}">
|
||||
<title>qivip</title>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'homepage/partials/_nav.html' %}
|
||||
{% block content%}{% endblock %}
|
||||
{% include 'homepage/partials/_footer.html' %}
|
||||
</body>
|
||||
</html>
|
||||
15
core/homepage/templates/homepage/home.html
Normal file
15
core/homepage/templates/homepage/home.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{% extends 'homepage/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="grid place-content-center md:h-screen h-150 w-full -z-50 top-0 p-4">
|
||||
<div class="text-center">
|
||||
<h2 class="font-bold md:text-8xl text-6xl md:mb-8 text-blue-600">qivip</h2>
|
||||
<h3 class="italic font-extralight sm:text-2xl text-md">Interaktives Lernen neu definiert.</h3>
|
||||
</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">
|
||||
<a class="qp-a-button bg-green-500 text-white" href="#">Teilnehmen</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>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
7
core/homepage/templates/homepage/partials/_footer.html
Normal file
7
core/homepage/templates/homepage/partials/_footer.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<div class="grid place-content-center gap-2 w-full mt-4">
|
||||
<div class="flex space-x-2 pb-4">
|
||||
<p class="text-sm font-extralight"><a href="#">Impressum</a></p>
|
||||
<p class="text-sm font-extralight"><a href="#">Datenschutz</a></p>
|
||||
<p class="text-sm font-extralight"><a href="#">Repository</a></p>
|
||||
</div>
|
||||
</div>
|
||||
15
core/homepage/templates/homepage/partials/_nav.html
Normal file
15
core/homepage/templates/homepage/partials/_nav.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<nav class="flex justify-between bg-blue-600 h-12 px-4 sm:px-6 lg:px-8 rounded-lg m-2 shadow-md">
|
||||
<div class="flex items-center">
|
||||
<h2 class="text-xl font-bold text-white hover:scale-110 transition duration-200"><a href="{% url 'homepage:home' %}">qivip</a></h2>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<ul class="flex space-x-4 qp-nav-list">
|
||||
<li><a href="{% url 'library:overview_quiz' %}">Bibliothek</a></li>
|
||||
{% if user.is_authenticated %}
|
||||
<li><a href="{% url 'accounts:home' %}">Konto</a></li>
|
||||
{% else %}
|
||||
<li><a href="{% url 'accounts:login' %}">Anmelden</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -5,6 +5,4 @@ app_name = 'homepage' # Verhindert Konflikt mit anderen Apps
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.home, name="home"),
|
||||
path('impress', views.impress, name="impress"),
|
||||
path('privacy', views.privacy, name="privacy"),
|
||||
]
|
||||
4
core/homepage/views.py
Normal file
4
core/homepage/views.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from django.shortcuts import render, redirect
|
||||
|
||||
def home(request):
|
||||
return render(request, 'homepage/home.html')
|
||||
@@ -1,14 +1,15 @@
|
||||
from django.contrib import admin
|
||||
from .models import QivipQuiz, QivipQuestion, QivipQuizFavorite, QuestionImage, QuizCategory, QuizImage
|
||||
from .models import QivipQuiz, QivipQuestion, QuizCategory, Tag
|
||||
|
||||
# Für das QivipQuiz Modell
|
||||
class QivipQuizAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'user_id', 'status', 'category', 'creation_date', 'update_date') # Welche Felder sollen in der Übersicht angezeigt werden
|
||||
search_fields = ('name', 'user_id__username', 'category__name') # Suchfelder
|
||||
list_filter = ('status', 'category') # Filteroptionen
|
||||
|
||||
# Für das QivipQuestion Modell
|
||||
class QivipQuestionAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'quiz_id', 'data', 'creation_date', 'update_date')
|
||||
list_display = ('quiz_id', 'data', 'creation_date', 'update_date')
|
||||
search_fields = ('data',)
|
||||
list_filter = ('quiz_id',)
|
||||
|
||||
@@ -18,22 +19,13 @@ class QuizCategoryAdmin(admin.ModelAdmin):
|
||||
search_fields = ('name',)
|
||||
prepopulated_fields = {'slug': ('name',)} # Erstelle automatisch den Slug basierend auf dem Namen
|
||||
|
||||
|
||||
|
||||
class QivipQuizFavoriteAdmin(admin.ModelAdmin):
|
||||
list_display = ('user', 'favorite', 'quiz') # Welche Felder sollen in der Übersicht angezeigt werden
|
||||
|
||||
|
||||
class QuizImageAdmin(admin.ModelAdmin):
|
||||
list_display = ('image',)
|
||||
|
||||
class QuestionImageAdmin(admin.ModelAdmin):
|
||||
list_display = ('image',)
|
||||
# Für das Tag Modell
|
||||
class TagAdmin(admin.ModelAdmin):
|
||||
list_display = ('name',) # Zeige nur den Namen des Tags
|
||||
search_fields = ('name',)
|
||||
|
||||
# Registrierung der Modelle im Admin
|
||||
admin.site.register(QivipQuiz, QivipQuizAdmin)
|
||||
admin.site.register(QivipQuestion, QivipQuestionAdmin)
|
||||
admin.site.register(QuizCategory, QuizCategoryAdmin)
|
||||
admin.site.register(QivipQuizFavorite, QivipQuizFavoriteAdmin)
|
||||
admin.site.register(QuizImage, QuizImageAdmin)
|
||||
admin.site.register(QuestionImage, QuestionImageAdmin)
|
||||
admin.site.register(Tag, TagAdmin)
|
||||
12
core/library/forms.py
Normal file
12
core/library/forms.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django import forms
|
||||
from .models import QivipQuiz, QivipQuestion
|
||||
|
||||
class QuizForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = QivipQuiz
|
||||
fields = ['name', 'description', 'status', 'category', 'tags']
|
||||
|
||||
class QuestionForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = QivipQuestion
|
||||
fields = ['quiz_id', 'data']
|
||||
55
core/library/models.py
Normal file
55
core/library/models.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.text import slugify
|
||||
import uuid
|
||||
|
||||
class QivipQuiz(models.Model):
|
||||
STATUS_VALUES = {
|
||||
"public": "Öffentlich",
|
||||
"hidden": "Versteckt",
|
||||
"private": "Privat",
|
||||
}
|
||||
|
||||
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name='quiz')
|
||||
creation_date = models.DateTimeField(auto_now_add=True)
|
||||
update_date = models.DateTimeField(auto_now=True)
|
||||
status = models.CharField(max_length=10, choices=STATUS_VALUES.items())
|
||||
category = models.ForeignKey('QuizCategory', related_name='quiz', on_delete=models.CASCADE)
|
||||
tags = models.ManyToManyField('Tag', blank=True)
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class QivipQuestion(models.Model):
|
||||
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||
quiz_id = models.ForeignKey(QivipQuiz, on_delete=models.CASCADE, related_name='question')
|
||||
creation_date = models.DateTimeField(auto_now_add=True)
|
||||
update_date = models.DateTimeField(auto_now=True)
|
||||
data = models.TextField()
|
||||
|
||||
def __str__(self):
|
||||
return self.data[:50]
|
||||
|
||||
class Tag(models.Model):
|
||||
name = models.CharField(max_length=100, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class QuizCategory(models.Model):
|
||||
name = models.CharField(max_length=100, unique=True)
|
||||
description = models.TextField(blank=True, null=True)
|
||||
slug = models.SlugField(unique=True, blank=True)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(self.name)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
17
core/library/templates/library/base.html
Normal file
17
core/library/templates/library/base.html
Normal file
@@ -0,0 +1,17 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="{% static 'homepage/t-style.css' %}">
|
||||
<title>qivip</title>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'homepage/partials/_nav.html' %}
|
||||
<div class="max-w-screen-lg mx-auto">
|
||||
{% block content%}{% endblock %}
|
||||
</div>
|
||||
{% include 'homepage/partials/_footer.html' %}
|
||||
</body>
|
||||
</html>
|
||||
11
core/library/templates/library/delete_confirmation.html
Normal file
11
core/library/templates/library/delete_confirmation.html
Normal file
@@ -0,0 +1,11 @@
|
||||
{% extends 'library/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Löschen</h1>
|
||||
<p>Soll das Quiz "{{ object.name }}" wirklich gelöscht werden?</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit">Löschen</button>
|
||||
<a href="{% url 'library:overview_quiz' %}">Abbrechen</a>
|
||||
</form>
|
||||
{% endblock %}
|
||||
19
core/library/templates/library/detail_quiz.html
Normal file
19
core/library/templates/library/detail_quiz.html
Normal file
@@ -0,0 +1,19 @@
|
||||
{% extends 'library/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ quiz.name }}</h1>
|
||||
<p>{{ quiz.description }}</p>
|
||||
<a href="{% url 'library:edit_quiz' quiz.id %}">Bearbeiten</a>
|
||||
<a href="{% url 'library:delete_quiz' quiz.id %}">Löschen</a>
|
||||
|
||||
<h2>Fragen</h2>
|
||||
<ul>
|
||||
<a href="{% url 'library:new_question' %}">Neue Frage erstellen</a>
|
||||
{% for question in questions %}
|
||||
<li>{{ question.data }}</li>
|
||||
<a href="{% url 'library:edit_question' question.id %}">Bearbeiten</a>
|
||||
<a href="{% url 'library:delete_question' question.id %}">Löschen</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
||||
13
core/library/templates/library/form.html
Normal file
13
core/library/templates/library/form.html
Normal file
@@ -0,0 +1,13 @@
|
||||
{% extends 'library/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Formular</h1>
|
||||
<h2>{{ form.name.value }}</h2>
|
||||
<div class="input-group">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit">Speichern</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
19
core/library/templates/library/overview_quiz.html
Normal file
19
core/library/templates/library/overview_quiz.html
Normal file
@@ -0,0 +1,19 @@
|
||||
{% extends 'library/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Übersicht</h1>
|
||||
<div class="flex justify-end">
|
||||
<a href="{% url 'library:new_quiz' %}" class="bg-blue-500 text-white px-4 py-2 rounded-md">Neues Quiz erstellen</a>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{% for quiz in quizzes %}
|
||||
<div class="bg-white rounded-lg p-4 shadow-md">
|
||||
<h2 class="text-lg font-bold"><a href="{% url 'library:detail_quiz' quiz.id %}">{{ quiz.name }}</a></h2>
|
||||
<p class="text-sm text-gray-600">{{ quiz.description }}</p>
|
||||
<a href="#">Spiel starten</a>
|
||||
<a href="{% url 'library:edit_quiz' quiz.id %}">Bearbeiten</a>
|
||||
<a href="{% url 'library:delete_quiz' quiz.id %}">Löschen</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,8 +1,6 @@
|
||||
from django.urls import path
|
||||
|
||||
from django.conf import settings
|
||||
from . import views
|
||||
from django.conf.urls.static import static
|
||||
|
||||
app_name = 'library'
|
||||
|
||||
urlpatterns = [
|
||||
@@ -14,7 +12,4 @@ urlpatterns = [
|
||||
path('question/new/', views.new_question, name='new_question'),
|
||||
path('question/edit/<int:pk>/', views.edit_question, name='edit_question'),
|
||||
path('question/delete/<int:pk>/', views.delete_question, name='delete_question'),
|
||||
path('detail/copy/<int:pk>/', views.copy_quiz, name='copy_quiz'),
|
||||
path('favorite_quiz/<int:pk>/', views.favorite_quiz, name='favorite_quiz'),
|
||||
|
||||
]
|
||||
99
core/library/views.py
Normal file
99
core/library/views.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.models import User
|
||||
from .models import QivipQuiz, QivipQuestion
|
||||
from .forms import QuizForm, QuestionForm
|
||||
|
||||
# Übersicht aller Quizze
|
||||
@login_required
|
||||
def overview_quiz(request):
|
||||
quizzes = QivipQuiz.objects.filter(user_id=request.user)
|
||||
return render(request, 'library/overview_quiz.html', {'quizzes': quizzes})
|
||||
|
||||
# Neues Quiz erstellen
|
||||
@login_required
|
||||
def new_quiz(request):
|
||||
if request.method == 'POST':
|
||||
form = QuizForm(request.POST)
|
||||
if form.is_valid():
|
||||
quiz = form.save(commit=False)
|
||||
quiz.user_id = request.user
|
||||
quiz.save()
|
||||
form.save_m2m() # Speichert die Many-to-Many Beziehungen (Tags)
|
||||
return redirect('library:edit_quiz', pk=quiz.pk)
|
||||
else:
|
||||
form = QuizForm()
|
||||
return render(request, 'library/form.html', {'form': form})
|
||||
|
||||
# Quiz bearbeiten
|
||||
@login_required
|
||||
def edit_quiz(request, pk):
|
||||
quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user)
|
||||
if request.method == 'POST':
|
||||
form = QuizForm(request.POST, instance=quiz)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect('library:overview_quiz')
|
||||
else:
|
||||
form = QuizForm(instance=quiz)
|
||||
return render(request, 'library/form.html', {'form': form})
|
||||
|
||||
# Quiz löschen
|
||||
@login_required
|
||||
def delete_quiz(request, pk):
|
||||
quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user)
|
||||
if request.method == 'POST':
|
||||
quiz.delete()
|
||||
return redirect('library:overview_quiz')
|
||||
return render(request, 'library/delete_confirmation.html', {'object': quiz})
|
||||
|
||||
# Quiz anzeigen
|
||||
@login_required
|
||||
def detail_quiz(request, pk):
|
||||
quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user)
|
||||
questions = QivipQuestion.objects.filter(quiz_id=quiz)
|
||||
return render(request, 'library/detail_quiz.html', {'quiz': quiz, 'questions': questions})
|
||||
|
||||
# Übersicht aller Fragen
|
||||
@login_required
|
||||
def question_list(request):
|
||||
questions = QivipQuestion.objects.filter(quiz_id__user_id=request.user)
|
||||
return render(request, 'library/question_list.html', {'questions': questions})
|
||||
|
||||
# Neue Frage erstellen
|
||||
@login_required
|
||||
def new_question(request):
|
||||
if request.method == 'POST':
|
||||
form = QuestionForm(request.POST)
|
||||
if form.is_valid():
|
||||
question = form.save(commit=False)
|
||||
# Prüfe ob der User Zugriff auf das Quiz hat
|
||||
if question.quiz_id.user_id != request.user:
|
||||
return redirect('library:question_list')
|
||||
question.save()
|
||||
return redirect('library:edit_question', pk=question.pk)
|
||||
else:
|
||||
form = QuestionForm()
|
||||
return render(request, 'library/form.html', {'form': form})
|
||||
|
||||
# Frage bearbeiten
|
||||
@login_required
|
||||
def edit_question(request, pk):
|
||||
question = get_object_or_404(QivipQuestion, pk=pk, quiz_id__user_id=request.user)
|
||||
if request.method == 'POST':
|
||||
form = QuestionForm(request.POST, instance=question)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect('library:edit_question', pk=pk)
|
||||
else:
|
||||
form = QuestionForm(instance=question)
|
||||
return render(request, 'library/form.html', {'form': form})
|
||||
|
||||
# Frage löschen
|
||||
@login_required
|
||||
def delete_question(request, pk):
|
||||
question = get_object_or_404(QivipQuestion, pk=pk, quiz_id__user_id=request.user)
|
||||
if request.method == 'POST':
|
||||
question.delete()
|
||||
return redirect('library:detail_quiz', pk=question.quiz_id.pk)
|
||||
return render(request, 'library/delete_confirmation.html', {'object': question})
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"ENABLE_RATING_SYSTEM": true
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
"""
|
||||
ASGI config for core project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
from django.core.asgi import get_asgi_application
|
||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||
from channels.auth import AuthMiddlewareStack
|
||||
import django
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
|
||||
django.setup()
|
||||
|
||||
from play.routing import websocket_urlpatterns
|
||||
|
||||
application = ProtocolTypeRouter({
|
||||
'http': get_asgi_application(),
|
||||
'websocket': AuthMiddlewareStack(
|
||||
URLRouter(
|
||||
websocket_urlpatterns
|
||||
)
|
||||
),
|
||||
})
|
||||
@@ -1,9 +0,0 @@
|
||||
from django.shortcuts import render, redirect
|
||||
|
||||
def home(request):
|
||||
return render(request, 'homepage/home.html')
|
||||
|
||||
def impress(request):
|
||||
return render(request, 'homepage/impress.html')
|
||||
def privacy(request):
|
||||
return render(request, 'homepage/privacy.html')
|
||||
@@ -1,31 +0,0 @@
|
||||
from django import forms
|
||||
from .models import QivipQuiz, QivipQuestion
|
||||
|
||||
class QuizForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = QivipQuiz
|
||||
fields = ['name', 'description', 'status', 'category','difficulty','credits']
|
||||
|
||||
class QuestionForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = QivipQuestion
|
||||
fields = ['quiz_id', 'data','time_per_question']
|
||||
|
||||
from django import forms
|
||||
|
||||
class QuizFilterForm(forms.Form):
|
||||
search = forms.CharField(required=False, label="Suche", widget=forms.TextInput(attrs={
|
||||
'class': 'flex flex-grow rounded-lg p-2' ,'placeholder': 'Suche ...',
|
||||
}))
|
||||
|
||||
|
||||
min_amout_questions = forms.IntegerField(required=False, min_value=1, label="Minimale Anzahl an Fragen", widget=forms.NumberInput(attrs={
|
||||
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
|
||||
}))
|
||||
|
||||
max_amout_questions = forms.IntegerField(required=False, min_value=1, label="Maximale Anzahl an Fragen", widget=forms.NumberInput(attrs={
|
||||
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
|
||||
}))
|
||||
user = forms.CharField(required=False, label="von User", widget=forms.TextInput(attrs={
|
||||
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
|
||||
}))
|
||||
@@ -1,13 +0,0 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from play.models import QuizGame
|
||||
from django.utils import timezone
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Bereinigt beendete Spiele'
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
expired_games = QuizGame.objects.filter(
|
||||
updated_at__lt=timezone.now() - timezone.timedelta(minutes=5))
|
||||
deleted_count = expired_games.count()
|
||||
expired_games.delete()
|
||||
self.stdout.write(f'{deleted_count} alte Spiele gelöscht.')
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='private', max_length=10),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0002_alter_qivipquiz_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='Privat', max_length=10),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0003_alter_qivipquiz_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='private', max_length=10),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0004_alter_qivipquiz_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='public', max_length=10),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0005_alter_qivipquiz_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, default='In dem Quiz geht es um ...', null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0006_alter_qivipquiz_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, default='Das Quiz ist ein ... Quiz. In dem Quiz geht es um ...', null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0007_alter_qivipquiz_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, default='Das Quiz ist ein ... Quiz. \n In dem Quiz geht es um ...', null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0008_alter_qivipquiz_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, default='Das Quiz ist ein ... Quiz. \nIn dem Quiz geht es um ...', null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0009_alter_qivipquiz_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, default='In dem Quiz geht es um ...', null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0010_alter_qivipquiz_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='qivipquiz',
|
||||
name='difficulty',
|
||||
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='public', max_length=10),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0011_qivipquiz_difficulty'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='difficulty',
|
||||
field=models.CharField(choices=[('public', 'Öffentlich'), ('hidden', 'Versteckt'), ('private', 'Privat')], default='not defined', max_length=10),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0012_alter_qivipquiz_difficulty'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='difficulty',
|
||||
field=models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('not defined', 'nicht gesetzt')], default='not defined', max_length=11),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0013_alter_qivipquiz_difficulty'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='difficulty',
|
||||
field=models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('not defined', 'nicht gesetzt')], default='not defined', help_text='1: niedrigste Schwierigkeit, 2: höchste Schwierigkeit', max_length=11),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.3 on 2025-03-08 13:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0014_alter_qivipquiz_difficulty'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='difficulty',
|
||||
field=models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('not defined', 'nicht gesetzt')], default='not defined', help_text='1: niedrigste Schwierigkeit und 5: höchste Schwierigkeit', max_length=11),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# 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),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# 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),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# 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),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# 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),
|
||||
),
|
||||
]
|
||||
@@ -1,19 +0,0 @@
|
||||
# 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]),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# 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),
|
||||
),
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
# 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),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-03-21 17:24
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0022_alter_qivipquiz_description_alter_qivipquiz_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='qivipquiz',
|
||||
name='image',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='quiz_images/'),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-03-22 10:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0023_qivipquiz_image'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='image',
|
||||
field=models.ImageField(blank=True, max_length=256, null=True, upload_to='quiz_images/'),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-04-03 16:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0024_alter_qivipquiz_image'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='qivipquiz',
|
||||
name='modified_description',
|
||||
field=models.TextField(blank=True, default='Das Quiz wurde modifiziert von:', editable=False),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-04-04 13:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0025_qivipquiz_modified_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='modified_description',
|
||||
field=models.TextField(blank=True, editable=False),
|
||||
),
|
||||
]
|
||||
@@ -1,21 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-04-04 13:37
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0026_alter_qivipquiz_modified_description'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='qivipquiz',
|
||||
name='creator',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='creator_quiz', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
@@ -1,21 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-04-04 13:57
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0027_qivipquiz_creator'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquiz',
|
||||
name='creator',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='creator_quiz', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-04-04 16:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0028_alter_qivipquiz_creator'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='qivipquiz',
|
||||
name='credits',
|
||||
field=models.TextField(blank=True),
|
||||
),
|
||||
]
|
||||
@@ -1,27 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-04-05 09:39
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0029_qivipquiz_credits'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='qivipquiz',
|
||||
name='creator',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='qivipquiz',
|
||||
name='modified_description',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='qivipquiz',
|
||||
name='base_quiz',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='child_quizzes', to='library.qivipquiz'),
|
||||
),
|
||||
]
|
||||
@@ -1,19 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-04-05 17:45
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0030_remove_qivipquiz_creator_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='qivipquestion',
|
||||
name='quiz_id',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='library.qivipquiz'),
|
||||
),
|
||||
]
|
||||
@@ -1,37 +0,0 @@
|
||||
# Generated by Django 5.1.7 on 2025-04-05 19:08
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0031_alter_qivipquestion_quiz_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='qivipquiz',
|
||||
name='average_rating',
|
||||
field=models.FloatField(default=3.0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='qivipquiz',
|
||||
name='rating_count',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='QuizRating',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('participant_id', models.CharField(max_length=200)),
|
||||
('rating', models.IntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5')])),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('quiz', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to='library.qivipquiz')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('quiz', 'participant_id')},
|
||||
},
|
||||
),
|
||||
]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user