work + report draft

This commit is contained in:
JMARyA 2024-12-05 13:45:51 +01:00
parent 43fe9aea31
commit 7a70163e76
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
42 changed files with 228 additions and 19 deletions

0
.gitignore vendored Executable file → Normal file
View file

0
azube/__init__.py Executable file → Normal file
View file

0
azube/asgi.py Executable file → Normal file
View file

0
azube/settings.py Executable file → Normal file
View file

0
azube/urls.py Executable file → Normal file
View file

0
azube/wsgi.py Executable file → Normal file
View file

0
core/__init__.py Executable file → Normal file
View file

0
core/admin.py Executable file → Normal file
View file

0
core/apps.py Executable file → Normal file
View file

0
core/azure_auth.py Executable file → Normal file
View file

0
core/doc_gen.py Executable file → Normal file
View file

1
core/migrations/0001_initial.py Executable file → Normal file
View file

@ -6,7 +6,6 @@ import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [

1
core/migrations/0002_alter_berichtsheft_id.py Executable file → Normal file
View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0001_initial"),
]

View file

@ -8,7 +8,6 @@ import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
("core", "0002_alter_berichtsheft_id"),

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0003_group_azureuser_alter_berichtsheft_user"),
]

View file

@ -7,7 +7,6 @@ import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
("core", "0004_remove_berichtsheft_approved_by"),

1
core/migrations/0006_berichtsheft_num.py Executable file → Normal file
View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0005_user_alter_berichtsheft_user_delete_azureuser"),
]

1
core/migrations/0007_approval.py Executable file → Normal file
View file

@ -5,7 +5,6 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("core", "0006_berichtsheft_num"),
]

1
core/migrations/0008_berichtsheft_needs_rewrite.py Executable file → Normal file
View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0007_approval"),
]

1
core/migrations/0009_berichtsheft_kind.py Executable file → Normal file
View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0008_berichtsheft_needs_rewrite"),
]

1
core/migrations/0010_berichtsheft_department.py Executable file → Normal file
View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0009_berichtsheft_kind"),
]

View file

@ -0,0 +1,20 @@
# Generated by Django 4.2.17 on 2024-12-05 10:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0010_berichtsheft_department"),
]
operations = [
migrations.CreateModel(
name="BerichtsheftDraft",
fields=[
("user", models.TextField(primary_key=True, serialize=False)),
("department", models.CharField(default="", max_length=160)),
("content", models.JSONField()),
],
),
]

0
core/migrations/__init__.py Executable file → Normal file
View file

6
core/models.py Executable file → Normal file
View file

@ -55,3 +55,9 @@ class Approval(models.Model):
report = models.ForeignKey(
Berichtsheft, on_delete=models.CASCADE, related_name="report"
)
class BerichtsheftDraft(models.Model):
user = models.TextField(primary_key=True)
department = models.CharField(max_length=160, default="")
content = models.JSONField()

4
core/reports.py Executable file → Normal file
View file

@ -20,7 +20,7 @@ class WeeklyReport(forms.Form):
widget=forms.Textarea(
attrs={
"class": "w-full p-2 border border-gray-300 rounded-lg focus:ring focus:ring-blue-400 mb-5",
"rows": 10,
"rows": 5,
"placeholder": "Betriebliche Tätigkeiten",
}
),
@ -31,7 +31,7 @@ class WeeklyReport(forms.Form):
widget=forms.Textarea(
attrs={
"class": "w-full p-2 border border-gray-300 rounded-lg focus:ring focus:ring-blue-400 mb-5",
"rows": 8,
"rows": 10,
"placeholder": "Thema der Woche",
}
),

0
core/styles.py Executable file → Normal file
View file

0
core/templates/component/report.html Executable file → Normal file
View file

0
core/templates/head.html Executable file → Normal file
View file

2
core/templates/header.html Executable file → Normal file
View file

@ -1,4 +1,4 @@
<header class="w-full bg-red-600 text-white py-4 shadow-md">
<header class="w-full bg-red-600 text-white py-4 shadow-md max-h-20 h-full max-h-20">
<div class="max-w-4xl {% if center is None or center %}mx-auto{% endif %} px-4">
<h1 class="text-2xl font-bold {% if center is None or center %}{% else %}ml-5{% endif %}">{{ title }}</h1>
</div>

0
core/templates/htmx/reports.html Executable file → Normal file
View file

0
core/templates/index.html Executable file → Normal file
View file

0
core/templates/report.html Executable file → Normal file
View file

89
core/templates/write.html Executable file → Normal file
View file

