dashboard_network.html 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. {% extends "base.html" %}
  2. {% block title %}Vue réseau{% endblock %}
  3. {% block content %}
  4. <div class="flex items-center justify-between mb-6">
  5. <h1 class="text-xl font-bold text-gray-900">Vue réseau</h1>
  6. <div class="flex gap-2">
  7. <a href="{{ url_for('jobs.index') }}"
  8. class="text-sm text-gray-500 hover:text-gray-700 px-3 py-1.5 rounded border border-gray-200 bg-white transition">
  9. Vue locale
  10. </a>
  11. <form method="post" action="{{ url_for('network.network_sync_all') }}">
  12. <button type="submit" class="btn-primary btn-sm">Synchroniser tout</button>
  13. </form>
  14. </div>
  15. </div>
  16. {% macro instance_section(inst_name, inst_url, jobs_data, inst_id=None, inst_status=None, last_seen=None) %}
  17. <div class="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden mb-6">
  18. <div class="px-6 py-4 border-b border-gray-100 flex items-center justify-between gap-4">
  19. <div class="flex items-center gap-3 min-w-0">
  20. <h2 class="text-base font-semibold text-gray-800">{{ inst_name }}</h2>
  21. {% if inst_id %}
  22. {% set sc = {'online':'bg-green-100 text-green-700','error':'bg-red-100 text-red-700','offline':'bg-gray-100 text-gray-500'} %}
  23. <span class="text-xs px-2 py-0.5 rounded-full font-medium {{ sc.get(inst_status, 'bg-gray-100 text-gray-400') }}">
  24. {{ inst_status or 'unknown' }}
  25. </span>
  26. {% if last_seen %}
  27. <span class="text-xs text-gray-400 hidden sm:inline">sync {{ last_seen.strftime('%d/%m %H:%M') }}</span>
  28. {% endif %}
  29. {% else %}
  30. <span class="text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full font-medium">local</span>
  31. {% endif %}
  32. {% if inst_url %}
  33. <span class="text-xs font-mono text-gray-400 truncate hidden md:inline">{{ inst_url }}</span>
  34. {% endif %}
  35. </div>
  36. {% if inst_id %}
  37. <div class="flex gap-2 shrink-0">
  38. <form method="post" action="{{ url_for('network.remote_instance_sync', inst_id=inst_id) }}">
  39. <button type="submit" class="btn-secondary btn-sm">Sync</button>
  40. </form>
  41. <a href="{{ url_for('network.remote_instance_edit', inst_id=inst_id) }}"
  42. class="btn-secondary btn-sm">Éditer</a>
  43. </div>
  44. {% endif %}
  45. </div>
  46. {% if not jobs_data %}
  47. <div class="px-6 py-8 text-center text-gray-400 text-sm">
  48. {% if inst_id %}
  49. Aucune donnée — cliquez sur "Sync" pour récupérer l'état de cette instance.
  50. {% else %}
  51. Aucun job configuré.
  52. <a href="{{ url_for('jobs.job_new') }}" class="text-blue-600 hover:underline ml-1">Créer un job →</a>
  53. {% endif %}
  54. </div>
  55. {% else %}
  56. <div class="overflow-x-auto">
  57. <table class="w-full text-sm">
  58. <thead>
  59. <tr class="text-xs text-gray-500 uppercase tracking-wide bg-gray-50">
  60. <th class="px-6 py-3 text-left font-medium">Job</th>
  61. <th class="px-6 py-3 text-left font-medium">Type</th>
  62. <th class="px-6 py-3 text-left font-medium">Dernière exéc.</th>
  63. <th class="px-6 py-3 text-left font-medium">Statut</th>
  64. <th class="px-6 py-3 text-left font-medium">Taille</th>
  65. <th class="px-6 py-3 text-right font-medium">Actions</th>
  66. </tr>
  67. </thead>
  68. <tbody class="divide-y divide-gray-100">
  69. {% for row in jobs_data %}
  70. <tr class="hover:bg-gray-50">
  71. <td class="px-6 py-3 font-medium text-gray-900">{{ row.name }}</td>
  72. <td class="px-6 py-3">
  73. <span class="bg-gray-100 text-gray-600 text-xs px-2 py-0.5 rounded font-mono">{{ row.type }}</span>
  74. </td>
  75. <td class="px-6 py-3 text-xs text-gray-500">
  76. {% if row.last_run_at %}{{ row.last_run_at.strftime('%d/%m/%Y %H:%M') }}
  77. {% else %}<span class="text-gray-300">Jamais</span>{% endif %}
  78. </td>
  79. <td class="px-6 py-3">
  80. {% if row.last_status == 'success' %}
  81. <span class="bg-green-100 text-green-700 text-xs font-medium px-2 py-0.5 rounded-full">✓ succès</span>
  82. {% elif row.last_status == 'error' %}
  83. <span class="bg-red-100 text-red-700 text-xs font-medium px-2 py-0.5 rounded-full">✗ erreur</span>
  84. {% elif row.last_status == 'running' %}
  85. <span class="bg-blue-100 text-blue-700 text-xs font-medium px-2 py-0.5 rounded-full animate-pulse">⟳ en cours</span>
  86. {% else %}
  87. <span class="text-gray-300 text-xs">—</span>
  88. {% endif %}
  89. </td>
  90. <td class="px-6 py-3 text-xs text-gray-500">
  91. {% if row.last_size_human is defined %}{{ row.last_size_human }}
  92. {% elif row.size_human is defined %}{{ row.size_human }}
  93. {% else %}—{% endif %}
  94. </td>
  95. <td class="px-6 py-3 text-right">
  96. <div class="flex items-center justify-end gap-2">
  97. {% if inst_id and row.job_id %}
  98. <form method="post"
  99. action="{{ url_for('network.remote_job_run', inst_id=inst_id, job_id=row.job_id) }}"
  100. onsubmit="return confirm('Lancer « {{ row.name }} » sur {{ inst_name }} ?')">
  101. <button type="submit" class="btn-primary btn-sm">▶ Lancer</button>
  102. </form>
  103. {% elif not inst_id and row.job_id %}
  104. <form method="post"
  105. action="{{ url_for('jobs.job_run_now', job_id=row.job_id) }}"
  106. onsubmit="return confirm('Lancer « {{ row.name }} » maintenant ?')">
  107. <button type="submit" class="btn-primary btn-sm">▶ Lancer</button>
  108. </form>
  109. {% endif %}
  110. {% if inst_id and row.job_id %}
  111. <form method="post"
  112. action="{{ url_for('network.archive_pull_latest', inst_id=inst_id, remote_job_id=row.job_id) }}"
  113. onsubmit="return confirm('Rapatrier la dernière archive de « {{ row.name }} » depuis {{ inst_name }} ?')">
  114. <button type="submit" class="btn-ghost btn-sm">← Rapatrier</button>
  115. </form>
  116. {% endif %}
  117. </div>
  118. </td>
  119. </tr>
  120. {% endfor %}
  121. </tbody>
  122. </table>
  123. </div>
  124. {% endif %}
  125. </div>
  126. {% endmacro %}
  127. {# ── Instance locale ─────────────────────────────────────────────── #}
  128. {{ instance_section(instance_name, instance_url, local_jobs_data) }}
  129. {# ── Instances distantes ──────────────────────────────────────────── #}
  130. {% if not instances %}
  131. <div class="bg-white rounded-xl border border-gray-200 px-6 py-10 text-center text-gray-400">
  132. <p>Aucune instance distante enregistrée.</p>
  133. <a href="{{ url_for('network.remote_instance_new') }}" class="mt-2 inline-block text-blue-600 hover:underline text-sm">
  134. Ajouter une instance →
  135. </a>
  136. </div>
  137. {% else %}
  138. {% for inst in instances %}
  139. {{ instance_section(inst.name, inst.url_display, inst.remote_runs,
  140. inst_id=inst.id, inst_status=inst.status, last_seen=inst.last_seen) }}
  141. {% endfor %}
  142. {% endif %}
  143. {% endblock %}