code under construction
This commit is contained in:
parent
fb848b9244
commit
02341dccee
26 changed files with 820 additions and 1 deletions
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
azube/__init__.py
Normal file → Executable file
0
azube/__init__.py
Normal file → Executable file
0
azube/asgi.py
Normal file → Executable file
0
azube/asgi.py
Normal file → Executable file
1
azube/settings.py
Normal file → Executable file
1
azube/settings.py
Normal file → Executable file
|
@ -37,6 +37,7 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
"core"
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
3
azube/urls.py
Normal file → Executable file
3
azube/urls.py
Normal file → Executable file
|
@ -15,8 +15,9 @@ Including another URLconf
|
||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path
|
from django.urls import path, include
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
path('', include('core.urls'))
|
||||||
]
|
]
|
||||||
|
|
0
azube/wsgi.py
Normal file → Executable file
0
azube/wsgi.py
Normal file → Executable file
0
core/__init__.py
Executable file
0
core/__init__.py
Executable file
3
core/admin.py
Executable file
3
core/admin.py
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
core/apps.py
Executable file
6
core/apps.py
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MainAppConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'core'
|
28
core/azure_auth.py
Executable file
28
core/azure_auth.py
Executable file
|
@ -0,0 +1,28 @@
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
|
||||||
|
class AzureUser:
|
||||||
|
display_name: str
|
||||||
|
id: str
|
||||||
|
claims: list
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
try:
|
||||||
|
# X-MS-CLIENT-PRINCIPAL-ID
|
||||||
|
self.id = request.headers["X-MS-CLIENT-PRINCIPAL-ID"]
|
||||||
|
|
||||||
|
# X-MS-CLIENT-PRINCIPAL-NAME
|
||||||
|
self.id = request.headers["X-MS-CLIENT-PRINCIPAL-NAME"]
|
||||||
|
|
||||||
|
# X-MS-CLIENT-PRINCIPAL
|
||||||
|
claims_json = json.loads(base64.decode(request.headers["X-MS-CLIENT-PRINCIPAL"]))
|
||||||
|
claims = {}
|
||||||
|
|
||||||
|
for claim in claims_json["claims"]:
|
||||||
|
claims[claim["typ"]] = claim["val"]
|
||||||
|
|
||||||
|
self.claims = claims
|
||||||
|
except:
|
||||||
|
self.display_name = "Anon"
|
||||||
|
self.id = "anon"
|
||||||
|
|
11
core/doc_gen.py
Executable file
11
core/doc_gen.py
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
def gen_doc(template: str, vars: dict) -> str:
|
||||||
|
definition = json.loads(open(f"{template}.json").read())
|
||||||
|
content = open(f"{template}.html").read()
|
||||||
|
|
||||||
|
for var in definition["vars"]:
|
||||||
|
var_r = var["name"].upper()
|
||||||
|
content = content.replace(f"[[{var_r}]]", vars[var["name"]])
|
||||||
|
|
||||||
|
return content
|
29
core/migrations/0001_initial.py
Executable file
29
core/migrations/0001_initial.py
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
# Generated by Django 4.2.16 on 2024-12-02 12:55
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Berichtsheft',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('year', models.PositiveIntegerField()),
|
||||||
|
('week', models.PositiveSmallIntegerField()),
|
||||||
|
('content', models.JSONField()),
|
||||||
|
('approved_by', models.JSONField()),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='berichtshefte', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
18
core/migrations/0002_alter_berichtsheft_id.py
Executable file
18
core/migrations/0002_alter_berichtsheft_id.py
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2.16 on 2024-12-02 13:17
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='berichtsheft',
|
||||||
|
name='id',
|
||||||
|
field=models.AutoField(primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
]
|
63
core/migrations/0003_group_azureuser_alter_berichtsheft_user.py
Executable file
63
core/migrations/0003_group_azureuser_alter_berichtsheft_user.py
Executable file
|
@ -0,0 +1,63 @@
|
||||||
|
# Generated by Django 4.2.16 on 2024-12-02 14:28
|
||||||
|
|
||||||
|
import django.contrib.auth.models
|
||||||
|
import django.contrib.auth.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
('core', '0002_alter_berichtsheft_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Group',
|
||||||
|
fields=[
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'proxy': True,
|
||||||
|
'indexes': [],
|
||||||
|
'constraints': [],
|
||||||
|
},
|
||||||
|
bases=('auth.group',),
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.GroupManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AzureUser',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||||
|
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||||
|
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||||
|
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||||
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('groups', models.ManyToManyField(blank=True, related_name='azure_user_groups', to='auth.group')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, related_name='azure_user_permissions', to='auth.permission')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'user',
|
||||||
|
'verbose_name_plural': 'users',
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='berichtsheft',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='berichtshefte', to='core.azureuser'),
|
||||||
|
),
|
||||||
|
]
|
17
core/migrations/0004_remove_berichtsheft_approved_by.py
Executable file
17
core/migrations/0004_remove_berichtsheft_approved_by.py
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 4.2.16 on 2024-12-02 14:33
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0003_group_azureuser_alter_berichtsheft_user'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='berichtsheft',
|
||||||
|
name='approved_by',
|
||||||
|
),
|
||||||
|
]
|
51
core/migrations/0005_user_alter_berichtsheft_user_delete_azureuser.py
Executable file
51
core/migrations/0005_user_alter_berichtsheft_user_delete_azureuser.py
Executable file
|
@ -0,0 +1,51 @@
|
||||||
|
# Generated by Django 4.2.16 on 2024-12-02 14:46
|
||||||
|
|
||||||
|
import django.contrib.auth.models
|
||||||
|
import django.contrib.auth.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
('core', '0004_remove_berichtsheft_approved_by'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='User',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||||
|
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||||
|
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||||
|
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||||
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('groups', models.ManyToManyField(blank=True, related_name='user_groups', to='auth.group')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, related_name='user_permissions', to='auth.permission')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'user',
|
||||||
|
'verbose_name_plural': 'users',
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='berichtsheft',
|
||||||
|
name='user',
|
||||||
|
field=models.TextField(),
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='AzureUser',
|
||||||
|
),
|
||||||
|
]
|
0
core/migrations/__init__.py
Executable file
0
core/migrations/__init__.py
Executable file
36
core/models.py
Executable file
36
core/models.py
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import AbstractUser
|
||||||
|
from django.contrib.auth.models import Group as BuiltinGroup
|
||||||
|
|
||||||
|
class User(AbstractUser):
|
||||||
|
groups = models.ManyToManyField(
|
||||||
|
'auth.Group',
|
||||||
|
related_name='user_groups',
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
user_permissions = models.ManyToManyField(
|
||||||
|
'auth.Permission',
|
||||||
|
related_name='user_permissions',
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Group(BuiltinGroup):
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
||||||
|
|
||||||
|
class Berichtsheft(models.Model):
|
||||||
|
id = models.AutoField(primary_key=True)
|
||||||
|
user = models.TextField()
|
||||||
|
year = models.PositiveIntegerField()
|
||||||
|
week = models.PositiveSmallIntegerField()
|
||||||
|
content = models.JSONField()
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Berichtsheft: {self.user.username}, Year: {self.year}, Week: {self.week}"
|
||||||
|
|
||||||
|
class Approval:
|
||||||
|
id = models.AutoField(primary_key=True)
|
||||||
|
user = models.TextField()
|
||||||
|
report = models.ForeignKey(Berichtsheft, on_delete=models.CASCADE, related_name="report")
|
||||||
|
|
34
core/templates/index.html
Executable file
34
core/templates/index.html
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Azube</title>
|
||||||
|
<style>
|
||||||
|
.grid-container {
|
||||||
|
display: inline-grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{ user.display_name }}
|
||||||
|
|
||||||
|
<h1> Deine Berichtshefte </h1>
|
||||||
|
|
||||||
|
{% if reports|length == 0 %}
|
||||||
|
<button onclick="location = '/write'">
|
||||||
|
Füge dein erstes Berichtsheft hinzu!
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for report in reports %}
|
||||||
|
<div class="grid-container">
|
||||||
|
<p> {{ report }} </p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
384
core/templates/test.html
Executable file
384
core/templates/test.html
Executable file
|
@ -0,0 +1,384 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> Test </title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Modal-Elemente holen
|
||||||
|
const modal = document.getElementById('berichtModal');
|
||||||
|
const btnNew = document.querySelector('.btn-new');
|
||||||
|
const btnClose = document.querySelector('.btn-close');
|
||||||
|
const btnSave = document.querySelector('.btn-save');
|
||||||
|
const titelInput = document.getElementById('titel');
|
||||||
|
const textInput = document.getElementById('text');
|
||||||
|
|
||||||
|
// Funktion, um Sterne zu erzeugen
|
||||||
|
function createStars() {
|
||||||
|
const starContainer = document.createElement('div');
|
||||||
|
starContainer.classList.add('stars'); // Container für Sterne
|
||||||
|
document.body.appendChild(starContainer); // Sterncontainer zum Body hinzufügen
|
||||||
|
|
||||||
|
// Anzahl der Sterne, die du erzeugen möchtest
|
||||||
|
const numStars = 50;
|
||||||
|
|
||||||
|
// Zufällige Sterne erzeugen
|
||||||
|
for (let i = 0; i < numStars; i++) {
|
||||||
|
const star = document.createElement('div');
|
||||||
|
star.classList.add('star');
|
||||||
|
|
||||||
|
// Zufällige Positionen und Größen für Sterne
|
||||||
|
const size = Math.random() * 3 + 1; // Größe zwischen 1 und 4px
|
||||||
|
star.style.width = `${size}px`;
|
||||||
|
star.style.height = `${size}px`;
|
||||||
|
|
||||||
|
// Zufällige Position
|
||||||
|
const x = Math.random() * window.innerWidth; // Zufällige horizontale Position
|
||||||
|
const y = Math.random() * window.innerHeight; // Zufällige vertikale Position
|
||||||
|
star.style.left = `${x}px`;
|
||||||
|
star.style.top = `${y}px`;
|
||||||
|
|
||||||
|
// Zufällige Verzögerung für den Twinkle-Effekt
|
||||||
|
const delay = Math.random() * 2; // Zufällige Verzögerung zwischen 0 und 2s
|
||||||
|
star.style.animationDelay = `${delay}s`;
|
||||||
|
|
||||||
|
starContainer.appendChild(star); // Stern zum Container hinzufügen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Entfernen der Sterne
|
||||||
|
function removeStars() {
|
||||||
|
const starContainer = document.querySelector('.stars');
|
||||||
|
if (starContainer) {
|
||||||
|
starContainer.remove(); // Entfernt den Container mit den Sternen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Öffnen des Modals
|
||||||
|
function openModal() {
|
||||||
|
modal.classList.add('show'); // Modal anzeigen mit 'show' Klasse
|
||||||
|
document.body.classList.add('modal-open'); // Hintergrund unscharf machen
|
||||||
|
createStars(); // Sterne erzeugen, wenn das Modal geöffnet wird
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Schließen des Modals
|
||||||
|
function closeModal() {
|
||||||
|
modal.classList.remove('show'); // Modal ausblenden durch Entfernen der 'show' Klasse
|
||||||
|
document.body.classList.remove('modal-open'); // Hintergrund zurücksetzen
|
||||||
|
removeStars(); // Sterne entfernen, wenn das Modal geschlossen wird
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Speichern eines neuen Berichtshefts
|
||||||
|
function saveReport() {
|
||||||
|
const titel = titelInput.value;
|
||||||
|
const text = textInput.value;
|
||||||
|
|
||||||
|
if (titel && text) {
|
||||||
|
// Hier kannst du das neue Berichtsheft speichern (z.B. in LocalStorage oder in einer Datenbank)
|
||||||
|
alert(`Berichtsheft gespeichert!\nTitel: ${titel}\nText: ${text}`);
|
||||||
|
|
||||||
|
// Modal schließen nach dem Speichern
|
||||||
|
closeModal();
|
||||||
|
|
||||||
|
// Eingabefelder zurücksetzen
|
||||||
|
titelInput.value = '';
|
||||||
|
textInput.value = '';
|
||||||
|
} else {
|
||||||
|
alert('Bitte fülle alle Felder aus!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event-Listener für das Öffnen des Modals
|
||||||
|
btnNew.addEventListener('click', openModal);
|
||||||
|
|
||||||
|
// Event-Listener für das Schließen des Modals
|
||||||
|
btnClose.addEventListener('click', closeModal);
|
||||||
|
|
||||||
|
// Event-Listener für das Speichern des Berichtshefts
|
||||||
|
btnSave.addEventListener('click', saveReport);
|
||||||
|
|
||||||
|
// Wenn der Benutzer außerhalb des Modals klickt, soll es ebenfalls geschlossen werden
|
||||||
|
window.addEventListener('click', function(event) {
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mausbewegung über das Grid für 3D-Rotation
|
||||||
|
const reportElements = document.querySelectorAll('.bericht');
|
||||||
|
|
||||||
|
function handleMouseMove(event) {
|
||||||
|
reportElements.forEach((element) => {
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
const centerX = rect.left + rect.width / 2;
|
||||||
|
const centerY = rect.top + rect.height / 2;
|
||||||
|
|
||||||
|
const deltaX = (event.clientX - centerX) / rect.width;
|
||||||
|
const deltaY = (event.clientY - centerY) / rect.height;
|
||||||
|
|
||||||
|
const rotateX = deltaY * 15;
|
||||||
|
const rotateY = deltaX * -25;
|
||||||
|
const moveX = deltaX * 40;
|
||||||
|
|
||||||
|
// Nur das angeklickte Element wird rotiert und skaliert
|
||||||
|
if (element.matches(':hover')) {
|
||||||
|
element.style.transform = `scale(1.05) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
|
||||||
|
element.style.zIndex = 10; // Erhöht den Stapelwert des Elements, um es hervorzuheben
|
||||||
|
} else {
|
||||||
|
element.style.transform = 'scale(1)'; // Andere Elemente bleiben unverändert
|
||||||
|
element.style.zIndex = 1; // Standardstapelwert
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event-Listener für Mausbewegung
|
||||||
|
document.querySelector('.container').addEventListener('mousemove', handleMouseMove);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #333;
|
||||||
|
color: white;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
perspective: 1500px; /* 3D-Effekt */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stil für die Berichtselemente */
|
||||||
|
.bericht {
|
||||||
|
background-color: white;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||||
|
position: relative;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-new {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-new {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
font-size: 30px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s, transform 0.2s;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-new:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal für neues Berichtsheft */
|
||||||
|
.modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.show {
|
||||||
|
display: flex;
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 1000; /* Modal über anderen Inhalten */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hintergrundunschärfe beim Öffnen des Modals */
|
||||||
|
.modal.show::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
filter: blur(10px); /* Hintergrund unscharf */
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal-Inhalt */
|
||||||
|
.modal-content {
|
||||||
|
background-color: white;
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 500px;
|
||||||
|
max-width: 90%;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
transform: scale(1.05); /* Kleinere Zoom-Animation */
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.modal-open {
|
||||||
|
filter: blur(5px); /* Unschärfe des Hintergrunds */
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal bleibt scharf und im Vordergrund */
|
||||||
|
.modal.show {
|
||||||
|
z-index: 100; /* Stellt sicher, dass das Modal im Vordergrund ist */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Eingabefelder im Modal */
|
||||||
|
.modal input,
|
||||||
|
.modal textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons im Modal */
|
||||||
|
.modal-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal button {
|
||||||
|
padding: 10px 15px;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Schließen-Button im Modal */
|
||||||
|
.btn-close {
|
||||||
|
background-color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Speichern-Button im Modal */
|
||||||
|
.btn-save {
|
||||||
|
background-color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-save:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation für die Sterne */
|
||||||
|
.stars {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star {
|
||||||
|
position: absolute;
|
||||||
|
width: 3px;
|
||||||
|
height: 3px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
opacity: 0;
|
||||||
|
animation: star-animation 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sterneffekt im Hintergrund */
|
||||||
|
body.modal-open .stars {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 50; /* Überlagert den Hintergrund, aber unter dem Modal */
|
||||||
|
pointer-events: none; /* Verhindert Interaktionen mit den Sternen */
|
||||||
|
}
|
||||||
|
|
||||||
|
.star {
|
||||||
|
position: absolute;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: twinkle 2s infinite alternate;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes twinkle {
|
||||||
|
0% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal-Styling */
|
||||||
|
#berichtModal {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 100; /* Modal ganz oben */
|
||||||
|
background-color: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 400px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: white;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-open #berichtModal {
|
||||||
|
z-index: 1000; /* Sicherstellen, dass das Modal immer oben bleibt */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h1> Deine Berichtshefte </h1>
|
||||||
|
|
||||||
|
<div class="modal-content stars">
|
||||||
|
<p class="bericht" > Berichtsheft 1 </p>
|
||||||
|
</div>
|
||||||
|
<button class="modal"> Add new I guess </button>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
34
core/templates/write.html
Executable file
34
core/templates/write.html
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> Neues Berichtsheft </title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1> Neues Berichtsheft </h1>
|
||||||
|
|
||||||
|
<p> Berichtsheft {{ year }} / {{ week }} </p>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{% for var in definition.vars %}
|
||||||
|
<div>
|
||||||
|
<label for="{{ var.name }}"> {{ var.display_name }}:</label>
|
||||||
|
<input type="text" id="{{ var.name }}" name="{{ var.name }}"
|
||||||
|
|
||||||
|
{% if var.name == "week" %}
|
||||||
|
value = "{{ week }}"
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if var.name == "year" %}
|
||||||
|
value = "{{ year }}"
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<button> Submit </button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
3
core/tests.py
Executable file
3
core/tests.py
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
8
core/urls.py
Executable file
8
core/urls.py
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.index, name='index'),
|
||||||
|
path('write', views.write_new_report, name='write'),
|
||||||
|
path("test", views.test, name="test")
|
||||||
|
]
|
47
core/views.py
Executable file
47
core/views.py
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
from .azure_auth import AzureUser
|
||||||
|
from .models import Berichtsheft
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
|
||||||
|
def next_date(year: int, week: int) -> (int, int):
|
||||||
|
if week >= 52:
|
||||||
|
return (year + 1, 1)
|
||||||
|
else:
|
||||||
|
return (year, week + 1)
|
||||||
|
|
||||||
|
def write_new_report(request):
|
||||||
|
user = AzureUser(request)
|
||||||
|
|
||||||
|
# TODO : Get template for user
|
||||||
|
definition = json.loads(open("report_weekly.json").read())
|
||||||
|
|
||||||
|
# Get the latest year and week
|
||||||
|
latest = Berichtsheft.objects.filter(user=user).order_by('-year', '-week').first()
|
||||||
|
if latest is not None:
|
||||||
|
current_year, current_week = next_date(latest.year, latest.week)
|
||||||
|
else:
|
||||||
|
current_year, current_week, _ = datetime.datetime.today().isocalendar()
|
||||||
|
|
||||||
|
# Get cookies for persistent saves
|
||||||
|
|
||||||
|
|
||||||
|
return render(request, "write.html", {
|
||||||
|
"user": user,
|
||||||
|
"year": current_year,
|
||||||
|
"week": current_week,
|
||||||
|
"definition": definition
|
||||||
|
})
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
user = AzureUser(request)
|
||||||
|
|
||||||
|
# Get all berichtshefte
|
||||||
|
all_reports = Berichtsheft.objects.filter(user=user)
|
||||||
|
|
||||||
|
return render(request, "index.html", {"user": user, "reports": all_reports})
|
||||||
|
|
||||||
|
def test(request):
|
||||||
|
return render(request, "test.html", {})
|
44
report_weekly.json
Executable file
44
report_weekly.json
Executable file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"vars": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"display_name": "Name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "num_doc",
|
||||||
|
"display_name": "Berichtsheft Nummer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "year",
|
||||||
|
"display_name": "Jahr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "week",
|
||||||
|
"display_name": "Kalenderwoche"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "start_date",
|
||||||
|
"display_name": "Von"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "end_date",
|
||||||
|
"display_name": "bis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "department",
|
||||||
|
"display_name": "Abteilung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "company_text",
|
||||||
|
"display_name": "Betriebliche Tätigkeiten"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "week_topic",
|
||||||
|
"display_name": "Thema der Woche"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "school_text",
|
||||||
|
"display_name": "Berufsschule"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1
requirements.txt
Normal file → Executable file
1
requirements.txt
Normal file → Executable file
|
@ -1 +1,2 @@
|
||||||
Django~=4.2.11
|
Django~=4.2.11
|
||||||
|
psycopg2
|
Loading…
Add table
Reference in a new issue