100 lines
5.1 KiB
HTML
100 lines
5.1 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}{{ 'Edit' if wf else 'New' }} workflow — MartinhalApprovalFlow{% endblock %}
|
|
{% block content %}
|
|
<p><a href="{{ url_for('admin') }}">← All workflows</a></p>
|
|
<h1>{{ 'Edit workflow' if wf else 'New workflow' }}</h1>
|
|
<p class="sub">Each workflow has its own allowed sender domain, its own approver, and its own email wording.</p>
|
|
|
|
<form method="post">
|
|
<div class="card">
|
|
<h2 style="margin-top:0">Basics</h2>
|
|
<div class="grid2">
|
|
<div>
|
|
<label for="n">Name</label>
|
|
<input id="n" type="text" name="name" value="{{ wf.name if wf else '' }}" required>
|
|
</div>
|
|
<div>
|
|
<label for="de">Description <small>(shown to users on the main page)</small></label>
|
|
<input id="de" type="text" name="description" value="{{ wf.description if wf else '' }}">
|
|
</div>
|
|
</div>
|
|
<div class="grid2">
|
|
<div>
|
|
<label for="ap">Approval chain <small>(one email per line, in order — the request goes to the first person; each approval sends it to the next; any denial stops the workflow)</small></label>
|
|
<textarea id="ap" name="approvers" class="tpl" required
|
|
placeholder="team-lead@example.com manager@example.com director@example.com">{% if wf %}{{ '\n'.join(wf.approvers | fromjson) }}{% endif %}</textarea>
|
|
</div>
|
|
<div>
|
|
<label for="kn">Knowledge chain <small>(one email per line — these people never approve, but are informed by e-mail when a request is created and every time an approval, denial or final decision happens)</small></label>
|
|
<textarea id="kn" name="knowledge" class="tpl"
|
|
placeholder="team-inbox@example.com finance@example.com">{% if wf %}{{ '\n'.join(wf.knowledge | fromjson) }}{% endif %}</textarea>
|
|
</div>
|
|
</div>
|
|
<label class="check"><input type="checkbox" name="active" {% if not wf or wf.active %}checked{% endif %}> Active — accept new requests</label>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2 style="margin-top:0">Email wording</h2>
|
|
<p class="muted">Placeholders: <span class="mono">{workflow} {requester} {subject} {body} {request_id} {step} {total_steps} {decided_by} {created_at} {summary} {deny_reason} {approve_url} {deny_url}</span>. Leave a field empty to use the default.</p>
|
|
|
|
<label>Approval request — subject</label>
|
|
<input type="text" name="approval_subject" value="{{ wf.approval_subject if wf else defaults.approval_subject }}">
|
|
<label>Approval request — body <small>(must include {approve_url} and {deny_url})</small></label>
|
|
<textarea class="tpl" name="approval_body">{{ wf.approval_body if wf else defaults.approval_body }}</textarea>
|
|
|
|
<div class="grid2">
|
|
<div>
|
|
<label>Approved notice — subject</label>
|
|
<input type="text" name="approved_subject" value="{{ wf.approved_subject if wf else defaults.approved_subject }}">
|
|
<label>Approved notice — body</label>
|
|
<textarea class="tpl" name="approved_body">{{ wf.approved_body if wf else defaults.approved_body }}</textarea>
|
|
</div>
|
|
<div>
|
|
<label>Denied notice — subject</label>
|
|
<input type="text" name="denied_subject" value="{{ wf.denied_subject if wf else defaults.denied_subject }}">
|
|
<label>Denied notice — body</label>
|
|
<textarea class="tpl" name="denied_body">{{ wf.denied_body if wf else defaults.denied_body }}</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit">{{ 'Save changes' if wf else 'Create workflow' }}</button>
|
|
</form>
|
|
|
|
<div class="card">
|
|
<h2 style="margin-top:0">Images for email bodies</h2>
|
|
<p class="muted">These images can be used in the <b>approval request body</b>, <b>approved notice body</b> and
|
|
<b>denied notice body</b> above. Copy an image's tag into the text where the image should appear —
|
|
it is embedded in the email at that spot. PNG, JPG or GIF, up to 2 MB each. The library is shared by all workflows.</p>
|
|
|
|
{% if images %}
|
|
<div class="imggrid">
|
|
{% for name in images %}
|
|
<div class="imgcard">
|
|
<img src="{{ url_for('email_image', name=name) }}" alt="{{ name }}">
|
|
<div class="imgmeta">
|
|
<span class="mono" title="{{ name }}">{{ name }}</span>
|
|
<code class="mono tag" onclick="navigator.clipboard && navigator.clipboard.writeText(this.textContent)"
|
|
title="Click to copy">{{ '{image:' + name + '}' }}</code>
|
|
<form method="post" action="{{ url_for('email_image_delete') }}"
|
|
onsubmit="return confirm('Delete {{ name }}? Emails using its tag will show plain text instead.')">
|
|
<input type="hidden" name="name" value="{{ name }}">
|
|
<input type="hidden" name="next" value="{{ request.path }}">
|
|
<button class="btn ghost small" type="submit">Delete</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="muted">No images uploaded yet.</p>
|
|
{% endif %}
|
|
|
|
<form method="post" action="{{ url_for('email_image_upload') }}" enctype="multipart/form-data" class="rowline" style="margin-top:1rem">
|
|
<input type="hidden" name="next" value="{{ request.path }}">
|
|
<input type="file" name="image" accept=".png,.jpg,.jpeg,.gif" required>
|
|
<button class="small" type="submit">Upload image</button>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|