ci/woodpecker/push/tests Pipeline was successful
Expose /jobs, /dedup, and /events routes backed by a new EventBus that broadcasts job lifecycle and per-job progress over SSE. Serve a static dashboard from front/ that lists pending and in-progress jobs, streams agent activity, and shows completed reviews. Also add migrate-apply/migrate-generate scripts that load .env, update the Containerfile to ship the front assets and use the apply script, and refresh the roadmap.
62 lines
1.8 KiB
JavaScript
62 lines
1.8 KiB
JavaScript
// Carte d'un job. Le détail (id, event, date) est replié par défaut ; le
|
|
// bouton « Watch activity » remonte un évènement `job-select` que le dashboard
|
|
// écoute pour ouvrir le flux d'activité du job.
|
|
|
|
import { escapeHtml, relativeTime } from "./api.js";
|
|
|
|
class JobCard extends HTMLElement {
|
|
set job(value) {
|
|
this._job = value;
|
|
this.render();
|
|
}
|
|
|
|
get job() {
|
|
return this._job;
|
|
}
|
|
|
|
set selected(value) {
|
|
this._selected = Boolean(value);
|
|
this.render();
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.render();
|
|
}
|
|
|
|
render() {
|
|
if (!this._job) return;
|
|
const job = this._job;
|
|
|
|
// Préserve l'état replié/déplié à travers les re-rendus (mises à jour SSE).
|
|
const open = this._details?.open ? " open" : "";
|
|
|
|
this.innerHTML = `
|
|
<details class="job" data-selected="${this._selected ? "true" : "false"}"${open}>
|
|
<summary class="job-head">
|
|
<span class="chev" aria-hidden="true"></span>
|
|
<span class="job-repo">${escapeHtml(job.repo)}</span>
|
|
<span class="job-pr">#${job.pr}</span>
|
|
</summary>
|
|
<div class="job-info">
|
|
<dl>
|
|
<div><dt>Job</dt><dd>${job.id}</dd></div>
|
|
${job.event_id != null ? `<div><dt>Event</dt><dd>${job.event_id}</dd></div>` : ""}
|
|
<div><dt>Created</dt><dd>${escapeHtml(relativeTime(job.created_at))}</dd></div>
|
|
</dl>
|
|
<button class="button button-secondary job-watch" type="button">Watch activity</button>
|
|
</div>
|
|
</details>`;
|
|
|
|
this._details = this.querySelector("details");
|
|
this.querySelector(".job-watch").addEventListener("click", () => this.select());
|
|
}
|
|
|
|
select() {
|
|
this.dispatchEvent(
|
|
new CustomEvent("job-select", { detail: { id: this._job.id }, bubbles: true }),
|
|
);
|
|
}
|
|
}
|
|
|
|
customElements.define("steep-job-card", JobCard);
|