Improve task filters and tag collections

This commit is contained in:
rucki
2026-09-04 11:44:49 +02:00
parent e04385c23d
commit c6309cabd8
12 changed files with 342 additions and 13 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
{% load i18n %}
<form id="item-{{ item.id }}" class="card item-card shadow-sm position-relative" method="post" enctype="multipart/form-data" hx-post="{% url 'edit_item' item.id %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}" hx-target="#item-{{ item.id }}" hx-swap="outerHTML" x-data="quickComposer([{% for tag in tags %}'{{ tag.name|escapejs }}'{% if not forloop.last %},{% endif %}{% endfor %}], {{ item.id }})" x-init="text=$el.querySelector('textarea').value">
<form id="item-{{ item.id }}" class="card item-card shadow-sm position-relative inline-edit-form" method="post" enctype="multipart/form-data" hx-post="{% url 'edit_item' item.id %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}" hx-target="#item-{{ item.id }}" hx-swap="outerHTML" x-data="quickComposer([{% for tag in tags %}'{{ tag.name|escapejs }}'{% if not forloop.last %},{% endif %}{% endfor %}], {{ item.id }})" x-init="text=$el.querySelector('textarea').value">
{% csrf_token %}
<div class="card-body vstack gap-2">
{% if form.errors %}<div class="alert alert-danger mb-0">{{ form.errors }}</div>{% endif %}
+1 -1
View File
@@ -27,7 +27,7 @@
<div class="collection-flow content">
{% if collection.mode == 'tags' %}
{% for item in tag_items %}<section id="item-{{ item.id }}" class="collection-flow-section">{{ item|collection_item_markdown }}{% if item.comment %}<div class="text-muted border-start ps-3 mt-3">{{ item.comment|markdown }}</div>{% endif %}</section>{% empty %}<div class="text-center text-muted py-5">{% trans "No notes match the selected tags yet." %}</div>{% endfor %}
{% for item in tag_items %}<section id="item-{{ item.id }}" class="collection-flow-section">{{ item|collection_item_markdown }}{% if item.comment %}<div class="text-muted border-start ps-3 mt-3">{{ item.comment|markdown }}</div>{% endif %}</section>{% empty %}<div class="text-center text-muted py-5">{% trans "No entries match the selected tags and types yet." %}</div>{% endfor %}
{% else %}
{% for section in sections %}{% include 'core/_collection_section_read.html' with section=section %}{% empty %}<div class="text-center text-muted py-5">{% trans "This collection has no sections yet." %}</div>{% endfor %}
{% endif %}
+9 -1
View File
@@ -13,7 +13,15 @@
<div><label class="form-label">{{ form.mode.label }}</label>{{ form.mode }}{{ form.mode.errors }}<div class="form-text">{% trans "Tag collections update automatically and are sorted by note creation time." %}</div></div>
<div><label class="form-label">{{ form.visibility.label }}</label>{{ form.visibility }}{{ form.visibility.errors }}</div>
<div><label class="form-label">{{ form.team.label }}</label>{{ form.team }}{{ form.team.errors }}<div class="form-text">{% trans "Optional. Selecting a team makes the collection visible and editable for that team." %}</div></div>
<div x-show="mode === 'tags'"><label class="form-label">{{ form.filter_tags.label }}</label><div class="border rounded p-3 collection-tags">{% if form.filter_tags.field.queryset.exists %}{{ form.filter_tags }}{% else %}<span class="text-muted small">{% trans "No content tags are available yet." %}</span>{% endif %}</div>{{ form.filter_tags.errors }}<div class="form-text">{% trans "Notes must contain all selected tags. Team tags are not content filters." %}</div></div>
<div x-show="mode === 'tags'" class="vstack gap-3">
<div><label class="form-label">{{ form.filter_tags.label }}</label><div class="border rounded p-3 collection-tags">{% if form.filter_tags.field.queryset.exists %}{{ form.filter_tags }}{% else %}<span class="text-muted small">{% trans "No content tags are available yet." %}</span>{% endif %}</div>{{ form.filter_tags.errors }}<div class="form-text">{% trans "Entries must contain all selected tags. Team tags are not content filters." %}</div></div>
<div><label class="form-label">{% trans "Entry types" %}</label><div class="border rounded p-3 d-flex flex-wrap gap-3">
<label class="form-check mb-0">{{ form.include_notes }} <span class="form-check-label">{{ form.include_notes.label }}</span></label>
<label class="form-check mb-0">{{ form.include_todos }} <span class="form-check-label">{{ form.include_todos.label }}</span></label>
<label class="form-check mb-0">{{ form.include_links }} <span class="form-check-label">{{ form.include_links.label }}</span></label>
<label class="form-check mb-0">{{ form.include_journal }} <span class="form-check-label">{{ form.include_journal.label }}</span></label>
</div>{{ form.include_notes.errors }}</div>
</div>
<div class="d-flex gap-2"><button class="btn btn-dark">{% trans "Save" %}</button><a class="btn btn-outline-secondary" href="{% if collection %}{{ collection.get_absolute_url }}{% else %}{% url 'collection_list' %}{% endif %}">{% trans "Cancel" %}</a></div>
</div></form>
</div>
+7
View File
@@ -111,6 +111,8 @@
<section class="col-lg-9">
<form class="desktop-quick-create position-relative mb-4" method="post" enctype="multipart/form-data" hx-post="{{ request.get_full_path }}" hx-target="#items" hx-swap="afterbegin" x-data="quickComposer([{% for tag in tags %}'{{ tag.name|escapejs }}'{% if not forloop.last %},{% endif %}{% endfor %}])" x-on:htmx:after-request="if($event.detail.successful){text='';open=false;$el.reset()}">
{% csrf_token %}
<fieldset :disabled="editingItem" :class="{'opacity-50': editingItem}">
<div class="small text-muted mb-2" x-show="editingItem" x-cloak>{% trans "Inline editor is open; quick entry is disabled." %}</div>
<div class="mobile-create-buttons gap-2 mb-2">
<button type="button" class="btn btn-sm btn-primary flex-fill" @click="insert('/todo')">{% trans "Task" %}</button>
<button type="button" class="btn btn-sm btn-secondary flex-fill" @click="$el.querySelector('textarea').focus()">{% trans "Note" %}</button>
@@ -130,6 +132,7 @@
<button class="btn btn-dark px-4">{% trans "Save" %}</button>
</div>
<template x-if="preview"><div class="mt-2 small"><span class="text-muted">{% trans "Image from clipboard:" %}</span><br><img :src="preview" class="img-thumbnail mt-1" style="max-height:160px"></div></template>
</fieldset>
</form>
<div id="items" class="vstack gap-3">
{% for item in items %}{% include 'core/_item.html' %}{% empty %}<div class="text-center text-muted py-5">{% trans "Nothing here yet." %}</div>{% endfor %}
@@ -150,12 +153,16 @@ function quickComposer(tags, itemId=null){
linkSuggestions:[],
attachmentSuggestions:[],
pendingFiles:[],
editingItem:false,
itemId:itemId,
tags:tags||[],
init(){
document.body.addEventListener('htmx:afterRequest', (event) => {
if(event.detail.successful && event.target === this.$el){ this.preview=null; }
});
document.body.addEventListener('htmx:afterSwap', () => {
this.editingItem=!!document.querySelector('.inline-edit-form');
});
document.body.addEventListener('tagsChanged', () => {
fetch('{% url "api_tags" %}').then(r=>r.json()).then(data=>{this.tags=data.tags||[]});
});