Files
ltbxd-actorle/assets/controllers/import_status_controller.js
thibaud-leclere 6a844542ad feat: replace notifications with import status in profile dropdown
Remove the notification system entirely and show import progress
directly in the user dropdown menu. Block new imports while one
is already running.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 21:34:05 +02:00

100 lines
3.0 KiB
JavaScript

import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['item', 'importBtn', 'badge'];
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();
this.badgeTarget.hidden = true;
}
_showActive(data) {
this.importBtnTarget.disabled = true;
this.importBtnTarget.textContent = 'Import en cours\u2026';
const progress = data.totalBatches > 0
? Math.round((data.processedBatches / data.totalBatches) * 100)
: 0;
this._setStatus(`${progress}% — ${data.totalFilms} films`, 'active');
this.badgeTarget.hidden = true;
}
_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');
this.badgeTarget.hidden = true;
}
_showFailed() {
this.importBtnTarget.disabled = false;
this.importBtnTarget.textContent = 'Importer ses films';
this._setStatus('Dernier import : échoué', 'failed');
this.badgeTarget.hidden = true;
}
_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();
}
}