/** * @fileoverview Cron Jobs UI mixed into * CodemanApp.prototype. Renders the job list + create/edit form in the * #cronModal, and reacts to cron:* SSE events. * * @mixin Extends CodemanApp.prototype via Object.assign * @dependency app.js, api-client.js, constants.js (escapeHtml) */ Object.assign(CodemanApp.prototype, { // ── SSE handlers ────────────────────────────────────────────────────────── _onCronJobsChanged(data) { if (data && Array.isArray(data.jobs)) { this._cronJobs = data.jobs; if (this._isCronOpen()) this.renderCronJobs(); } else if (this._isCronOpen()) { this.refreshCron(); } }, _onCronRunChanged() { // A run's status changed — refresh the list so lastStatus stays current. if (this._isCronOpen()) this.refreshCron(); }, // ── Modal open/close ────────────────────────────────────────────────────── _isCronOpen() { const el = document.getElementById('cronModal'); return !!el && el.classList.contains('active'); }, openCron() { const el = document.getElementById('cronModal'); if (!el) return; el.classList.add('active'); this.cancelCronJobForm(); this.refreshCron(); }, closeCron() { const el = document.getElementById('cronModal'); if (el) el.classList.remove('active'); }, async refreshCron() { const jobs = await this._apiJson('/api/cron/jobs'); this._cronJobs = Array.isArray(jobs) ? jobs : []; this.renderCronJobs(); }, // ── List rendering ──────────────────────────────────────────────────────── renderCronJobs() { const list = document.getElementById('cronJobList'); if (!list) return; const jobs = this._cronJobs || []; if (jobs.length === 0) { list.innerHTML = '