|
|
@@ -4,9 +4,14 @@
|
|
|
{% block content %}
|
|
|
<div class="flex items-center justify-between mb-6">
|
|
|
<h1 class="text-xl font-bold text-gray-900">Paramètres</h1>
|
|
|
- <a href="{{ url_for('dest.destination_new') }}" id="btn-new-dest" class="btn-primary btn-sm hidden">
|
|
|
- + Nouvelle destination
|
|
|
- </a>
|
|
|
+ <div class="flex gap-2">
|
|
|
+ <a href="{{ url_for('dest.destination_new') }}" id="btn-new-dest" class="btn-primary btn-sm hidden">
|
|
|
+ + Nouvelle destination
|
|
|
+ </a>
|
|
|
+ <a href="{{ url_for('network.remote_instance_new') }}" id="btn-new-inst" class="btn-primary btn-sm hidden">
|
|
|
+ + Ajouter une instance
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
{# ── Onglets ─────────────────────────────────────────────────────── #}
|
|
|
@@ -15,6 +20,10 @@
|
|
|
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 border-blue-600 text-blue-600 -mb-px">
|
|
|
Destinations
|
|
|
</button>
|
|
|
+ <button id="tab-btn-instances" onclick="setTab('instances')"
|
|
|
+ class="tab-btn px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 -mb-px">
|
|
|
+ Instances
|
|
|
+ </button>
|
|
|
<button id="tab-btn-config" onclick="setTab('config')"
|
|
|
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 -mb-px">
|
|
|
Configuration
|
|
|
@@ -75,6 +84,67 @@
|
|
|
|
|
|
</div>{# /pane-destinations #}
|
|
|
|
|
|
+{# ══════════════════ PANNEAU : INSTANCES ══════════════════════════ #}
|
|
|
+<div id="pane-instances" class="hidden">
|
|
|
+
|
|
|
+{% if not instances %}
|
|
|
+ <div class="bg-white rounded-xl border border-gray-200 px-6 py-12 text-center text-gray-400">
|
|
|
+ <p class="text-lg">Aucune instance distante configurée.</p>
|
|
|
+ <p class="text-sm mt-2">Ajoutez une instance pour voir son état et déclencher des sauvegardes à distance.</p>
|
|
|
+ <a href="{{ url_for('network.remote_instance_new') }}" class="mt-4 inline-block text-blue-600 hover:underline text-sm">
|
|
|
+ Ajouter une première instance →
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+{% else %}
|
|
|
+ <div class="space-y-4">
|
|
|
+ {% for inst in instances %}
|
|
|
+ {% set status_color = {
|
|
|
+ 'online': 'bg-green-100 text-green-700',
|
|
|
+ 'error': 'bg-red-100 text-red-700',
|
|
|
+ 'offline': 'bg-gray-100 text-gray-500',
|
|
|
+ }.get(inst.status, 'bg-gray-100 text-gray-400') %}
|
|
|
+
|
|
|
+ <div class="bg-white rounded-xl border border-gray-200 shadow-sm p-5">
|
|
|
+ <div class="flex items-start justify-between gap-4">
|
|
|
+ <div class="space-y-1 min-w-0 flex-1">
|
|
|
+ <div class="flex items-center gap-2 flex-wrap">
|
|
|
+ <span class="font-semibold text-gray-900">{{ inst.name }}</span>
|
|
|
+ <span class="text-xs px-2 py-0.5 rounded-full font-medium {{ status_color }}">
|
|
|
+ {{ inst.status or 'unknown' }}
|
|
|
+ </span>
|
|
|
+ {% if inst.last_seen %}
|
|
|
+ <span class="text-xs text-gray-400">vu {{ inst.last_seen.strftime('%d/%m %H:%M') }}</span>
|
|
|
+ {% endif %}
|
|
|
+ </div>
|
|
|
+ <p class="text-sm font-mono text-gray-500">{{ inst.url_display }}</p>
|
|
|
+ {% if inst.remote_runs %}
|
|
|
+ <p class="text-xs text-gray-400">
|
|
|
+ {{ inst.remote_runs | length }} job{{ 's' if inst.remote_runs | length != 1 }} synchronisé{{ 's' if inst.remote_runs | length != 1 }}
|
|
|
+ </p>
|
|
|
+ {% endif %}
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center gap-2 shrink-0">
|
|
|
+ <form method="post" action="{{ url_for('network.remote_instance_test', inst_id=inst.id) }}">
|
|
|
+ <button type="submit" class="btn-secondary btn-sm">Tester</button>
|
|
|
+ </form>
|
|
|
+ <form method="post" action="{{ url_for('network.remote_instance_sync', inst_id=inst.id) }}">
|
|
|
+ <button type="submit" class="btn-secondary btn-sm">Sync</button>
|
|
|
+ </form>
|
|
|
+ <a href="{{ url_for('network.remote_instance_edit', inst_id=inst.id) }}"
|
|
|
+ class="btn-secondary btn-sm">Éditer</a>
|
|
|
+ <form method="post" action="{{ url_for('network.remote_instance_delete', inst_id=inst.id) }}"
|
|
|
+ onsubmit="return confirm('Supprimer l\'instance « {{ inst.name }} » ?')">
|
|
|
+ <button type="submit" class="btn-danger btn-icon-sm">✕</button>
|
|
|
+ </form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {% endfor %}
|
|
|
+ </div>
|
|
|
+{% endif %}
|
|
|
+
|
|
|
+</div>{# /pane-instances #}
|
|
|
+
|
|
|
{# ══════════════════ PANNEAU : CONFIGURATION ══════════════════════ #}
|
|
|
<div id="pane-config" class="hidden max-w-xl">
|
|
|
|
|
|
@@ -194,8 +264,10 @@
|
|
|
</div>{# /pane-config #}
|
|
|
|
|
|
<script>
|
|
|
+const TAB_NAMES = ['destinations', 'instances', 'config'];
|
|
|
+
|
|
|
function setTab(name) {
|
|
|
- ['destinations', 'config'].forEach(function(t) {
|
|
|
+ TAB_NAMES.forEach(function(t) {
|
|
|
const pane = document.getElementById('pane-' + t);
|
|
|
const btn = document.getElementById('tab-btn-' + t);
|
|
|
const active = t === name;
|
|
|
@@ -204,12 +276,9 @@ function setTab(name) {
|
|
|
btn.classList.toggle('text-blue-600', active);
|
|
|
btn.classList.toggle('border-transparent', !active);
|
|
|
btn.classList.toggle('text-gray-500', !active);
|
|
|
- if (name === 'destinations') {
|
|
|
- document.getElementById('btn-new-dest').classList.remove('hidden');
|
|
|
- } else {
|
|
|
- document.getElementById('btn-new-dest').classList.add('hidden');
|
|
|
- }
|
|
|
});
|
|
|
+ document.getElementById('btn-new-dest').classList.toggle('hidden', name !== 'destinations');
|
|
|
+ document.getElementById('btn-new-inst').classList.toggle('hidden', name !== 'instances');
|
|
|
history.replaceState(null, '', location.pathname + '?tab=' + name);
|
|
|
}
|
|
|
|
|
|
@@ -224,8 +293,10 @@ function copyToken() {
|
|
|
|
|
|
// Restore tab from URL
|
|
|
const urlTab = new URLSearchParams(location.search).get('tab');
|
|
|
-if (urlTab === 'config') setTab('config');
|
|
|
-// Show "+ Nouvelle destination" button by default (destinations tab is first)
|
|
|
-document.getElementById('btn-new-dest').classList.remove('hidden');
|
|
|
+if (TAB_NAMES.includes(urlTab)) {
|
|
|
+ setTab(urlTab);
|
|
|
+} else {
|
|
|
+ setTab('destinations');
|
|
|
+}
|
|
|
</script>
|
|
|
{% endblock %}
|