@ -20,12 +20,24 @@
<form method="post" class="space-y-4">
{% load set_content %}
{% load access %}
{% for field in form %}
<div class="mb-4">
<label for="{{ field.id_for_label }}">
{{ field.label|safe }}
</label>
{{ field }}
{% if field.id_for_label == "id_department" %}
{{ field|set_content:draft.department}}
{%else%}
{% with content=draft.content|access:field.id_for_label %}
{{ field|set_content:content }}
{% endwith %}
{%endif%}
{% if field.errors %}
<p class="text-red-500 text-sm">{{ field.errors|join:", " }}</p>
{% endif %}
@ -39,6 +51,81 @@
Submit
</button>
</form>
<script>
function updateDraft() {
const formData = {};
{% for field in form %}
var field = document.getElementById("{{ field.id_for_label }}");
formData["{{ field.id_for_label }}"] = field.value;
{% endfor%}
fetch("/draft", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify(formData),
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Updated Draft:', data);
})
.catch(error => {
console.error('Error:', error);
});
}
function debounce(func, delay) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), delay);
};
}
function getCSRFToken() {
const name = 'csrftoken';
const cookies = document.cookie.split(';');
for (let cookie of cookies) {
cookie = cookie.trim();
if (cookie.startsWith(name + '=')) {
return cookie.substring(name.length + 1);
}
}
return null;
}
function setupListeners() {
const textareas = document.querySelectorAll('textarea');
const inputs = document.querySelectorAll('input');
const debouncedSend = debounce(() => updateDraft(), 500);
textareas.forEach(textarea => {
textarea.addEventListener('input', () => debouncedSend());
});
inputs.forEach(input => {
if (input.type === 'text' || input.type === 'email' || input.type === 'password' || input.type === 'hidden') {
input.addEventListener('input', () => debouncedSend());
}
});
}
setupListeners();
</script>
</main>
</body>
</html>

View file

@ -0,0 +1,13 @@
from django import template
register = template.Library()
@register.filter(name="access")
def access(value, arg):
if value == None:
return ""
try:
return value[arg]
except:
return ""

View file

@ -0,0 +1,25 @@
from django import template
register = template.Library()
@register.filter(name="add_attr")
def add_attr(field, css):
"""Adds custom attributes (like class) to form fields without overwriting existing attributes."""
# Retrieve existing attributes (if any)
attrs = field.field.widget.attrs.copy()
# Split the CSS string into key-value pairs
definition = css.split(",")
for d in definition:
if ":" not in d:
# If no key-value pair is provided, default to 'class'
attrs["class"] = f"{attrs.get('class', '')} {d}".strip()
else:
# If a key-value pair is provided (e.g., 'data-test:123'), add it
key, val = d.split(":")
attrs[key.strip()] = val.strip()
# Return the widget with the updated attributes
return field.as_widget(attrs=attrs)

View file

@ -0,0 +1,27 @@
from django import template
from django import forms
register = template.Library()
@register.filter(name="set_content")
def set_content(field, content):
"""Sets the content of input and textarea fields, and custom attributes like class, without overwriting existing attributes."""
# Retrieve existing attributes (if any)
attrs = field.field.widget.attrs.copy()
# Check if the field is a text input or textarea
is_input_or_textarea = isinstance(
field.field.widget, (forms.widgets.TextInput, forms.widgets.Textarea)
)
# If the field is an input or textarea, set the content as value or text
if is_input_or_textarea:
attrs["value"] = content.strip() # Set the value to the provided content
if isinstance(field.field.widget, forms.widgets.Textarea):
field.initial = content.strip()
# Return the widget with the updated attributes
return field.as_widget(attrs=attrs)

0
core/tests.py Executable file → Normal file
View file

1
core/urls.py Executable file → Normal file
View file

@ -6,4 +6,5 @@ urlpatterns = [
path("write", views.write_new_report, name="write"),
path("report/<int:report_id>", views.report_detail_page, name="report_detail"),
path("reports", views.reports_list, name="reports_list"),
path("draft", views.report_draft_update, name="report_draft_update"),
]

0
core/util.py Executable file → Normal file
View file

48
core/views.py Executable file → Normal file
View file

@ -7,6 +7,7 @@ from .models import Berichtsheft
from django.core.paginator import Paginator
from core.styles import STYLE
import datetime
import json
# Create your views here.
@ -78,11 +79,15 @@ def write_new_report_get(request):
if latest.year == year_now and latest.week == week_now:
return redirect(f"/report/{latest.id}")
current_year, current_week, start_date, end_date, current_num = (
get_current_report_values(latest)
)
(
current_year,
current_week,
start_date,
end_date,
current_num,
) = get_current_report_values(latest)
# TODO : Cookies for persistent saves
draft = BerichtsheftDraft.objects.filter(user=user.id).first()
form = user.get_report_kind_form()
@ -98,6 +103,7 @@ def write_new_report_get(request):
"current_num": current_num,
"report_kind": report_kind,
"form": form,
"draft": draft,
},
)
@ -154,3 +160,37 @@ def report_detail_page(request, report_id):
return HttpResponse("Nah", status=401)
return render(request, "report.html", {"report": report})
from django.shortcuts import get_object_or_404
from django.http import JsonResponse
from .models import BerichtsheftDraft
def report_draft_update(request):
if request.method == "POST":
user = AzureUser(request)
data = json.loads(request.body)
department = ""
content = {}
for key, val in data.items():
if key == "id_department":
department = val
continue
content[key] = val
draft, created = BerichtsheftDraft.objects.update_or_create(
user=user.id, defaults={"department": department, "content": content}
)
return JsonResponse(
{
"status": "success",
"message": "Draft updated" if not created else "Draft created",
}
)
return JsonResponse({"error": "Invalid request method"}, status=400)

0
manage.py Executable file → Normal file
View file

0
requirements.txt Executable file → Normal file
View file