Compare commits

..

1 Commits

Author SHA1 Message Date
Juhn-Vitus Saß
b09eb2c7d6 Fix #5 2025-03-08 15:34:39 +01:00
39 changed files with 1282 additions and 478 deletions

1
.gitignore vendored
View File

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

View 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;
}

View 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;
}
}

View File

@@ -1,25 +1,11 @@
{% extends 'accounts/base.html'%} {% extends 'accounts/base.html'%}
{% block title %}Home{% endblock %} {% block title %}Home{% endblock %}
{% block content %} {% block content %}
<div class="grid place-content-center h-120">
<h1 class="text-center m-4 text-6xl">Moin moin!</h1>
<div class="max-w-screen-lg mx-auto mt-12"> <form action="{% url 'accounts:logout' %}" method="post">
<div class="input-group p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md">
<div class="grid place-content-center h-120">
<h1 class="text-center m-4 text-3xl lg:text-6xl">Willkommen, <b>{{ request.user.username }}</b>!</h1>
<form action="{% url 'accounts:logout' %}" method="post">
{% csrf_token %} {% csrf_token %}
<button class="bg-blue-600 text-white p-3 rounded-full font-black" type="submit">Abmelden</button> <button class="bg-red-600 text-white p-3 rounded-full font-black" type="submit">Abmelden</button>
</form> </form>
</div>
<div class="flex justify-end">
<span class="text-[clamp(0.6rem,5vw,1rem)] font-black overflow-x-auto mt-1 ml-0.75 text-blue-600 p-0.75 bg-white">qivip
</span>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@@ -2,12 +2,12 @@
{% load static %} {% load static %}
{% block title %}Anmeldung{% endblock %} {% block title %}Anmeldung{% endblock %}
{% block content %} {% block content %}
<div class="grid place-content-center h-120 mt-12 mb-12 "> <div class="grid place-content-center h-120">
<div class="w-80 p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md lg:w-90"> <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> <h2 class="text-2xl text-center p-4 font-black">Anmeldung</h2>
<form class="space-y-4" method="post"> <form class="space-y-4" method="post">
{% if form.errors %} {% 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> <ul>
{% for field, errors in form.errors.items %} {% for field, errors in form.errors.items %}
{% for error in errors %} {% for error in errors %}
@@ -18,11 +18,11 @@
</div> </div>
{% endif %} {% endif %}
{% csrf_token %} {% csrf_token %}
<div class="items-center w-full"> <div class="">
<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"> <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>
<div class="items-center w-full"> <div class="">
<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"> <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> </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> <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> </form>

View File

@@ -1,9 +1,8 @@
{% extends 'accounts/base.html' %} {% extends 'accounts/base.html' %}
{% block title %}Registrierung{% endblock %} {% block title %}Registrierung{% endblock %}
{% block content %} {% block content %}
<div class="grid place-content-center h-120 mt-12 mb-12"> <div class="grid place-content-center h-120">
<div class=" p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md">
<div class="w-80 p-4 m-2 border-blue-600 border-2 rounded-xl shadow-md lg:w-90">
<h2 class="text-2xl text-center p-4 font-black">Registrieren</h2> <h2 class="text-2xl text-center p-4 font-black">Registrieren</h2>
<form class=" space-y-4" method="post"> <form class=" space-y-4" method="post">

View File

@@ -127,4 +127,4 @@ BASE_DIR='static' #von ben8 hinzugefügt
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_URL = '/account/login/' LOGIN_URL = '/login/'

View File

@@ -7,10 +7,7 @@ nav div ul.qp-nav-list li {
a.qp-a-button { a.qp-a-button {
@apply p-6 rounded-md font-extrabold hover:scale-105 transition duration-300 shadow-md; @apply p-6 rounded-md font-extrabold hover:scale-105 transition duration-300 shadow-md;
} }
a.qp-a-button-small { .register input{@apply p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black;}
@apply p-1 rounded-md 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 w-full;}
.input-group { .input-group {
@apply flex flex-col gap-4 max-w-md mx-auto p-6 bg-white rounded-xl shadow-lg; @apply flex flex-col gap-4 max-w-md mx-auto p-6 bg-white rounded-xl shadow-lg;

View File

@@ -1,31 +1,50 @@
/*! tailwindcss v4.0.12 | MIT License | https://tailwindcss.com */ /*! tailwindcss v4.0.9 | MIT License | https://tailwindcss.com */
@layer theme, base, components, utilities; @layer theme, base, components, utilities;
@layer theme { @layer theme {
:root, :host { :root, :host {
--font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", --font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 'Noto Color Emoji';
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
"Courier New", monospace; monospace;
--color-red-100: oklch(0.936 0.032 17.717);
--color-red-200: oklch(0.885 0.062 18.334);
--color-red-500: oklch(0.637 0.237 25.331); --color-red-500: oklch(0.637 0.237 25.331);
--color-red-600: oklch(0.577 0.245 27.325); --color-red-600: oklch(0.577 0.245 27.325);
--color-red-700: oklch(0.505 0.213 27.518);
--color-red-800: oklch(0.444 0.177 26.899);
--color-yellow-500: oklch(0.795 0.184 86.047); --color-yellow-500: oklch(0.795 0.184 86.047);
--color-green-500: oklch(0.723 0.219 149.579); --color-green-500: oklch(0.723 0.219 149.579);
--color-green-600: oklch(0.627 0.194 149.214);
--color-green-700: oklch(0.527 0.154 150.069);
--color-blue-100: oklch(0.932 0.032 255.585); --color-blue-100: oklch(0.932 0.032 255.585);
--color-blue-400: oklch(0.707 0.165 254.624); --color-blue-400: oklch(0.707 0.165 254.624);
--color-blue-500: oklch(0.623 0.214 259.815); --color-blue-500: oklch(0.623 0.214 259.815);
--color-blue-600: oklch(0.546 0.245 262.881); --color-blue-600: oklch(0.546 0.245 262.881);
--color-blue-700: oklch(0.488 0.243 264.376);
--color-blue-800: oklch(0.424 0.199 265.638);
--color-indigo-500: oklch(0.585 0.233 277.117); --color-indigo-500: oklch(0.585 0.233 277.117);
--color-indigo-600: oklch(0.511 0.262 276.966); --color-indigo-600: oklch(0.511 0.262 276.966);
--color-purple-100: oklch(0.946 0.033 307.174);
--color-purple-500: oklch(0.627 0.265 303.9); --color-purple-500: oklch(0.627 0.265 303.9);
--color-purple-800: oklch(0.438 0.218 303.724);
--color-gray-50: oklch(0.985 0.002 247.839);
--color-gray-100: oklch(0.967 0.003 264.542);
--color-gray-200: oklch(0.928 0.006 264.531); --color-gray-200: oklch(0.928 0.006 264.531);
--color-gray-300: oklch(0.872 0.01 258.338);
--color-gray-400: oklch(0.707 0.022 261.325);
--color-gray-500: oklch(0.551 0.027 264.364); --color-gray-500: oklch(0.551 0.027 264.364);
--color-gray-600: oklch(0.446 0.03 256.802); --color-gray-600: oklch(0.446 0.03 256.802);
--color-gray-700: oklch(0.373 0.034 259.733);
--color-gray-900: oklch(0.21 0.034 264.665); --color-gray-900: oklch(0.21 0.034 264.665);
--color-stone-900: oklch(0.216 0.006 56.043); --color-stone-900: oklch(0.216 0.006 56.043);
--color-white: #fff; --color-white: #fff;
--spacing: 0.25rem; --spacing: 0.25rem;
--breakpoint-lg: 64rem; --breakpoint-lg: 64rem;
--container-md: 28rem; --container-md: 28rem;
--container-lg: 32rem;
--container-7xl: 80rem;
--text-xs: 0.75rem;
--text-xs--line-height: calc(1 / 0.75);
--text-sm: 0.875rem; --text-sm: 0.875rem;
--text-sm--line-height: calc(1.25 / 0.875); --text-sm--line-height: calc(1.25 / 0.875);
--text-lg: 1.125rem; --text-lg: 1.125rem;
@@ -34,14 +53,13 @@
--text-xl--line-height: calc(1.75 / 1.25); --text-xl--line-height: calc(1.75 / 1.25);
--text-2xl: 1.5rem; --text-2xl: 1.5rem;
--text-2xl--line-height: calc(2 / 1.5); --text-2xl--line-height: calc(2 / 1.5);
--text-3xl: 1.875rem;
--text-3xl--line-height: calc(2.25 / 1.875);
--text-6xl: 3.75rem; --text-6xl: 3.75rem;
--text-6xl--line-height: 1; --text-6xl--line-height: 1;
--text-8xl: 6rem; --text-8xl: 6rem;
--text-8xl--line-height: 1; --text-8xl--line-height: 1;
--font-weight-extralight: 200; --font-weight-extralight: 200;
--font-weight-light: 300; --font-weight-light: 300;
--font-weight-medium: 500;
--font-weight-semibold: 600; --font-weight-semibold: 600;
--font-weight-bold: 700; --font-weight-bold: 700;
--font-weight-extrabold: 800; --font-weight-extrabold: 800;
@@ -54,16 +72,10 @@
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-sans); --default-font-family: var(--font-sans);
--default-font-feature-settings: var(--font-sans--font-feature-settings); --default-font-feature-settings: var(--font-sans--font-feature-settings);
--default-font-variation-settings: var( --default-font-variation-settings: var(--font-sans--font-variation-settings);
--font-sans--font-variation-settings
);
--default-mono-font-family: var(--font-mono); --default-mono-font-family: var(--font-mono);
--default-mono-font-feature-settings: var( --default-mono-font-feature-settings: var(--font-mono--font-feature-settings);
--font-mono--font-feature-settings --default-mono-font-variation-settings: var(--font-mono--font-variation-settings);
);
--default-mono-font-variation-settings: var(
--font-mono--font-variation-settings
);
} }
} }
@layer base { @layer base {
@@ -77,9 +89,9 @@
line-height: 1.5; line-height: 1.5;
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
tab-size: 4; 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-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-feature-settings: var(--default-font-feature-settings, normal);
font-variation-settings: var( --default-font-variation-settings, normal ); font-variation-settings: var(--default-font-variation-settings, normal);
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
body { body {
@@ -107,9 +119,9 @@
font-weight: bolder; font-weight: bolder;
} }
code, kbd, samp, pre { code, kbd, samp, pre {
font-family: var( --default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace ); 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-feature-settings: var(--default-mono-font-feature-settings, normal);
font-variation-settings: var( --default-mono-font-variation-settings, normal ); font-variation-settings: var(--default-mono-font-variation-settings, normal);
font-size: 1em; font-size: 1em;
} }
small { small {
@@ -197,23 +209,32 @@
:-moz-ui-invalid { :-moz-ui-invalid {
box-shadow: none; box-shadow: none;
} }
button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { button, input:where([type='button'], [type='reset'], [type='submit']), ::file-selector-button {
appearance: button; appearance: button;
} }
::-webkit-inner-spin-button, ::-webkit-outer-spin-button { ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
height: auto; height: auto;
} }
[hidden]:where(:not([hidden="until-found"])) { [hidden]:where(:not([hidden='until-found'])) {
display: none !important; display: none !important;
} }
} }
@layer utilities { @layer utilities {
.collapse {
visibility: collapse;
}
.absolute { .absolute {
position: absolute; position: absolute;
} }
.relative {
position: relative;
}
.static { .static {
position: static; position: static;
} }
.sticky {
position: sticky;
}
.top-0 { .top-0 {
top: calc(var(--spacing) * 0); top: calc(var(--spacing) * 0);
} }
@@ -232,6 +253,24 @@
.z-40 { .z-40 {
z-index: 40; z-index: 40;
} }
.container {
width: 100%;
@media (width >= 40rem) {
max-width: 40rem;
}
@media (width >= 48rem) {
max-width: 48rem;
}
@media (width >= 64rem) {
max-width: 64rem;
}
@media (width >= 80rem) {
max-width: 80rem;
}
@media (width >= 96rem) {
max-width: 96rem;
}
}
.m-0 { .m-0 {
margin: calc(var(--spacing) * 0); margin: calc(var(--spacing) * 0);
} }
@@ -247,9 +286,6 @@
.mx-auto { .mx-auto {
margin-inline: auto; margin-inline: auto;
} }
.my-8 {
margin-block: calc(var(--spacing) * 8);
}
.mt-1 { .mt-1 {
margin-top: calc(var(--spacing) * 1); margin-top: calc(var(--spacing) * 1);
} }
@@ -262,24 +298,54 @@
.mt-4 { .mt-4 {
margin-top: calc(var(--spacing) * 4); margin-top: calc(var(--spacing) * 4);
} }
.mt-6 {
margin-top: calc(var(--spacing) * 6);
}
.mt-8 { .mt-8 {
margin-top: calc(var(--spacing) * 8); margin-top: calc(var(--spacing) * 8);
} }
.mt-12 { .mt-10 {
margin-top: calc(var(--spacing) * 12); margin-top: calc(var(--spacing) * 10);
}
.mr-0 {
margin-right: calc(var(--spacing) * 0);
} }
.mr-0\.75 { .mr-0\.75 {
margin-right: calc(var(--spacing) * 0.75); margin-right: calc(var(--spacing) * 0.75);
} }
.mr-1\.5 {
margin-right: calc(var(--spacing) * 1.5);
}
.mb-2 {
margin-bottom: calc(var(--spacing) * 2);
}
.mb-3 {
margin-bottom: calc(var(--spacing) * 3);
}
.mb-4 {
margin-bottom: calc(var(--spacing) * 4);
}
.mb-6 { .mb-6 {
margin-bottom: calc(var(--spacing) * 6); margin-bottom: calc(var(--spacing) * 6);
} }
.mb-12 { .mb-8 {
margin-bottom: calc(var(--spacing) * 12); margin-bottom: calc(var(--spacing) * 8);
}
.ml-0 {
margin-left: calc(var(--spacing) * 0);
} }
.ml-0\.75 { .ml-0\.75 {
margin-left: calc(var(--spacing) * 0.75); margin-left: calc(var(--spacing) * 0.75);
} }
.ml-1 {
margin-left: calc(var(--spacing) * 1);
}
.ml-2 {
margin-left: calc(var(--spacing) * 2);
}
.ml-4 {
margin-left: calc(var(--spacing) * 4);
}
.block { .block {
display: block; display: block;
} }
@@ -292,6 +358,21 @@
.hidden { .hidden {
display: none; display: none;
} }
.inline-flex {
display: inline-flex;
}
.list-item {
display: list-item;
}
.table {
display: table;
}
.h-4 {
height: calc(var(--spacing) * 4);
}
.h-5 {
height: calc(var(--spacing) * 5);
}
.h-12 { .h-12 {
height: calc(var(--spacing) * 12); height: calc(var(--spacing) * 12);
} }
@@ -307,15 +388,51 @@
.h-full { .h-full {
height: 100%; height: 100%;
} }
.w-80 { .w-4 {
width: calc(var(--spacing) * 80); width: calc(var(--spacing) * 4);
}
.w-5 {
width: calc(var(--spacing) * 5);
}
.w-12 {
width: calc(var(--spacing) * 12);
} }
.w-full { .w-full {
width: 100%; width: 100%;
} }
.max-w-7xl {
max-width: var(--container-7xl);
}
.max-w-lg {
max-width: var(--container-lg);
}
.max-w-screen-lg { .max-w-screen-lg {
max-width: var(--breakpoint-lg); max-width: var(--breakpoint-lg);
} }
.flex-shrink-0 {
flex-shrink: 0;
}
.flex-grow {
flex-grow: 1;
}
.border-collapse {
border-collapse: collapse;
}
.transform {
transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y);
}
.cursor-pointer {
cursor: pointer;
}
.resize {
resize: both;
}
.list-inside {
list-style-position: inside;
}
.list-disc {
list-style-type: disc;
}
.grid-cols-1 { .grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr)); grid-template-columns: repeat(1, minmax(0, 1fr));
} }
@@ -331,6 +448,9 @@
.items-center { .items-center {
align-items: center; align-items: center;
} }
.items-start {
align-items: flex-start;
}
.justify-around { .justify-around {
justify-content: space-around; justify-content: space-around;
} }
@@ -349,6 +469,20 @@
.gap-4 { .gap-4 {
gap: calc(var(--spacing) * 4); gap: calc(var(--spacing) * 4);
} }
.space-y-1 {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)));
}
}
.space-y-3 {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)));
}
}
.space-y-4 { .space-y-4 {
:where(& > :not(:last-child)) { :where(& > :not(:last-child)) {
--tw-space-y-reverse: 0; --tw-space-y-reverse: 0;
@@ -356,6 +490,13 @@
margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse))); margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
} }
} }
.space-y-6 {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)));
}
}
.space-x-2 { .space-x-2 {
:where(& > :not(:last-child)) { :where(& > :not(:last-child)) {
--tw-space-x-reverse: 0; --tw-space-x-reverse: 0;
@@ -363,6 +504,13 @@
margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse))); margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
} }
} }
.space-x-3 {
:where(& > :not(:last-child)) {
--tw-space-x-reverse: 0;
margin-inline-start: calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));
margin-inline-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)));
}
}
.space-x-4 { .space-x-4 {
:where(& > :not(:last-child)) { :where(& > :not(:last-child)) {
--tw-space-x-reverse: 0; --tw-space-x-reverse: 0;
@@ -370,12 +518,29 @@
margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse))); margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)));
} }
} }
.divide-y {
:where(& > :not(:last-child)) {
--tw-divide-y-reverse: 0;
border-bottom-style: var(--tw-border-style);
border-top-style: var(--tw-border-style);
border-top-width: calc(1px * var(--tw-divide-y-reverse));
border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
}
}
.divide-gray-200 {
:where(& > :not(:last-child)) {
border-color: var(--color-gray-200);
}
}
.overflow-hidden { .overflow-hidden {
overflow: hidden; overflow: hidden;
} }
.overflow-x-auto { .overflow-x-auto {
overflow-x: auto; overflow-x: auto;
} }
.rounded {
border-radius: 0.25rem;
}
.rounded-full { .rounded-full {
border-radius: calc(infinity * 1px); border-radius: calc(infinity * 1px);
} }
@@ -391,6 +556,10 @@
.rounded-xl { .rounded-xl {
border-radius: var(--radius-xl); border-radius: var(--radius-xl);
} }
.border {
border-style: var(--tw-border-style);
border-width: 1px;
}
.border-2 { .border-2 {
border-style: var(--tw-border-style); border-style: var(--tw-border-style);
border-width: 2px; border-width: 2px;
@@ -403,15 +572,28 @@
border-style: var(--tw-border-style); border-style: var(--tw-border-style);
border-width: 4px; border-width: 4px;
} }
.border-b {
border-bottom-style: var(--tw-border-style);
border-bottom-width: 1px;
}
.border-blue-100 { .border-blue-100 {
border-color: var(--color-blue-100); border-color: var(--color-blue-100);
} }
.border-blue-600 { .border-blue-600 {
border-color: var(--color-blue-600); border-color: var(--color-blue-600);
} }
.border-gray-200 {
border-color: var(--color-gray-200);
}
.border-gray-300 {
border-color: var(--color-gray-300);
}
.border-gray-500 { .border-gray-500 {
border-color: var(--color-gray-500); border-color: var(--color-gray-500);
} }
.border-transparent {
border-color: transparent;
}
.bg-blue-100 { .bg-blue-100 {
background-color: var(--color-blue-100); background-color: var(--color-blue-100);
} }
@@ -424,6 +606,12 @@
.bg-blue-600 { .bg-blue-600 {
background-color: var(--color-blue-600); background-color: var(--color-blue-600);
} }
.bg-gray-50 {
background-color: var(--color-gray-50);
}
.bg-gray-100 {
background-color: var(--color-gray-100);
}
.bg-gray-200 { .bg-gray-200 {
background-color: var(--color-gray-200); background-color: var(--color-gray-200);
} }
@@ -436,12 +624,21 @@
.bg-indigo-500 { .bg-indigo-500 {
background-color: var(--color-indigo-500); background-color: var(--color-indigo-500);
} }
.bg-purple-100 {
background-color: var(--color-purple-100);
}
.bg-purple-500 { .bg-purple-500 {
background-color: var(--color-purple-500); background-color: var(--color-purple-500);
} }
.bg-red-100 {
background-color: var(--color-red-100);
}
.bg-red-500 { .bg-red-500 {
background-color: var(--color-red-500); background-color: var(--color-red-500);
} }
.bg-red-600 {
background-color: var(--color-red-600);
}
.bg-white { .bg-white {
background-color: var(--color-white); background-color: var(--color-white);
} }
@@ -451,6 +648,9 @@
.object-cover { .object-cover {
object-fit: cover; object-fit: cover;
} }
.p-0 {
padding: calc(var(--spacing) * 0);
}
.p-0\.75 { .p-0\.75 {
padding: calc(var(--spacing) * 0.75); padding: calc(var(--spacing) * 0.75);
} }
@@ -463,29 +663,64 @@
.p-4 { .p-4 {
padding: calc(var(--spacing) * 4); padding: calc(var(--spacing) * 4);
} }
.p-6 {
padding: calc(var(--spacing) * 6);
}
.p-8 {
padding: calc(var(--spacing) * 8);
}
.px-1 { .px-1 {
padding-inline: calc(var(--spacing) * 1); padding-inline: calc(var(--spacing) * 1);
} }
.px-2 {
padding-inline: calc(var(--spacing) * 2);
}
.px-2\.5 {
padding-inline: calc(var(--spacing) * 2.5);
}
.px-3 {
padding-inline: calc(var(--spacing) * 3);
}
.px-4 { .px-4 {
padding-inline: calc(var(--spacing) * 4); padding-inline: calc(var(--spacing) * 4);
} }
.py-0\.5 {
padding-block: calc(var(--spacing) * 0.5);
}
.py-1 {
padding-block: calc(var(--spacing) * 1);
}
.py-1\.5 {
padding-block: calc(var(--spacing) * 1.5);
}
.py-2 { .py-2 {
padding-block: calc(var(--spacing) * 2); padding-block: calc(var(--spacing) * 2);
} }
.py-4 {
padding-block: calc(var(--spacing) * 4);
}
.py-5 {
padding-block: calc(var(--spacing) * 5);
}
.py-8 {
padding-block: calc(var(--spacing) * 8);
}
.py-12 {
padding-block: calc(var(--spacing) * 12);
}
.pb-4 { .pb-4 {
padding-bottom: calc(var(--spacing) * 4); padding-bottom: calc(var(--spacing) * 4);
} }
.text-center { .text-center {
text-align: center; text-align: center;
} }
.font-mono {
font-family: var(--font-mono);
}
.text-2xl { .text-2xl {
font-size: var(--text-2xl); font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height)); line-height: var(--tw-leading, var(--text-2xl--line-height));
} }
.text-3xl {
font-size: var(--text-3xl);
line-height: var(--tw-leading, var(--text-3xl--line-height));
}
.text-6xl { .text-6xl {
font-size: var(--text-6xl); font-size: var(--text-6xl);
line-height: var(--tw-leading, var(--text-6xl--line-height)); line-height: var(--tw-leading, var(--text-6xl--line-height));
@@ -502,6 +737,10 @@
font-size: var(--text-xl); font-size: var(--text-xl);
line-height: var(--tw-leading, var(--text-xl--line-height)); line-height: var(--tw-leading, var(--text-xl--line-height));
} }
.text-xs {
font-size: var(--text-xs);
line-height: var(--tw-leading, var(--text-xs--line-height));
}
.text-\[clamp\(0\.3rem\,5vw\,1rem\)\] { .text-\[clamp\(0\.3rem\,5vw\,1rem\)\] {
font-size: clamp(0.3rem, 5vw, 1rem); font-size: clamp(0.3rem, 5vw, 1rem);
} }
@@ -533,6 +772,10 @@
--tw-font-weight: var(--font-weight-light); --tw-font-weight: var(--font-weight-light);
font-weight: var(--font-weight-light); font-weight: var(--font-weight-light);
} }
.font-medium {
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
}
.font-semibold { .font-semibold {
--tw-font-weight: var(--font-weight-semibold); --tw-font-weight: var(--font-weight-semibold);
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
@@ -540,15 +783,54 @@
.break-words { .break-words {
overflow-wrap: break-word; overflow-wrap: break-word;
} }
.break-all {
word-break: break-all;
}
.whitespace-pre-wrap {
white-space: pre-wrap;
}
.text-blue-600 { .text-blue-600 {
color: var(--color-blue-600); color: var(--color-blue-600);
} }
.text-blue-800 {
color: var(--color-blue-800);
}
.text-gray-400 {
color: var(--color-gray-400);
}
.text-gray-500 {
color: var(--color-gray-500);
}
.text-gray-600 { .text-gray-600 {
color: var(--color-gray-600); color: var(--color-gray-600);
} }
.text-gray-700 {
color: var(--color-gray-700);
}
.text-gray-900 {
color: var(--color-gray-900);
}
.text-green-500 {
color: var(--color-green-500);
}
.text-green-600 {
color: var(--color-green-600);
}
.text-green-700 {
color: var(--color-green-700);
}
.text-purple-800 {
color: var(--color-purple-800);
}
.text-red-500 {
color: var(--color-red-500);
}
.text-red-600 { .text-red-600 {
color: var(--color-red-600); color: var(--color-red-600);
} }
.text-red-700 {
color: var(--color-red-700);
}
.text-stone-900 { .text-stone-900 {
color: var(--color-stone-900); color: var(--color-stone-900);
} }
@@ -558,6 +840,13 @@
.italic { .italic {
font-style: italic; font-style: italic;
} }
.underline {
text-decoration-line: underline;
}
.shadow {
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px 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);
}
.shadow-\[0_2px_2px_rgba\(0\,0\,0\,0\.1\)\] { .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)); --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); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -566,11 +855,31 @@
--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)); --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); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
} }
.shadow-sm {
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px 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);
}
.outline {
outline-style: var(--tw-outline-style);
outline-width: 1px;
}
.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,);
}
.backdrop-filter {
-webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
}
.transition { .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-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-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration)); transition-duration: var(--tw-duration, var(--default-transition-duration));
} }
.transition-colors {
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.duration-200 { .duration-200 {
--tw-duration: 200ms; --tw-duration: 200ms;
transition-duration: 200ms; transition-duration: 200ms;
@@ -595,6 +904,83 @@
} }
} }
} }
.hover\:bg-blue-600 {
&:hover {
@media (hover: hover) {
background-color: var(--color-blue-600);
}
}
}
.hover\:bg-blue-700 {
&:hover {
@media (hover: hover) {
background-color: var(--color-blue-700);
}
}
}
.hover\:bg-gray-50 {
&:hover {
@media (hover: hover) {
background-color: var(--color-gray-50);
}
}
}
.hover\:bg-gray-200 {
&:hover {
@media (hover: hover) {
background-color: var(--color-gray-200);
}
}
}
.hover\:bg-gray-600 {
&:hover {
@media (hover: hover) {
background-color: var(--color-gray-600);
}
}
}
.hover\:bg-green-600 {
&:hover {
@media (hover: hover) {
background-color: var(--color-green-600);
}
}
}
.hover\:bg-red-200 {
&:hover {
@media (hover: hover) {
background-color: var(--color-red-200);
}
}
}
.hover\:bg-red-600 {
&:hover {
@media (hover: hover) {
background-color: var(--color-red-600);
}
}
}
.hover\:bg-red-700 {
&:hover {
@media (hover: hover) {
background-color: var(--color-red-700);
}
}
}
.hover\:text-gray-700 {
&:hover {
@media (hover: hover) {
color: var(--color-gray-700);
}
}
}
.hover\:text-red-800 {
&:hover {
@media (hover: hover) {
color: var(--color-red-800);
}
}
}
.focus\:scale-105 { .focus\:scale-105 {
&:focus { &:focus {
--tw-scale-x: 105%; --tw-scale-x: 105%;
@@ -603,11 +989,49 @@
scale: var(--tw-scale-x) var(--tw-scale-y); scale: var(--tw-scale-x) var(--tw-scale-y);
} }
} }
.focus\:border-blue-500 {
&:focus {
border-color: var(--color-blue-500);
}
}
.focus\:ring-2 {
&: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\:ring-blue-500 {
&:focus {
--tw-ring-color: var(--color-blue-500);
}
}
.focus\:ring-red-500 {
&:focus {
--tw-ring-color: var(--color-red-500);
}
}
.focus\:ring-offset-2 {
&:focus {
--tw-ring-offset-width: 2px;
--tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
}
}
.focus\:outline-none {
&:focus {
--tw-outline-style: none;
outline-style: none;
}
}
.sm\:grid-cols-3 { .sm\:grid-cols-3 {
@media (width >= 40rem) { @media (width >= 40rem) {
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
} }
} }
.sm\:rounded-lg {
@media (width >= 40rem) {
border-radius: var(--radius-lg);
}
}
.sm\:px-6 { .sm\:px-6 {
@media (width >= 40rem) { @media (width >= 40rem) {
padding-inline: calc(var(--spacing) * 6); padding-inline: calc(var(--spacing) * 6);
@@ -619,6 +1043,12 @@
line-height: var(--tw-leading, var(--text-2xl--line-height)); line-height: var(--tw-leading, var(--text-2xl--line-height));
} }
} }
.sm\:text-sm {
@media (width >= 40rem) {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
}
}
.md\:mb-8 { .md\:mb-8 {
@media (width >= 48rem) { @media (width >= 48rem) {
margin-bottom: calc(var(--spacing) * 8); margin-bottom: calc(var(--spacing) * 8);
@@ -640,11 +1070,6 @@
line-height: var(--tw-leading, var(--text-8xl--line-height)); line-height: var(--tw-leading, var(--text-8xl--line-height));
} }
} }
.lg\:w-90 {
@media (width >= 64rem) {
width: calc(var(--spacing) * 90);
}
}
.lg\:grid-cols-3 { .lg\:grid-cols-3 {
@media (width >= 64rem) { @media (width >= 64rem) {
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -655,12 +1080,6 @@
padding-inline: calc(var(--spacing) * 8); padding-inline: calc(var(--spacing) * 8);
} }
} }
.lg\:text-6xl {
@media (width >= 64rem) {
font-size: var(--text-6xl);
line-height: var(--tw-leading, var(--text-6xl--line-height));
}
}
.dark\:bg-gray-900 { .dark\:bg-gray-900 {
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
background-color: var(--color-gray-900); background-color: var(--color-gray-900);
@@ -715,25 +1134,6 @@ a.qp-a-button {
} }
} }
} }
a.qp-a-button-small {
border-radius: var(--radius-md);
padding: calc(var(--spacing) * 1);
--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 { .register input {
border-radius: calc(infinity * 1px); border-radius: calc(infinity * 1px);
background-color: var(--color-gray-200); background-color: var(--color-gray-200);
@@ -910,6 +1310,31 @@ a.qp-a-button-small {
scale: var(--tw-scale-x) var(--tw-scale-y); scale: var(--tw-scale-x) var(--tw-scale-y);
} }
} }
@property --tw-rotate-x {
syntax: "*";
inherits: false;
initial-value: rotateX(0);
}
@property --tw-rotate-y {
syntax: "*";
inherits: false;
initial-value: rotateY(0);
}
@property --tw-rotate-z {
syntax: "*";
inherits: false;
initial-value: rotateZ(0);
}
@property --tw-skew-x {
syntax: "*";
inherits: false;
initial-value: skewX(0);
}
@property --tw-skew-y {
syntax: "*";
inherits: false;
initial-value: skewY(0);
}
@property --tw-space-y-reverse { @property --tw-space-y-reverse {
syntax: "*"; syntax: "*";
inherits: false; inherits: false;
@@ -920,6 +1345,11 @@ a.qp-a-button-small {
inherits: false; inherits: false;
initial-value: 0; initial-value: 0;
} }
@property --tw-divide-y-reverse {
syntax: "*";
inherits: false;
initial-value: 0;
}
@property --tw-border-style { @property --tw-border-style {
syntax: "*"; syntax: "*";
inherits: false; inherits: false;
@@ -984,6 +1414,87 @@ a.qp-a-button-small {
inherits: false; inherits: false;
initial-value: 0 0 #0000; initial-value: 0 0 #0000;
} }
@property --tw-outline-style {
syntax: "*";
inherits: false;
initial-value: solid;
}
@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-backdrop-blur {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-brightness {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-contrast {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-grayscale {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-hue-rotate {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-invert {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-opacity {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-saturate {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-sepia {
syntax: "*";
inherits: false;
}
@property --tw-duration { @property --tw-duration {
syntax: "*"; syntax: "*";
inherits: false; inherits: false;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -2,7 +2,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<link rel="icon" type="image/png" href="{% static 'icons/favicon.png' %}">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'homepage/t-style.css' %}"> <link rel="stylesheet" href="{% static 'homepage/t-style.css' %}">

View File

@@ -1,34 +0,0 @@
{% load static %}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Impressum</title>
<link rel="icon" type="image/png" href="{% static 'icons/favicon.png' %}">
<link rel="stylesheet" href="{% static 'homepage/t-style.css' %}">
</head>
<body>
{% include 'homepage/partials/_nav.html' %}
<div class="max-w-screen-lg mx-auto">
{% block content%}{% endblock %}
</div>
<div>
<div>
<h1>Impressum</h1>
</div>
<div>
<dvi>
<p>
Katharineum zu Lübeck
</p>
<p>
Königsstraße 27-31
</p>
<p>
23552 Lübeck
</p>
</div>
{% include 'homepage/partials/_footer.html' %}
</body>
</html>

View File

@@ -1,7 +1,7 @@
<div class="grid place-content-center gap-2 w-full mt-4"> <div class="grid place-content-center gap-2 w-full mt-4">
<div class="flex space-x-2 pb-4"> <div class="flex space-x-2 pb-4">
<p class="text-sm font-extralight"><a href="/impress">Impressum</a></p> <p class="text-sm font-extralight"><a href="#">Impressum</a></p>
<p class="text-sm font-extralight"><a href="/privacy">Datenschutz</a></p> <p class="text-sm font-extralight"><a href="#">Datenschutz</a></p>
<p class="text-sm font-extralight"><a href="https://edugit.org/enrichment-2024/qivip">Repository</a></p> <p class="text-sm font-extralight"><a href="#">Repository</a></p>
</div> </div>
</div> </div>

View File

@@ -1,23 +0,0 @@
{% load static %}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Impressum</title>
<link rel="icon" type="image/png" href="{% static 'icons/favicon.png' %}">
<link rel="stylesheet" href="{% static 'homepage/t-style.css' %}">
</head>
<body>
{% include 'homepage/partials/_nav.html' %}
<div class="max-w-screen-lg mx-auto">
{% block content%}{% endblock %}
<div>
<p>
Hallo, ich bin die Datenschutzerklärung!
</p>
</div>
</div>
{% include 'homepage/partials/_footer.html' %}
</body>
</html>

View File

@@ -5,6 +5,4 @@ app_name = 'homepage' # Verhindert Konflikt mit anderen Apps
urlpatterns = [ urlpatterns = [
path('', views.home, name="home"), path('', views.home, name="home"),
path('impress', views.impress, name="impress"),
path('privacy', views.privacy, name="privacy"),
] ]

View File

@@ -2,8 +2,3 @@ from django.shortcuts import render, redirect
def home(request): def home(request):
return render(request, 'homepage/home.html') return render(request, 'homepage/home.html')
def impress(request):
return render(request, 'homepage/impress.html')
def privacy(request):
return render(request, 'homepage/privacy.html')

View File

@@ -4,7 +4,7 @@ from .models import QivipQuiz, QivipQuestion
class QuizForm(forms.ModelForm): class QuizForm(forms.ModelForm):
class Meta: class Meta:
model = QivipQuiz model = QivipQuiz
fields = ['name', 'description', 'status', 'category', 'tags',"difficulty"] fields = ['name', 'description', 'status', 'category', 'tags']
class QuestionForm(forms.ModelForm): class QuestionForm(forms.ModelForm):
class Meta: class Meta:

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -9,26 +9,16 @@ class QivipQuiz(models.Model):
"hidden": "Versteckt", "hidden": "Versteckt",
"private": "Privat", "private": "Privat",
} }
DIFFICULTY_VALUES = {
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"not defined":"nicht gesetzt",
}
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name='quiz') user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name='quiz')
creation_date = models.DateTimeField(auto_now_add=True) creation_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True) update_date = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=list(STATUS_VALUES.items()), default="public") status = models.CharField(max_length=10, choices=STATUS_VALUES.items())
category = models.ForeignKey('QuizCategory', related_name='quiz', on_delete=models.CASCADE) category = models.ForeignKey('QuizCategory', related_name='quiz', on_delete=models.CASCADE)
tags = models.ManyToManyField('Tag', blank=True) tags = models.ManyToManyField('Tag', blank=True)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True, default="In dem Quiz geht es um ...") description = models.TextField(blank=True, null=True)
difficulty= models.CharField(max_length=11, choices=list(DIFFICULTY_VALUES.items()), default="not defined", help_text="1: niedrigste Schwierigkeit und 5: höchste Schwierigkeit")
def __str__(self): def __str__(self):
return self.name return self.name

View File

@@ -13,5 +13,6 @@
{% block content%}{% endblock %} {% block content%}{% endblock %}
</div> </div>
{% include 'homepage/partials/_footer.html' %} {% include 'homepage/partials/_footer.html' %}
{% block extra_js %}{% endblock %}
</body> </body>
</html> </html>

View File

@@ -1,11 +1,39 @@
{% extends 'library/base.html' %} {% extends 'library/base.html' %}
{% block content %} {% block content %}
<h1>Löschen</h1> <div class="container mx-auto px-4">
<p>Soll das Quiz "{{ object.name }}" wirklich gelöscht werden?</p> <div class="max-w-lg mx-auto mt-10">
<form method="post"> <div class="bg-white rounded-lg shadow-md p-6">
<div class="text-center">
<svg class="mx-auto h-12 w-12 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg>
<h2 class="mt-4 text-xl font-bold text-gray-900">Löschen bestätigen</h2>
<p class="mt-2 text-sm text-gray-600">
{% if object.quiz_id %}
Möchten Sie die Frage "{{ object.data.question }}" wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.
{% else %}
Möchten Sie das Quiz "{{ object.name }}" wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.
{% endif %}
</p>
</div>
<form method="post" class="mt-6">
{% csrf_token %} {% csrf_token %}
<button type="submit">Löschen</button> <div class="flex justify-center space-x-3">
<a href="{% url 'library:overview_quiz' %}">Abbrechen</a> <a href="{% if object.quiz_id %}{% url 'library:detail_quiz' object.quiz_id.id %}{% else %}{% url 'library:overview_quiz' %}{% endif %}"
</form> class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Abbrechen
</a>
<button type="submit"
class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
Löschen
</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@@ -1,19 +1,99 @@
{% extends 'library/base.html' %} {% extends 'library/base.html' %}
{% block content %} {% block content %}
<h1>{{ quiz.name }}</h1> <div class="container mx-auto px-4">
<p>{{ quiz.description }}</p> <div class="flex justify-between items-center mb-6">
<a href="{% url 'library:edit_quiz' quiz.id %}">Bearbeiten</a> <h1 class="text-2xl font-bold">{{ quiz.name }}</h1>
<a href="{% url 'library:delete_quiz' quiz.id %}">Löschen</a> <div class="flex space-x-2">
<a href="{% url 'library:edit_quiz' quiz.id %}" class="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 transition-colors">Bearbeiten</a>
<a href="{% url 'library:delete_quiz' quiz.id %}" class="bg-red-500 text-white px-4 py-2 rounded-md hover:bg-red-600 transition-colors">Löschen</a>
</div>
</div>
<h2>Fragen</h2> {% if quiz.description %}
<ul> <p class="text-gray-600 mb-8">{{ quiz.description }}</p>
<a href="{% url 'library:new_question' %}">Neue Frage erstellen</a> {% endif %}
<div class="bg-white rounded-lg shadow-md">
<div class="border-b border-gray-200 p-4">
<div class="flex justify-between items-center">
<h2 class="text-xl font-semibold">Fragen</h2>
<div class="flex space-x-2">
<a href="{% url 'library:new_question' %}?type=multiple_choice&quiz_id={{ quiz.id }}"
class="bg-green-500 text-white px-4 py-2 rounded-md hover:bg-green-600 transition-colors">
Multiple Choice
</a>
<a href="{% url 'library:new_question' %}?type=true_false&quiz_id={{ quiz.id }}"
class="bg-green-500 text-white px-4 py-2 rounded-md hover:bg-green-600 transition-colors">
Wahr/Falsch
</a>
</div>
</div>
</div>
{% if questions %}
<div class="divide-y divide-gray-200">
{% for question in questions %} {% for question in questions %}
<li>{{ question.data }}</li> <div class="p-4 hover:bg-gray-50">
<a href="{% url 'library:edit_question' question.id %}">Bearbeiten</a> <div class="flex justify-between items-start">
<a href="{% url 'library:delete_question' question.id %}">Löschen</a> <div class="flex-grow">
<p class="font-medium">{{ question.data.question }}</p>
<div class="mt-1">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium {% if question.data.type == 'multiple_choice' %}bg-blue-100 text-blue-800{% else %}bg-purple-100 text-purple-800{% endif %}">
{% if question.data.type == 'multiple_choice' %}Multiple Choice{% else %}Wahr/Falsch{% endif %}
</span>
</div>
<div class="mt-2">
{% if question.data.type == 'multiple_choice' %}
<ul class="space-y-1">
{% for option in question.data.options %}
<li class="flex items-center text-sm">
{% if option.is_correct %}
<svg class="h-4 w-4 text-green-500 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
<span class="font-medium text-green-700">{{ option.value }}</span>
{% else %}
<svg class="h-4 w-4 text-gray-400 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 12H6"/>
</svg>
<span class="text-gray-600">{{ option.value }}</span>
{% endif %}
</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% else %}
<p class="text-sm">
<span class="text-gray-500">Richtige Antwort:</span>
<span class="ml-1 font-medium {% if question.data.correct_answer %}text-green-700{% else %}text-red-700{% endif %}">
{% if question.data.correct_answer %}Wahr{% else %}Falsch{% endif %}
</span>
</p>
{% endif %}
</div>
</div>
<div class="flex space-x-2 ml-4">
<a href="{% url 'library:edit_question' question.id %}"
class="bg-gray-100 text-gray-700 px-3 py-1 rounded hover:bg-gray-200 transition-colors">
Bearbeiten
</a>
<a href="{% url 'library:delete_question' question.id %}"
class="bg-red-100 text-red-700 px-3 py-1 rounded hover:bg-red-200 transition-colors">
Löschen
</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="p-8 text-center">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
</svg>
<p class="mt-2 text-sm text-gray-500">Noch keine Fragen vorhanden. Erstellen Sie eine neue Frage, um loszulegen!</p>
</div>
{% endif %}
</div>
</div>
{% endblock %} {% endblock %}

View File

@@ -9,10 +9,10 @@
{% for quiz in quizzes %} {% for quiz in quizzes %}
<div class="bg-white rounded-lg p-4 shadow-md"> <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> <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 my-8">{{ quiz.description }}</p> <p class="text-sm text-gray-600">{{ quiz.description }}</p>
<a href="#"class="qp-a-button-small bg-green-500">Spiel starten</a> <a href="#">Spiel starten</a>
<button><a href="{% url 'library:edit_quiz' quiz.id %}"class="qp-a-button-small bg-indigo-500">Bearbeiten</a></button> <a href="{% url 'library:edit_quiz' quiz.id %}">Bearbeiten</a>
<a href="{% url 'library:delete_quiz' quiz.id %}" class="qp-a-button-small bg-purple-500">Löschen</a> <a href="{% url 'library:delete_quiz' quiz.id %}">Löschen</a>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>

View File

@@ -0,0 +1,164 @@
{% extends 'library/base.html' %}
{% block content %}
<div class="container mx-auto px-4">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold">{% if question %}Frage bearbeiten{% else %}Neue Multiple Choice Frage{% endif %}</h1>
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="bg-gray-100 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-200 transition-colors">
Zurück zum Quiz
</a>
</div>
<div class="bg-white rounded-lg shadow-md p-6">
<form method="post" class="space-y-6">
{% csrf_token %}
<!-- Question Text -->
<div>
<label for="question" class="block text-sm font-medium text-gray-700">Frage</label>
<div class="mt-1">
<input type="text" name="question" id="question"
class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md"
value="{{ question.data.question|default:'' }}" required>
</div>
</div>
<!-- Options -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Antwortmöglichkeiten</label>
<div id="options-container" class="space-y-3">
{% if question and question.data.options %}
{% for option in question.data.options %}
<div class="flex items-center space-x-3">
<input type="text" name="option_{{ forloop.counter }}"
class="shadow-sm focus:ring-blue-500 focus:border-blue-500 flex-grow sm:text-sm border-gray-300 rounded-md"
value="{{ option.value }}" required>
<label class="inline-flex items-center">
<input type="checkbox" name="correct_{{ forloop.counter }}" value="1"
class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded"
{% if option.is_correct %}checked{% endif %}>
<span class="ml-2 text-sm text-gray-600">Richtig</span>
</label>
<button type="button" onclick="removeOption(this)"
class="text-red-600 hover:text-red-800 transition-colors">
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
</button>
</div>
{% endfor %}
{% else %}
<div class="flex items-center space-x-3">
<input type="text" name="option_1"
class="shadow-sm focus:ring-blue-500 focus:border-blue-500 flex-grow sm:text-sm border-gray-300 rounded-md"
required>
<label class="inline-flex items-center">
<input type="checkbox" name="correct_1" value="1"
class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded">
<span class="ml-2 text-sm text-gray-600">Richtig</span>
</label>
<button type="button" onclick="removeOption(this)"
class="text-red-600 hover:text-red-800 transition-colors">
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
</button>
</div>
<div class="flex items-center space-x-3">
<input type="text" name="option_2"
class="shadow-sm focus:ring-blue-500 focus:border-blue-500 flex-grow sm:text-sm border-gray-300 rounded-md"
required>
<label class="inline-flex items-center">
<input type="checkbox" name="correct_2" value="1"
class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded">
<span class="ml-2 text-sm text-gray-600">Richtig</span>
</label>
<button type="button" onclick="removeOption(this)"
class="text-red-600 hover:text-red-800 transition-colors">
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
</button>
</div>
{% endif %}
</div>
<button type="button" onclick="addOption()"
class="mt-3 inline-flex items-center px-3 py-1.5 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<svg class="h-4 w-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
Option hinzufügen
</button>
</div>
<div class="flex justify-end space-x-3">
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Abbrechen
</a>
<button type="submit"
class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
{% if question %}Speichern{% else %}Erstellen{% endif %}
</button>
</div>
</form>
</div>
</div>
<script>
function addOption() {
const container = document.getElementById('options-container');
const optionCount = container.children.length + 1;
if (optionCount > 6) {
alert('Maximal 6 Optionen sind erlaubt.');
return;
}
const div = document.createElement('div');
div.className = 'flex items-center space-x-3';
div.innerHTML = `
<input type="text" name="option_${optionCount}"
class="shadow-sm focus:ring-blue-500 focus:border-blue-500 flex-grow sm:text-sm border-gray-300 rounded-md"
required>
<label class="inline-flex items-center">
<input type="checkbox" name="correct_${optionCount}" value="1"
class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded">
<span class="ml-2 text-sm text-gray-600">Richtig</span>
</label>
<button type="button" onclick="removeOption(this)"
class="text-red-600 hover:text-red-800 transition-colors">
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
</button>
`;
container.appendChild(div);
}
function removeOption(button) {
const container = document.getElementById('options-container');
if (container.children.length > 2) {
button.parentElement.remove();
updateOptionNumbers();
} else {
alert('Mindestens zwei Optionen müssen vorhanden sein.');
}
}
function updateOptionNumbers() {
const container = document.getElementById('options-container');
const options = container.children;
Array.from(options).forEach((option, index) => {
const number = index + 1;
const textInput = option.querySelector('input[type="text"]');
const checkbox = option.querySelector('input[type="checkbox"]');
textInput.name = `option_${number}`;
checkbox.name = `correct_${number}`;
});
}
</script>
{% endblock %}

View File

@@ -0,0 +1,59 @@
{% extends 'library/base.html' %}
{% block content %}
<div class="container mx-auto px-4">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold">{% if question %}Frage bearbeiten{% else %}Neue Wahr/Falsch Frage{% endif %}</h1>
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="bg-gray-100 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-200 transition-colors">
Zurück zum Quiz
</a>
</div>
<div class="bg-white rounded-lg shadow-md p-6">
<form method="post" class="space-y-6">
{% csrf_token %}
<!-- Question Text -->
<div>
<label for="question" class="block text-sm font-medium text-gray-700">Frage</label>
<div class="mt-1">
<input type="text" name="question" id="question"
class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md"
value="{{ question.question|default:'' }}" required>
</div>
</div>
<!-- True/False Selection -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Richtige Antwort</label>
<div class="flex space-x-4">
<label class="inline-flex items-center">
<input type="radio" name="correct_answer" value="true"
class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300"
{% if question.correct_answer %}checked{% endif %}>
<span class="ml-2 text-sm text-gray-600">Wahr</span>
</label>
<label class="inline-flex items-center">
<input type="radio" name="correct_answer" value="false"
class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300"
{% if not question.correct_answer %}checked{% endif %}>
<span class="ml-2 text-sm text-gray-600">Falsch</span>
</label>
</div>
</div>
<div class="flex justify-end space-x-3">
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Abbrechen
</a>
<button type="submit"
class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
{% if question %}Speichern{% else %}Erstellen{% endif %}
</button>
</div>
</form>
</div>
</div>
{% endblock %}

View File

@@ -1,8 +1,11 @@
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.urls import reverse
from django.contrib import messages
from .models import QivipQuiz, QivipQuestion from .models import QivipQuiz, QivipQuestion
from .forms import QuizForm, QuestionForm from .forms import QuizForm, QuestionForm
import json
# Übersicht aller Quizze # Übersicht aller Quizze
@login_required @login_required
@@ -52,47 +55,202 @@ def delete_quiz(request, pk):
def detail_quiz(request, pk): def detail_quiz(request, pk):
quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user) quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user)
questions = QivipQuestion.objects.filter(quiz_id=quiz) questions = QivipQuestion.objects.filter(quiz_id=quiz)
return render(request, 'library/detail_quiz.html', {'quiz': quiz, 'questions': questions})
# Parse JSON data for each question
for question in questions:
if question.data:
question.data = json.loads(question.data)
context = {
'quiz': quiz,
'questions': questions
}
return render(request, 'library/detail_quiz.html', context)
# Übersicht aller Fragen # Übersicht aller Fragen
@login_required @login_required
def question_list(request): def question_list(request):
questions = QivipQuestion.objects.filter(quiz_id__user_id=request.user) questions = QivipQuestion.objects.filter(quiz_id__user_id=request.user)
# Parse JSON data for each question
for question in questions:
if question.data:
question.data = json.loads(question.data)
return render(request, 'library/question_list.html', {'questions': questions}) return render(request, 'library/question_list.html', {'questions': questions})
# Neue Frage erstellen # Neue Frage erstellen
@login_required @login_required
def new_question(request): def new_question(request):
question_type = request.GET.get('type')
quiz_id = request.GET.get('quiz_id')
if not quiz_id:
messages.error(request, 'Quiz ID muss angegeben werden.')
return redirect('library:overview_quiz')
try:
quiz = QivipQuiz.objects.get(pk=quiz_id, user_id=request.user)
except QivipQuiz.DoesNotExist:
messages.error(request, 'Quiz nicht gefunden oder keine Berechtigung.')
return redirect('library:overview_quiz')
if question_type not in ['multiple_choice', 'true_false']:
base_url = reverse('library:new_question')
return redirect(f'{base_url}?type=multiple_choice&quiz_id={quiz_id}')
if request.method == 'POST': if request.method == 'POST':
form = QuestionForm(request.POST) question_text = request.POST.get('question')
if form.is_valid():
question = form.save(commit=False) # Handle different question types
# Prüfe ob der User Zugriff auf das Quiz hat if question_type == 'true_false':
if question.quiz_id.user_id != request.user: correct_answer = request.POST.get('correct_answer') == 'true'
return redirect('library:question_list') json_data = {
'type': question_type,
'question': question_text,
'options': [
{'value': 'Wahr', 'is_correct': correct_answer},
{'value': 'Falsch', 'is_correct': not correct_answer}
]
}
elif question_type == 'multiple_choice':
options = []
i = 1
# Limit to maximum 6 options
while request.POST.get(f'option_{i}') and i <= 6:
is_correct = request.POST.get(f'correct_{i}') == '1'
options.append({
'order': i,
'value': request.POST.get(f'option_{i}'),
'is_correct': is_correct
})
i += 1
# Validate minimum 2 options
if len(options) < 2:
messages.error(request, 'Mindestens zwei Optionen müssen angegeben werden.')
return redirect(request.get_full_path())
json_data = {
'type': question_type,
'question': question_text,
'options': options
}
question = QivipQuestion()
question.data = json.dumps(json_data)
question.quiz_id = quiz
question.type = question_type
question.question = question_text
question.save() question.save()
return redirect('library:edit_question', pk=question.pk) return redirect('library:detail_quiz', pk=quiz.pk)
else: else:
form = QuestionForm() # Initialize empty question data for new questions
return render(request, 'library/form.html', {'form': form}) question_data = {
'type': question_type,
'question': '',
'options': []
}
if question_type == 'true_false':
question_data['options'] = [
{'value': 'Wahr', 'is_correct': True},
{'value': 'Falsch', 'is_correct': False}
]
elif question_type == 'multiple_choice':
question_data['options'] = [
{'value': '', 'is_correct': False},
{'value': '', 'is_correct': False}
]
template_name = f'library/question/question_{question_type}.html'
context = {
'question': question_data,
'quiz_id': quiz_id,
'quiz': quiz
}
return render(request, template_name, context)
# Frage bearbeiten # Frage bearbeiten
@login_required @login_required
def edit_question(request, pk): def edit_question(request, pk):
question = get_object_or_404(QivipQuestion, pk=pk, quiz_id__user_id=request.user) question = get_object_or_404(QivipQuestion, pk=pk, quiz_id__user_id=request.user)
if question.quiz_id.user_id != request.user:
return redirect('library:question_list')
# Parse the existing JSON data
question_data = json.loads(question.data) if question.data else {}
question_type = question_data.get('type', 'multiple_choice')
if request.method == 'POST': if request.method == 'POST':
form = QuestionForm(request.POST, instance=question) question_text = request.POST.get('question')
if form.is_valid():
form.save() # Handle different question types
return redirect('library:edit_question', pk=pk) if question_type == 'true_false':
correct_answer = request.POST.get('correct_answer') == 'true'
json_data = {
'type': question_type,
'question': question_text,
'correct_answer': correct_answer,
'options': [
{'value': 'Wahr', 'is_correct': correct_answer},
{'value': 'Falsch', 'is_correct': not correct_answer}
]
}
elif question_type == 'multiple_choice':
options = []
i = 1
# Limit to maximum 6 options
while request.POST.get(f'option_{i}') and i <= 6:
options.append({
'order': i,
'value': request.POST.get(f'option_{i}'),
'is_correct': int(request.POST.get(f'correct_{i}', 0))
})
i += 1
# Validate minimum 2 options
if len(options) < 2:
messages.error(request, 'Mindestens zwei Optionen müssen angegeben werden.')
return redirect(request.get_full_path())
# Validate maximum 6 options
if len(options) > 6:
messages.error(request, 'Maximal 6 Optionen sind erlaubt.')
return redirect(request.get_full_path())
json_data = {
'type': question_type,
'question': question_text,
'options': options
}
question.question = question_text
question.type = question_type
question.data = json.dumps(json_data)
question.save()
return redirect('library:detail_quiz', pk=question.quiz_id.pk)
else: else:
form = QuestionForm(instance=question) question_data = json.loads(question.data) if question.data else {}
return render(request, 'library/form.html', {'form': form})
template_name = f'library/question/question_{question_type}.html'
context = {
'question': question_data,
'quiz_id': question.quiz_id.pk,
'quiz': question.quiz_id
}
return render(request, template_name, context)
# Frage löschen # Frage löschen
@login_required @login_required
def delete_question(request, pk): def delete_question(request, pk):
question = get_object_or_404(QivipQuestion, pk=pk, quiz_id__user_id=request.user) question = get_object_or_404(QivipQuestion, pk=pk, quiz_id__user_id=request.user)
# Parse JSON data for question
if question.data:
question.data = json.loads(question.data)
if request.method == 'POST': if request.method == 'POST':
question.delete() question.delete()
return redirect('library:detail_quiz', pk=question.quiz_id.pk) return redirect('library:detail_quiz', pk=question.quiz_id.pk)