Replace the per-row checkbox + numeric Order table on the blueprint detail page with a drag-to-reorder list of selected overlays plus a native <select> for adding more. Removing uses an × button per row; the option sorted-inserts back into the dropdown alphabetically. Native HTML5 drag-and-drop, no library, no JS-disabled fallback. Server contract is unchanged: each list row owns one hidden <input name="overlay_ids">, DOM order = submission order, and the existing fallback_position branch in ordered_overlay_ids_from_form absorbs the now-omitted overlay_position_<id> fields. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
60 lines
3 KiB
HTML
60 lines
3 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Blueprint {{ blueprint.name }} | left4me{% endblock %}
|
||
|
||
{% block content %}
|
||
<section class="panel">
|
||
<div class="page-heading">
|
||
<h1>Blueprint: {{ blueprint.name }}</h1>
|
||
<button type="button" class="danger" data-modal-open="delete-blueprint-modal">Delete</button>
|
||
</div>
|
||
<form method="post" action="/blueprints/{{ blueprint.id }}" class="stack">
|
||
<input type="hidden" name="csrf_token" value="{{ session.get('csrf_token', '') }}">
|
||
<label>Name <input name="name" value="{{ blueprint.name }}" required></label>
|
||
<p class="muted">Overlay order matters: the first overlay has highest precedence.</p>
|
||
<div class="overlay-picker">
|
||
<ol class="overlay-picker-list" data-overlay-list>
|
||
{% for overlay in selected_overlays %}
|
||
<li class="overlay-picker-row" draggable="true" data-overlay-id="{{ overlay.id }}" data-overlay-name="{{ overlay.name }}">
|
||
<span class="overlay-picker-handle" aria-hidden="true">⋮⋮</span>
|
||
<span class="overlay-picker-name">{{ overlay.name }}</span>
|
||
<button type="button" class="overlay-picker-remove" data-action="remove" aria-label="Remove {{ overlay.name }}">×</button>
|
||
<input type="hidden" name="overlay_ids" value="{{ overlay.id }}">
|
||
</li>
|
||
{% endfor %}
|
||
</ol>
|
||
<p class="overlay-picker-empty muted" data-overlay-empty {% if selected_overlays %}hidden{% endif %}>No overlays selected. Pick one below to add.</p>
|
||
<label class="overlay-picker-add">
|
||
<span>Add overlay</span>
|
||
<select data-overlay-add>
|
||
<option value="">Pick a name…</option>
|
||
{% for overlay in available_overlays %}
|
||
<option value="{{ overlay.id }}" data-overlay-name="{{ overlay.name }}">{{ overlay.name }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</label>
|
||
</div>
|
||
<label>Arguments <textarea name="arguments" rows="8" spellcheck="false">{{ arguments | join('\n') }}</textarea></label>
|
||
<label>Config <textarea name="config" rows="8" spellcheck="false">{{ config_lines | join('\n') }}</textarea></label>
|
||
<button type="submit">Save blueprint</button>
|
||
</form>
|
||
</section>
|
||
|
||
<dialog id="delete-blueprint-modal" class="modal" aria-labelledby="delete-blueprint-title">
|
||
<div class="modal-header">
|
||
<h2 id="delete-blueprint-title">Delete blueprint "{{ blueprint.name }}"?</h2>
|
||
<button type="button" class="modal-close" data-modal-close aria-label="Close">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p>This cannot be undone. Blueprints in use by a server cannot be deleted.</p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="button-secondary" data-modal-close>Cancel</button>
|
||
<form method="post" action="/blueprints/{{ blueprint.id }}/delete" class="inline-form">
|
||
<input type="hidden" name="csrf_token" value="{{ session.get('csrf_token', '') }}">
|
||
<button class="danger" type="submit">Delete</button>
|
||
</form>
|
||
</div>
|
||
</dialog>
|
||
<script src="{{ url_for('static', filename='js/blueprint-overlay-picker.js') }}" defer></script>
|
||
{% endblock %}
|