feat(templates/partials):add UI partials for form fields and kanban comment interactions
This commit is contained in:
4 files changed
+112
-24
No files matched your search
@@ -0,0 +1,30 @@
|
||||
{% load form_helpers %}
|
||||
<div class="form-control w-full">
|
||||
<label class="label">
|
||||
<span class="label-text">{{ field.label }}</span>
|
||||
</label>
|
||||
{% with wt=field|field_type %}
|
||||
{% if wt == "CheckboxInput" or wt == "CheckboxSelectMultiple" %}
|
||||
<label class="cursor-pointer label justify-start gap-3 p-0">
|
||||
{{ field|add_class:"checkbox checkbox-primary" }}
|
||||
<span class="label-text">{{ field.label }}</span>
|
||||
</label>
|
||||
{% elif wt == "Select" or wt == "NullBooleanSelect" or wt == "SelectMultiple" %}
|
||||
{{ field|add_class:"select select-bordered w-full" }}
|
||||
{% elif wt == "Textarea" %}
|
||||
{{ field|add_class:"textarea textarea-bordered w-full" }}
|
||||
{% else %}
|
||||
{{ field|add_class:"input input-bordered w-full" }}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% if field.errors %}
|
||||
<label class="label">
|
||||
<span class="label-text-alt text-error">{{ field.errors.0 }}</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
{% if field.help_text %}
|
||||
<label class="label">
|
||||
<span class="label-text-alt text-gray-500">{{ field.help_text }}</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -2,33 +2,34 @@
|
||||
id="card-{{ card.pk }}">
|
||||
<div class="p-3">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<p class="text-sm font-medium text-gray-900 leading-snug">{{ card.title }}</p>
|
||||
<div class="flex gap-1 flex-shrink-0">
|
||||
<a href="{% url 'card_edit' card.pk %}"
|
||||
class="text-gray-300 hover:text-gray-600 transition-colors"
|
||||
<div class="flex-1 min-w-0">
|
||||
<a href="{% url 'card_detail' card.pk %}"
|
||||
class="text-sm font-medium text-gray-900 leading-snug hover:text-blue-600 block truncate"
|
||||
onclick="event.stopPropagation()">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>
|
||||
{{ card.title }}
|
||||
</a>
|
||||
<form method="post" action="{% url 'card_delete' card.pk %}"
|
||||
hx-target="#card-{{ card.pk }}" hx-swap="outerHTML swap:0.3s"
|
||||
onsubmit="event.stopPropagation()"
|
||||
class="inline">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="text-gray-300 hover:text-red-500 transition-colors"
|
||||
onclick="return confirm('Delete this card?')">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
<div class="flex items-center gap-2 mt-1.5">
|
||||
{% if card.priority == 'urgent' %}
|
||||
<span class="text-[10px] px-1.5 py-0.5 rounded font-semibold bg-red-100 text-red-700">Urgent</span>
|
||||
{% elif card.priority == 'high' %}
|
||||
<span class="text-[10px] px-1.5 py-0.5 rounded font-semibold bg-amber-100 text-amber-700">High</span>
|
||||
{% elif card.priority == 'medium' %}
|
||||
<span class="text-[10px] px-1.5 py-0.5 rounded font-semibold bg-blue-100 text-blue-700">Med</span>
|
||||
{% elif card.priority == 'low' %}
|
||||
<span class="text-[10px] px-1.5 py-0.5 rounded font-semibold bg-gray-100 text-gray-600">Low</span>
|
||||
{% endif %}
|
||||
{% if card.assignee %}
|
||||
<span class="text-[10px] text-gray-400 truncate">{{ card.assignee.username }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-1 flex-shrink-0">
|
||||
<button type="button"
|
||||
class="text-gray-300 hover:text-red-500 transition-colors"
|
||||
onclick="event.stopPropagation(); openCardDeleteModal('{% url 'card_delete' card.pk %}')">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% if card.description %}
|
||||
<p class="text-xs text-gray-500 mt-1 line-clamp-2">{{ card.description }}</p>
|
||||
{% endif %}
|
||||
{% if card.due_date %}
|
||||
<div class="flex items-center gap-1 mt-2">
|
||||
<svg class="w-3 h-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
||||
<span class="text-xs text-gray-500">{{ card.due_date }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
{% load form_helpers %}
|
||||
<div class="card bg-white rounded-xl shadow-sm border border-gray-100" id="comment-{{ comment.pk }}">
|
||||
<div class="card-body p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-8 h-8 rounded-full bg-blue-600 flex items-center justify-center text-white text-xs font-bold flex-shrink-0">{{ comment.author.username|first|upper }}</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<span class="text-sm font-semibold text-gray-900">{{ comment.author.username }}</span>
|
||||
<span class="text-xs text-gray-400">{{ comment.created_at|timesince }} ago</span>
|
||||
</div>
|
||||
<div class="prose prose-sm max-w-none text-gray-700 mb-2">
|
||||
{{ comment.body|render_markdown }}
|
||||
</div>
|
||||
{% if not comment.parent %}
|
||||
<button type="button"
|
||||
class="text-xs text-blue-600 hover:text-blue-800 font-medium"
|
||||
onclick="event.stopPropagation(); document.getElementById('reply-form-{{ comment.pk }}').classList.toggle('hidden')">
|
||||
Reply
|
||||
</button>
|
||||
<form method="post" action="{% url 'card_comment' comment.card.pk %}"
|
||||
id="reply-form-{{ comment.pk }}"
|
||||
class="hidden mt-3 space-y-2"
|
||||
hx-target="#comment-{{ comment.pk }}" hx-swap="afterend">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="parent" value="{{ comment.pk }}">
|
||||
<textarea name="body" class="textarea textarea-bordered w-full text-sm min-h-[80px]" placeholder="Write a reply..." required></textarea>
|
||||
<div class="flex gap-2">
|
||||
<button type="submit" class="btn btn-sm bg-blue-600 text-white hover:bg-blue-700 border-0">Reply</button>
|
||||
<button type="button" class="btn btn-sm btn-ghost" onclick="this.closest('form').classList.add('hidden')">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if comment.replies.all %}
|
||||
<div class="mt-3 space-y-3 pl-4 border-l-2 border-gray-100">
|
||||
{% for reply in comment.replies.all %}
|
||||
{% include "partials/kanban/comment.html" with comment=reply %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
{% for comment in comments %}
|
||||
<div class="flex gap-3">
|
||||
<div class="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white text-xs font-bold flex-shrink-0">{{ comment.author.username|first|upper }}</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<span class="text-sm font-semibold text-gray-900">{{ comment.author.username }}</span>
|
||||
<span class="text-xs text-gray-400">{{ comment.created_at|timesince }} ago</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-700">{{ comment.body }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="text-sm text-gray-400 text-center py-4">No comments yet.</p>
|
||||
{% endfor %}
|
||||
Reference in new issue
Block a user