work + report draft
This commit is contained in:
parent
43fe9aea31
commit
7a70163e76
42 changed files with 228 additions and 19 deletions
0
.gitignore
vendored
Executable file → Normal file
0
.gitignore
vendored
Executable file → Normal file
0
azube/__init__.py
Executable file → Normal file
0
azube/__init__.py
Executable file → Normal file
0
azube/asgi.py
Executable file → Normal file
0
azube/asgi.py
Executable file → Normal file
0
azube/settings.py
Executable file → Normal file
0
azube/settings.py
Executable file → Normal file
0
azube/urls.py
Executable file → Normal file
0
azube/urls.py
Executable file → Normal file
0
azube/wsgi.py
Executable file → Normal file
0
azube/wsgi.py
Executable file → Normal file
0
core/__init__.py
Executable file → Normal file
0
core/__init__.py
Executable file → Normal file
0
core/admin.py
Executable file → Normal file
0
core/admin.py
Executable file → Normal file
0
core/apps.py
Executable file → Normal file
0
core/apps.py
Executable file → Normal file
0
core/azure_auth.py
Executable file → Normal file
0
core/azure_auth.py
Executable file → Normal file
0
core/doc_gen.py
Executable file → Normal file
0
core/doc_gen.py
Executable file → Normal file
1
core/migrations/0001_initial.py
Executable file → Normal file
1
core/migrations/0001_initial.py
Executable file → Normal 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
1
core/migrations/0002_alter_berichtsheft_id.py
Executable file → Normal file
|
@ -4,7 +4,6 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("core", "0001_initial"),
|
||||
]
|
||||
|
|
1
core/migrations/0003_group_azureuser_alter_berichtsheft_user.py
Executable file → Normal file
1
core/migrations/0003_group_azureuser_alter_berichtsheft_user.py
Executable file → Normal 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"),
|
||||
|
|
1
core/migrations/0004_remove_berichtsheft_approved_by.py
Executable file → Normal file
1
core/migrations/0004_remove_berichtsheft_approved_by.py
Executable file → Normal file
|
@ -4,7 +4,6 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("core", "0003_group_azureuser_alter_berichtsheft_user"),
|
||||
]
|
||||
|
|
1
core/migrations/0005_user_alter_berichtsheft_user_delete_azureuser.py
Executable file → Normal file
1
core/migrations/0005_user_alter_berichtsheft_user_delete_azureuser.py
Executable file → Normal 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
1
core/migrations/0006_berichtsheft_num.py
Executable file → Normal 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
1
core/migrations/0007_approval.py
Executable file → Normal 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
1
core/migrations/0008_berichtsheft_needs_rewrite.py
Executable file → Normal 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
1
core/migrations/0009_berichtsheft_kind.py
Executable file → Normal 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
1
core/migrations/0010_berichtsheft_department.py
Executable file → Normal file
|
@ -4,7 +4,6 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("core", "0009_berichtsheft_kind"),
|
||||
]
|
||||
|
|
20
core/migrations/0011_berichtsheftdraft.py
Normal file
20
core/migrations/0011_berichtsheftdraft.py
Normal 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
0
core/migrations/__init__.py
Executable file → Normal file
6
core/models.py
Executable file → Normal file
6
core/models.py
Executable file → Normal 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
4
core/reports.py
Executable file → Normal 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
0
core/styles.py
Executable file → Normal file
0
core/templates/component/report.html
Executable file → Normal file
0
core/templates/component/report.html
Executable file → Normal file
0
core/templates/head.html
Executable file → Normal file
0
core/templates/head.html
Executable file → Normal file
2
core/templates/header.html
Executable file → Normal file
2
core/templates/header.html
Executable file → Normal 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
0
core/templates/htmx/reports.html
Executable file → Normal file
0
core/templates/index.html
Executable file → Normal file
0
core/templates/index.html
Executable file → Normal file
0
core/templates/report.html
Executable file → Normal file
0
core/templates/report.html
Executable file → Normal file
89
core/templates/write.html
Executable file → Normal file
89
core/templates/write.html
Executable file → Normal 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>
|
||||
|
|
13
core/templatetags/access.py
Normal file
13
core/templatetags/access.py
Normal 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 ""
|
25
core/templatetags/add_attr.py
Normal file
25
core/templatetags/add_attr.py
Normal 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)
|
27
core/templatetags/set_content.py
Normal file
27
core/templatetags/set_content.py
Normal 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
0
core/tests.py
Executable file → Normal file
1
core/urls.py
Executable file → Normal file
1
core/urls.py
Executable file → Normal 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
0
core/util.py
Executable file → Normal file
48
core/views.py
Executable file → Normal file
48
core/views.py
Executable file → Normal 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
0
manage.py
Executable file → Normal file
0
requirements.txt
Executable file → Normal file
0
requirements.txt
Executable file → Normal file
Loading…
Add table
Reference in a new issue