prvng_platform/crates/provisioning-daemon/ui/templates/pages/job_detail.html

135 lines
5.2 KiB
HTML

{% extends "base.html" %}
{% import "macros/ui.html" as m %}
{% block title %}Job {{ job_id | truncate(length=12, end="") }}{% endblock %}
{% block nav_jobs %}btn-active{% endblock %}
{% block content %}
<div class="flex items-center gap-3 mb-6">
<a href="/ui/jobs" class="btn btn-ghost btn-xs font-mono">← Jobs</a>
<h1 class="text-xl font-bold font-mono">Job Detail</h1>
</div>
{% if job %}
{# ── Meta row ── #}
<div class="card bg-base-200 shadow mb-4">
<div class="card-body p-4">
<div class="flex flex-wrap gap-6">
<div>
<div class="text-xs text-base-content/40 mb-0.5">ID</div>
<div class="font-mono text-xs text-base-content/70">{{ job.id | default(value=job_id) }}</div>
</div>
<div>
<div class="text-xs text-base-content/40 mb-0.5">Name</div>
<div class="font-mono text-sm font-semibold">{{ job.name | default(value="—") }}</div>
</div>
<div>
<div class="text-xs text-base-content/40 mb-0.5">Status</div>
{% set s = job.status | default(value="unknown") %}
{% if s == "Running" or s == "running" %}
<span class="badge badge-info badge-sm">running</span>
{% elif s == "Completed" or s == "completed" or s == "Success" %}
<span class="badge badge-success badge-sm">completed</span>
{% elif s == "Failed" or s == "failed" or s == "Error" %}
<span class="badge badge-error badge-sm">failed</span>
{% elif s == "Pending" or s == "pending" %}
<span class="badge badge-warning badge-sm">pending</span>
{% else %}
<span class="badge badge-ghost badge-sm">{{ s }}</span>
{% endif %}
</div>
{% if job.created_at %}
<div>
<div class="text-xs text-base-content/40 mb-0.5">Created</div>
<div class="text-xs font-mono">{{ job.created_at }}</div>
</div>
{% endif %}
{% if job.started_at %}
<div>
<div class="text-xs text-base-content/40 mb-0.5">Started</div>
<div class="text-xs font-mono">{{ job.started_at }}</div>
</div>
{% endif %}
{% if job.completed_at %}
<div>
<div class="text-xs text-base-content/40 mb-0.5">Completed</div>
<div class="text-xs font-mono">{{ job.completed_at }}</div>
</div>
{% endif %}
</div>
</div>
</div>
{# ── Error (when present) ── #}
{% if job.error %}
<div class="card bg-error/10 border border-error/30 shadow mb-4">
<div class="card-body p-4">
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-semibold text-error">Error</span>
<button onclick="copyText('job-error-text', this)"
class="btn btn-ghost btn-xs gap-1 font-mono text-base-content/40 hover:text-base-content">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/>
</svg>
copy
</button>
</div>
<pre id="job-error-text" class="text-xs font-mono whitespace-pre-wrap break-all text-error/80">{{ job.error }}</pre>
</div>
</div>
{% endif %}
{# ── Output ── #}
<div class="card bg-base-200 shadow">
<div class="card-body p-4">
<div class="flex items-center justify-between mb-3">
<span class="text-xs font-semibold text-base-content/50 uppercase tracking-wider">Output</span>
{% if job.output %}
<button onclick="copyText('job-output-text', this)"
class="btn btn-ghost btn-xs gap-1 font-mono text-base-content/40 hover:text-base-content"
title="Copy output to clipboard">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/>
</svg>
copy
</button>
{% endif %}
</div>
{% if job.output %}
<pre id="job-output-text"
class="text-xs font-mono whitespace-pre-wrap break-words leading-relaxed overflow-auto max-h-[70vh] bg-base-300/40 rounded p-3">{{ job.output }}</pre>
{% else %}
<p class="text-xs text-base-content/30 font-mono">No output.</p>
{% endif %}
</div>
</div>
{% else %}
<div class="alert alert-warning">
<span class="font-mono text-sm">Job <code>{{ job_id }}</code> not found — orchestrator may be unavailable.</span>
</div>
{% endif %}
{% endblock %}
{% block scripts %}
<script>
function copyText(id, btn) {
var el = document.getElementById(id);
if (!el) return;
navigator.clipboard.writeText(el.textContent).then(function() {
var orig = btn.innerHTML;
btn.innerHTML = '<svg class="w-3.5 h-3.5 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg> copied';
setTimeout(function() { btn.innerHTML = orig; }, 2000);
});
}
</script>
{% endblock %}