~fabrixxm/activist

820d2ac8ae4d9174b67cac148eea9dff6a0fcd7f — fabrixxm 2 months ago acd0320
ui tasks: open task details in modal via htmx
A activist/templates/admin/task-modal.tpl.j2 => activist/templates/admin/task-modal.tpl.j2 +19 -0
@@ 0,0 1,19 @@
<div class="modal modal-lg" id="task-detail-modal">
    <a href="#close" class="modal-overlay" aria-label="Close"></a>
    <div class="modal-container">
        <div class="modal-header">
            <a href="#close" class="btn btn-clear float-right" aria-label="Close"></a>
            <div class="modal-title h5">Details</div>
        </div>
        <div class="modal-body">
            {% include "admin/task.tpl.j2" %}
        </div>
        <div class="modal-footer">
            <button class="btn btn-danger" title="Delete task" name="action" value="delete"
                    hx-on::after-request="location.hash = ''; location.reload()"
                    hx-post="{{ url_for('tasks', state=state, taskid=task._file.name) }}">
                <i class="icon i-trash">
            </i></button>
        </div>
    </div>
</div>
\ No newline at end of file

M activist/templates/admin/task.html.j2 => activist/templates/admin/task.html.j2 +1 -46
@@ 11,52 11,7 @@
            </form>
        </header>

        <style>table * { max-width: 1296px; } th { white-space: nowrap; };</style>
        <table class="table table-striped">
            <tbody>
                <tr>
                    <th>Task</th>
                    <td>{{ task.fnc }}</td>
                </tr>
                <tr>
                    <th>Args</th>
                    <td><pre class="code"><code>{{ dump(task.args) }}
{{ dump(task.kwargs) }}</code></pre></td>
                </tr>
                <tr>
                    <th>State</th>
                    <td>{{ state }}</td>
                </tr>
                <tr>
                    <th>Created</th>
                    <td title="{{ task.created|datetime }}">{{ task.created|dtrelative("full") }}</td>
                </tr>
                <tr>
                    <th>Scheduled at</th>
                    <td title="{{ task._scheduled_at|datetime }}">
                        {{ task._scheduled_at|dtrelative("full") }}
                        {% if task.every %} {{ every|seconds }} {% endif %}
                    </td>
                </tr>
                <tr>
                    <th>Every</th>
                    <td>{{ task.every }}</td>
                </tr>                

                <tr>
                    <th>Retry</th>
                    <td>{{ task.retry }}</td>
                </tr>
                {% if task.error %}
                <tr>
                    <th>Error</th>
                    <td><pre>{{ task.lasttry }}
                    {{ task.error }}</pre></td>
                </tr>
                {% endif %}
            </tbody>
        </table>
    </div>
        {% include "admin/task.tpl.j2" %}

    </div>
{% endblock %}

A activist/templates/admin/task.tpl.j2 => activist/templates/admin/task.tpl.j2 +48 -0
@@ 0,0 1,48 @@
        <style>table * { max-width: 1296px; } th { white-space: nowrap; };</style>
        <table class="table table-striped">
            <tbody>
                <tr>
                    <th>Task</th>
                    <td>{{ task.fnc }}</td>
                </tr>
                <tr>
                    <th>Args</th>
                    <td><pre class="code"><code>{{ dump(task.args) }}
{{ dump(task.kwargs) }}</code></pre></td>
                </tr>
                <tr>
                    <th>State</th>
                    <td>{{ state }}</td>
                </tr>
                <tr>
                    <th>Created</th>
                    <td title="{{ task.created|datetime }}">{{ task.created|dtrelative("full") }}</td>
                </tr>
                <tr>
                    <th>Scheduled</th>
                    <td title="{{ task._scheduled_at|datetime }}">
                        {{ task._scheduled_at|dtrelative("full") }}
                        {% if task.every %} {{ every|seconds }} {% endif %}
                    </td>
                </tr>
                {% if task.executed %}
                <tr title="{{ task.executed|datetime }}">
                    <th>Executed</th>
                    <td>{{ task.executed|dtrelative("full")  }}</td>
                </tr>
                {% endif %} 

                <tr>
                    <th>Retry</th>
                    <td>{{ task.retry }}</td>
                </tr>
            </tbody>
        </table>
        {% if task.error %}
            {% for e in task.error %}
                <dl>
                    <dt>{{ e.type }} : {{ e.value }} {{ e.datetime }}</dt>
                    <dd><pre class="code"><code>{{ e.traceback }}</code></pre></dd>
                </dl>
            {% endfor %}
        {% endif %}

M activist/templates/admin/tasks.html.j2 => activist/templates/admin/tasks.html.j2 +3 -36
@@ 4,41 4,8 @@

