Replace batch-level progress (processedBatches/totalBatches) with film-level progress (processedFilms/totalFilms) for smoother UI updates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
96 lines
2.9 KiB
JavaScript
96 lines
2.9 KiB
JavaScript
import { Controller } from '@hotwired/stimulus';
|
|
|
|
export default class extends Controller {
|
|
static targets = ['item', 'importBtn'];
|
|
|
|
connect() {
|
|
this._poll();
|
|
this._interval = setInterval(() => this._poll(), 5000);
|
|
this._onImportStarted = () => this._poll();
|
|
document.addEventListener('import:started', this._onImportStarted);
|
|
}
|
|
|
|
disconnect() {
|
|
clearInterval(this._interval);
|
|
document.removeEventListener('import:started', this._onImportStarted);
|
|
}
|
|
|
|
async _poll() {
|
|
try {
|
|
const response = await fetch('/api/imports/latest');
|
|
if (!response.ok) return;
|
|
|
|
const data = await response.json();
|
|
this._update(data);
|
|
} catch (e) {
|
|
// silently ignore
|
|
}
|
|
}
|
|
|
|
_update(data) {
|
|
if (!data) {
|
|
this._showDefault();
|
|
return;
|
|
}
|
|
|
|
const isActive = data.status === 'pending' || data.status === 'processing';
|
|
|
|
if (isActive) {
|
|
this._showActive(data);
|
|
} else if (data.status === 'completed') {
|
|
this._showCompleted(data);
|
|
} else if (data.status === 'failed') {
|
|
this._showFailed();
|
|
} else {
|
|
this._showDefault();
|
|
}
|
|
}
|
|
|
|
_showDefault() {
|
|
this.importBtnTarget.disabled = false;
|
|
this.importBtnTarget.textContent = 'Importer ses films';
|
|
this._removeStatus();
|
|
}
|
|
|
|
_showActive(data) {
|
|
this.importBtnTarget.disabled = true;
|
|
this.importBtnTarget.textContent = 'Import en cours\u2026';
|
|
|
|
const progress = data.totalFilms > 0
|
|
? Math.round((data.processedFilms / data.totalFilms) * 100)
|
|
: 0;
|
|
|
|
this._setStatus(`${progress}% — ${data.processedFilms}/${data.totalFilms} films`, 'active');
|
|
}
|
|
|
|
_showCompleted(data) {
|
|
this.importBtnTarget.disabled = false;
|
|
this.importBtnTarget.textContent = 'Importer ses films';
|
|
|
|
const imported = data.totalFilms - data.failedFilms;
|
|
this._setStatus(`Dernier import : ${imported}/${data.totalFilms} films`, 'completed');
|
|
}
|
|
|
|
_showFailed() {
|
|
this.importBtnTarget.disabled = false;
|
|
this.importBtnTarget.textContent = 'Importer ses films';
|
|
this._setStatus('Dernier import : échoué', 'failed');
|
|
}
|
|
|
|
_setStatus(text, type) {
|
|
let statusEl = this.itemTarget.querySelector('.import-status-text');
|
|
if (!statusEl) {
|
|
statusEl = document.createElement('span');
|
|
statusEl.className = 'import-status-text';
|
|
this.itemTarget.appendChild(statusEl);
|
|
}
|
|
statusEl.textContent = text;
|
|
statusEl.className = `import-status-text import-status-${type}`;
|
|
}
|
|
|
|
_removeStatus() {
|
|
const statusEl = this.itemTarget.querySelector('.import-status-text');
|
|
if (statusEl) statusEl.remove();
|
|
}
|
|
}
|