From 28f100c6df51c53ff26885a966b17e7e464f6705 Mon Sep 17 00:00:00 2001 From: rucki Date: Sat, 22 Aug 2026 15:19:39 +0200 Subject: [PATCH] Add journal, editor, and overview improvements --- core/admin.py | 3 +- core/forms.py | 33 ++++- core/migrations/0005_journal_kind.py | 12 ++ core/migrations/0006_userpreference.py | 21 +++ .../0007_userpreference_overview_lines.py | 12 ++ core/models.py | 14 ++ core/templates/core/_edit_form.html | 5 +- core/templates/core/_item.html | 21 ++- core/templates/core/base.html | 23 +-- core/templates/core/detail.html | 7 +- core/templates/core/index.html | 65 ++++++--- core/templates/core/item_editor.html | 24 ++++ core/templates/core/journal.html | 23 +++ core/templates/core/new_item.html | 21 +++ core/templates/core/settings.html | 14 ++ core/templatetags/markdown_extras.py | 26 +++- core/urls.py | 4 + core/views.py | 133 ++++++++++++++++-- 18 files changed, 405 insertions(+), 56 deletions(-) create mode 100644 core/migrations/0005_journal_kind.py create mode 100644 core/migrations/0006_userpreference.py create mode 100644 core/migrations/0007_userpreference_overview_lines.py create mode 100644 core/templates/core/item_editor.html create mode 100644 core/templates/core/journal.html create mode 100644 core/templates/core/new_item.html diff --git a/core/admin.py b/core/admin.py index 251f357..4c12be2 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import ApiKey, Attachment, Item, Tag +from .models import ApiKey, Attachment, Item, Tag, UserPreference class AttachmentInline(admin.TabularInline): @@ -22,3 +22,4 @@ class ApiKeyAdmin(admin.ModelAdmin): admin.site.register(Tag) +admin.site.register(UserPreference) diff --git a/core/forms.py b/core/forms.py index ab68d3b..f65a482 100644 --- a/core/forms.py +++ b/core/forms.py @@ -1,10 +1,22 @@ from django import forms from django.contrib.auth.models import User -from .models import ApiKey, Attachment, Item +from .models import ApiKey, Attachment, Item, UserPreference + + +class MultipleFileInput(forms.ClearableFileInput): + allow_multiple_selected = True + + +class MultipleFileField(forms.FileField): + def clean(self, data, initial=None): + if not data: + return [] + files = data if isinstance(data, (list, tuple)) else [data] + return [super(MultipleFileField, self).clean(file, initial) for file in files] class QuickItemForm(forms.ModelForm): - files = forms.FileField(required=False, widget=forms.ClearableFileInput(attrs={'multiple': False})) + files = MultipleFileField(required=False, widget=MultipleFileInput(attrs={'multiple': True})) class Meta: model = Item @@ -21,7 +33,7 @@ class QuickItemForm(forms.ModelForm): class EditItemForm(forms.ModelForm): - files = forms.FileField(required=False, widget=forms.ClearableFileInput()) + files = MultipleFileField(required=False, widget=MultipleFileInput(attrs={'multiple': True})) class Meta: model = Item @@ -44,6 +56,21 @@ class UserSettingsForm(forms.ModelForm): widgets = {field: forms.TextInput(attrs={'class': 'form-control'}) for field in ['first_name', 'last_name', 'email']} +class UserPreferenceForm(forms.ModelForm): + class Meta: + model = UserPreference + fields = ['show_notes', 'show_open_todos', 'show_done_todos', 'show_links', 'show_journal', 'overview_lines'] + labels = { + 'show_notes': 'Notizen anzeigen', + 'show_open_todos': 'Offene Aufgaben anzeigen', + 'show_done_todos': 'Erledigte Aufgaben anzeigen', + 'show_links': 'Links anzeigen', + 'show_journal': 'Journal anzeigen', + 'overview_lines': 'Zeilen in der Übersicht', + } + widgets = {'overview_lines': forms.NumberInput(attrs={'class': 'form-control', 'min': 1, 'max': 50})} + + class ApiKeyForm(forms.ModelForm): class Meta: model = ApiKey diff --git a/core/migrations/0005_journal_kind.py b/core/migrations/0005_journal_kind.py new file mode 100644 index 0000000..bcc0a8f --- /dev/null +++ b/core/migrations/0005_journal_kind.py @@ -0,0 +1,12 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [('core', '0004_item_comment')] + operations = [ + migrations.AlterField( + model_name='item', + name='kind', + field=models.CharField(choices=[('todo', 'Aufgabe'), ('note', 'Notiz'), ('link', 'Link'), ('journal', 'Journal')], default='note', max_length=10), + ), + ] diff --git a/core/migrations/0006_userpreference.py b/core/migrations/0006_userpreference.py new file mode 100644 index 0000000..343885b --- /dev/null +++ b/core/migrations/0006_userpreference.py @@ -0,0 +1,21 @@ +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [('core', '0005_journal_kind'), migrations.swappable_dependency(settings.AUTH_USER_MODEL)] + operations = [ + migrations.CreateModel( + name='UserPreference', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('show_notes', models.BooleanField(default=True)), + ('show_open_todos', models.BooleanField(default=True)), + ('show_done_todos', models.BooleanField(default=False)), + ('show_links', models.BooleanField(default=True)), + ('show_journal', models.BooleanField(default=False)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='preferences', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/core/migrations/0007_userpreference_overview_lines.py b/core/migrations/0007_userpreference_overview_lines.py new file mode 100644 index 0000000..0080961 --- /dev/null +++ b/core/migrations/0007_userpreference_overview_lines.py @@ -0,0 +1,12 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [('core', '0006_userpreference')] + operations = [ + migrations.AddField( + model_name='userpreference', + name='overview_lines', + field=models.PositiveSmallIntegerField(default=4), + ), + ] diff --git a/core/models.py b/core/models.py index 23049d2..f56b768 100644 --- a/core/models.py +++ b/core/models.py @@ -23,6 +23,7 @@ class Item(models.Model): TODO = 'todo', 'Aufgabe' NOTE = 'note', 'Notiz' LINK = 'link', 'Link' + JOURNAL = 'journal', 'Journal' class Visibility(models.TextChoices): PRIVATE = 'private', 'Privat' @@ -58,6 +59,19 @@ class Item(models.Model): self.linked_items.set(Item.objects.filter(id__in=ids).exclude(id=self.id)) +class UserPreference(models.Model): + user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='preferences') + show_notes = models.BooleanField(default=True) + show_open_todos = models.BooleanField(default=True) + show_done_todos = models.BooleanField(default=False) + show_links = models.BooleanField(default=True) + show_journal = models.BooleanField(default=False) + overview_lines = models.PositiveSmallIntegerField(default=4) + + def __str__(self): + return f'Einstellungen {self.user}' + + class ApiKey(models.Model): owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='api_keys') name = models.CharField(max_length=120, default='API Key') diff --git a/core/templates/core/_edit_form.html b/core/templates/core/_edit_form.html index 419c3c8..505968b 100644 --- a/core/templates/core/_edit_form.html +++ b/core/templates/core/_edit_form.html @@ -1,4 +1,4 @@ -
+ {% csrf_token %}
Typ: {{ item.get_kind_display }}
@@ -13,12 +13,13 @@
{% if item.kind == 'todo' %}
{% endif %} -
+
{% if item.kind == 'link' %}{% endif %}
+ Im Editor öffnen
diff --git a/core/templates/core/_item.html b/core/templates/core/_item.html index 422a363..64728e0 100644 --- a/core/templates/core/_item.html +++ b/core/templates/core/_item.html @@ -1,15 +1,18 @@ {% load markdown_extras %} -
+
-
+
- {{ item.get_kind_display }} + {{ item.get_kind_display }} {{ item.get_visibility_display }} - #{{ item.id }} + #{{ item.id }} {% for tag in item.tags.all %}#{{ tag.name }}{% endfor %}
-
{{ item.content|markdown }}
+
+
{{ item|item_markdown }}
+ {% if overview_lines %}{% endif %} +
{% if item.comment %}
{{ item.comment|markdown }}
{% endif %} {% if item.due_at %}
Fällig: {{ item.due_at|date:'d.m.Y H:i' }}
{% endif %} {% if item.url %}{{ item.url }}{% endif %} @@ -17,13 +20,17 @@ {% if item.attachments.all %}
Anhänge: {% for a in item.attachments.all %}{{ a.file.name }} {% endfor %}
- {% for a in item.attachments.all %}{% if a.file.name|is_image %}{{ a.file.name }}{% endif %}{% endfor %} + {% for a in item.attachments.all %}{% if a.file.name|is_image and not a|is_embedded:item %}{{ a.file.name }}{% endif %}{% endfor %}
{% endif %}
{% if item.kind == 'todo' %}{% endif %} - + {% if request.resolver_match.url_name == 'item_detail' %} + ✎ + {% else %} + + {% endif %}
diff --git a/core/templates/core/base.html b/core/templates/core/base.html index c952d12..85ec697 100644 --- a/core/templates/core/base.html +++ b/core/templates/core/base.html @@ -9,29 +9,32 @@ - +
{% for message in messages %}
{{ message }}
{% endfor %}{% block content %}{% endblock %}
diff --git a/core/templates/core/detail.html b/core/templates/core/detail.html index 4d9a5c0..19615c6 100644 --- a/core/templates/core/detail.html +++ b/core/templates/core/detail.html @@ -1,8 +1,13 @@ {% extends 'core/base.html' %} {% block content %} -← zurück +← zurück {% include 'core/_item.html' %} {% if item.backlinks.all %}
Backlinks
{% for item in item.backlinks.all %}{% include 'core/_item.html' %}{% endfor %}
{% endif %} + {% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index d05ced3..3c307bd 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -6,10 +6,10 @@
Filter
@@ -17,11 +17,22 @@ {% if active_tag %}{% endif %} {% if active_kind %}{% endif %} {% if active_status %}{% endif %} - +
+ + {% if q %}×{% endif %} +
+ {% if active_kind == 'journal' and journal_days %} +
Journal-Kalender
+
+ {% for d in journal_days %} + {% if d.has_entries %}{{ d.day }}{% else %}{{ d.day }}{% endif %} + {% endfor %} +
+ {% endif %}
Tags
{% include 'core/_tags.html' %} @@ -36,7 +47,8 @@
- + + Editor
@@ -47,7 +59,7 @@