{% block body %}
    <div class="container grid-xl">

        <table class="table table-striped table-hover">
            <thead class="p-sticky" style="top:0; background-color:#fff;">
                <tr>
                    <th>created</th>
                    <th>scheduled at</th>
                    <th>task</th>
                    <th>retry</th>
                    <th>error</th>
                </tr>
            </thead>

    {% for state, tasks in data.items()  %}
            <tbody>
                <tr class="p-sticky" style="top:2rem;" ><th colspan="5">
                    <div class="d-flex">
                        <span  class="f-fill">{{ state }}</span>
                        <form method="POST" action="{{ url_for('tasks', state=state) }}">
                            <button class="btn btn-danger" title="Delete all {{ state }} tasks" name="action" value="delete"><i class="icon i-trash"></i></button>
                        </form>
                    </div>
                </th></tr>
                {% for task in tasks  %}
                <tr>
                    <td><a href="{{ url_for('tasks', state=state, taskid=task._file.name) }}">{{ task.created|datetime }}</a></td>
                    <td>{{ task._scheduled_at|datetime }}</td>
                    <td>{{ task.fnc }}</td>
                    <td>{{ task.retry }}</td>
                    <td>{{ task.error.type if task.error else "" }}</td>
                </tr>
                {% endfor%}
            </tbody>
    {% endfor %}
        </table>
    </div>    

    {% include "admin/tasks.tpl.j2" %}
    </div>

    <div id="modal-container"></div>
{% endblock %}

A activist/templates/admin/tasks.tpl.j2 => activist/templates/admin/tasks.tpl.j2 +43 -0
@@ 0,0 1,43 @@
<table class="table table-striped table-hover">
    <thead class="p-sticky" style="top:0; background-color:#fff;">
        <tr>
            <th>created</th>
            <th>scheduled</th>
            <th>executed</th>
            <th>task</th>
            <th>retry</th>
            <th>error</th>
        </tr>
    </thead>

    {% for state, tasks in data.items()  %}
        <tbody>
            <tr class="p-sticky" style="top:2rem;" >
                <th colspan="5">
                    <div class="d-flex">
                        <span  class="f-fill">{{ state }}</span>
                        <form method="POST" action="{{ url_for('tasks', state=state) }}">
                            <button class="btn btn-danger" title="Delete all {{ state }} tasks" name="action" value="delete"><i class="icon i-trash"></i></button>
                        </form>
                    </div>
                </th>
            </tr>
            {% for task in tasks  %}
            <tr hx-get="{{ url_for('tasks', state=state, taskid=task._file.name) }}"
                    hx-on::after-request="location.hash = '#task-detail-modal'"
                    hx-target="#modal-container">
                <td>
                    <a href="{{ url_for('tasks', state=state, taskid=task._file.name) }}" hx-on:click="event.preventDefault()">
                        {{ task.created|datetime("%Y-%m-%d %H:%M:%S") }}
                    </a>
                </td>
                <td>{{ task._scheduled_at|datetime("%Y-%m-%d %H:%M:%S") }}</td>
                <td>{% if task.executed %}{{ task.executed|datetime("%Y-%m-%d %H:%M:%S") }}{% endif %}</td>
                <td>{{ task.fnc }}</td>
                <td>{{ task.retry }}</td>
                <td>{{ task.error[-1].type if task.error else "" }}</td>
            </tr>
            {% endfor%}
        </tbody>
    {% endfor %}
</table>
\ No newline at end of file

M activist/web.py => activist/web.py +6 -3
@@ 9,7 9,7 @@ from pathlib import Path
from flask import send_file, request, render_template, g, session, abort
from flask import redirect, url_for
from flask import Response
from flask_htmx import HTMX     # type: ignore
from flask_htmx import HTMX, make_response     # type: ignore
from markupsafe import Markup

from activist import activitypub


@@ 505,15 505,18 @@ def tasks(state:Optional[str] = None, taskid:Optional[str] = None):
            return redirect(url_for('tasks', state=state))
        
        task = TaskType.from_file(f)
        return render_template("admin/task.html.j2", state=state, task=task)
        tpl ="admin/task-modal.tpl.j2" if htmx else "admin/task.html.j2"
        return render_template(tpl, state=state, task=task)

    if state is not None:
        if request.method == "POST":
            for f in Background.taskstore(state).glob("*"):
                f.unlink()
            if htmx:
                make_response(trigger={'admin.tasks.updated'})
            return redirect(url_for('tasks'))

        data[state] = [TaskType.from_file(f) for f in Background.taskstore(state).glob("*")].sort(key = lambda t: t.created)
        data[state] = [TaskType.from_file(f) for f in Background.taskstore(state).glob("*")]
        data[state].sort(key = lambda t: t._scheduled_at)
        data[state].reverse()