Implement AI workspace and mitigation workflow

This commit is contained in:
2026-02-15 04:27:28 +00:00
parent de2b9a9bbb
commit 2d3b8fdac6
64 changed files with 7669 additions and 769 deletions

View File

@@ -3,69 +3,93 @@
{% get_last_invalidation 'core.QueuedMessage' as last %}
{% include 'mixins/partials/notify.html' %}
{% cache 600 objects_queue request.user.id object_list type last %}
<table
class="table is-fullwidth is-hoverable"
hx-target="#{{ context_object_name }}-table"
<div
id="{{ context_object_name }}-table"
hx-swap="outerHTML"
hx-trigger="{{ context_object_name_singular }}Event from:body"
hx-get="{{ list_url }}">
<thead>
<th>id</th>
<th>session</th>
<th>manipulation</th>
<th>ts</th>
<th>text</th>
<th>actions</th>
</thead>
{% for item in object_list %}
<tr>
<td>
<a
class="has-text-grey button nowrap-child"
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.id }}');">
<span class="icon" data-tooltip="Copy to clipboard">
<i class="fa-solid fa-copy" aria-hidden="true"></i>
</span>
</a>
</td>
<td>{{ item.session }}</td>
<td>{{ item.manipulation }}</td>
<td>{{ item.ts }}</td>
<td>{{ item.text.length }}</td>
<td>
<div class="buttons">
<button
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-get="{% url 'queue_update' type=type pk=item.id %}"
hx-trigger="click"
hx-target="#{{ type }}s-here"
hx-swap="innerHTML"
class="button">
<span class="icon-text">
<span class="icon">
<i class="fa-solid fa-pencil"></i>
</span>
</span>
</button>
<button
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-delete="{% url 'queue_delete' type=type pk=item.id %}"
hx-trigger="click"
hx-target="#modals-here"
hx-swap="innerHTML"
hx-confirm="Are you sure you wish to delete {{ item.id }}?"
class="button">
<span class="icon-text">
<span class="icon">
<i class="fa-solid fa-xmark"></i>
</span>
</span>
</button>
</div>
</td>
</tr>
{% endfor %}
hx-get="{{ list_url }}"
hx-target="#{{ context_object_name }}-table"
hx-swap="outerHTML">
</table>
{% endcache %}
<div class="is-flex is-justify-content-space-between is-align-items-center" style="margin-bottom: 0.75rem; gap: 0.5rem; flex-wrap: wrap;">
<div>
<h3 class="title is-6" style="margin-bottom: 0.15rem;">Outgoing Queue</h3>
<p class="is-size-7">Review queued drafts and approve or reject each message.</p>
</div>
<span class="tag is-dark is-medium">{{ object_list|length }} pending</span>
</div>
{% if object_list %}
<div class="columns is-multiline" style="margin: 0 -0.35rem;">
{% for item in object_list %}
<div class="column is-12" style="padding: 0.35rem;" id="queue-card-{{ item.id }}">
<article class="box" style="padding: 0.75rem; border: 1px solid rgba(0, 0, 0, 0.14); box-shadow: none;">
<div class="is-flex is-justify-content-space-between is-align-items-start" style="gap: 0.75rem; flex-wrap: wrap; margin-bottom: 0.5rem;">
<div>
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.2rem;">{{ item.session.identifier.person.name }}</p>
<div class="tags" style="margin-bottom: 0.2rem;">
<span class="tag is-light">{{ item.session.identifier.service|title }}</span>
<span class="tag is-light">{{ item.manipulation.name }}</span>
<span class="tag is-light">{{ item.ts }}</span>
</div>
</div>
<div class="buttons are-small" style="margin: 0;">
<button
class="button is-success is-light"
hx-get="{% url 'message_accept_api' message_id=item.id %}"
hx-swap="none"
_="on htmx:afterRequest if event.detail.successful remove #queue-card-{{ item.id }} then trigger {{ context_object_name_singular }}Event on body end">
<span class="icon is-small"><i class="fa-solid fa-check"></i></span>
<span>Approve</span>
</button>
<button
class="button is-danger is-light"
hx-get="{% url 'message_reject_api' message_id=item.id %}"
hx-swap="none"
_="on htmx:afterRequest if event.detail.successful remove #queue-card-{{ item.id }} then trigger {{ context_object_name_singular }}Event on body end">
<span class="icon is-small"><i class="fa-solid fa-xmark"></i></span>
<span>Reject</span>
</button>
</div>
</div>
<div style="padding: 0.6rem; border-radius: 8px; border: 1px solid rgba(0, 0, 0, 0.12); background: rgba(255, 255, 255, 0.45); margin-bottom: 0.5rem;">
<p style="white-space: pre-wrap; margin: 0;">{{ item.text|default:"(empty draft)" }}</p>
</div>
<div class="is-flex is-justify-content-space-between is-align-items-center" style="gap: 0.5rem; flex-wrap: wrap;">
<small class="has-text-grey">Queue ID: {{ item.id }}</small>
<div class="buttons are-small" style="margin: 0;">
<button
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-get="{% url 'queue_update' type=type pk=item.id %}"
hx-trigger="click"
hx-target="#{{ type }}s-here"
hx-swap="innerHTML"
class="button is-light">
<span class="icon is-small"><i class="fa-solid fa-pen"></i></span>
<span>Edit</span>
</button>
<button
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-delete="{% url 'queue_delete' type=type pk=item.id %}"
hx-trigger="click"
hx-target="#modals-here"
hx-swap="innerHTML"
hx-confirm="Delete queued message {{ item.id }}?"
class="button is-light">
<span class="icon is-small"><i class="fa-solid fa-trash"></i></span>
<span>Delete</span>
</button>
</div>
</div>
</article>
</div>
{% endfor %}
</div>
{% else %}
<article class="box" style="padding: 0.8rem; border: 1px dashed rgba(0, 0, 0, 0.25); box-shadow: none;">
<p class="is-size-7 has-text-grey">Queue is empty.</p>
</article>
{% endif %}
</div>
{% endcache %}