Files
otrs-turbo/public/js/views/userConfig.js
T

295 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* userConfig.js
* User Configurations View: Upload, preview and manage custom signature images stored in SQLite.
*/
const UserConfigView = {
async render() {
const container = document.getElementById('view-container');
container.innerHTML = '<div class="loading-screen"><div class="spinner"></div><p>Caricamento configurazioni utente...</p></div>';
const titleEl = document.getElementById('page-title');
if (titleEl) {
titleEl.textContent = 'Configurazioni Utente';
}
try {
const [signatures, shapes] = await Promise.all([
App.api('/api/config/user-signatures'),
App.api('/api/config/user-shapes')
]);
this.renderUI(container, signatures, shapes);
} catch (err) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">⚠️</div>
<div class="empty-state-text">Errore caricamento configurazioni utente</div>
<div class="empty-state-sub">${App.escapeHtml(err.message)}</div>
</div>
`;
}
},
renderUI(container, signatures = [], shapes = []) {
container.innerHTML = `
<div class="user-config-page">
<!-- Section 1: Custom Shapes -->
<div class="card" style="margin-bottom: var(--space-lg);">
<div class="card-title" style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
<span>📐 Forme Personalizzate & Preferiti</span>
<span style="font-size:0.8rem; font-weight:normal; color:var(--text-muted);">Configura forme salvate per l'editor PDF</span>
</div>
<div style="background: var(--bg-tertiary); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 16px; margin-bottom: 20px;">
<h4 style="margin-top:0; margin-bottom:12px; font-size:0.95rem; color:var(--text-primary);">Crea Nuova Forma Personalizzata</h4>
<div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap:12px; align-items:flex-end;">
<div>
<label style="font-size:0.8rem; font-weight:600; color:var(--text-secondary); margin-bottom:4px; display:block;">Nome Forma</label>
<input type="text" id="shape-name" class="form-input" placeholder="Es. Evidenziatore Blu, Timbro Approvato..." style="height:34px; font-size:0.85rem;" />
</div>
<div>
<label style="font-size:0.8rem; font-weight:600; color:var(--text-secondary); margin-bottom:4px; display:block;">Tipo Forma</label>
<select id="shape-type" class="form-select" style="height:34px; font-size:0.85rem;">
<option value="rectangle">🟦 Rettangolo</option>
<option value="circle">⚪ Cerchio / Ellisse</option>
</select>
</div>
<div>
<label style="font-size:0.8rem; font-weight:600; color:var(--text-secondary); margin-bottom:4px; display:block;">Colore Riempimento</label>
<div style="display:flex; align-items:center; gap:6px;">
<input type="color" id="shape-fill-color" value="#3b82f6" style="width:34px; height:34px; border:none; border-radius:4px; cursor:pointer; background:none;" />
<label style="font-size:0.78rem; color:var(--text-muted); cursor:pointer;"><input type="checkbox" id="shape-transparent-fill" /> Trasparente</label>
</div>
</div>
<div>
<label style="font-size:0.8rem; font-weight:600; color:var(--text-secondary); margin-bottom:4px; display:block;">Colore Bordo</label>
<input type="color" id="shape-border-color" value="#1e3a8a" style="width:34px; height:34px; border:none; border-radius:4px; cursor:pointer; background:none;" />
</div>
<div>
<label style="font-size:0.8rem; font-weight:600; color:var(--text-secondary); margin-bottom:4px; display:block;">Spessore Bordo (px)</label>
<input type="number" id="shape-border-width" min="0" max="30" value="3" class="form-input" style="height:34px; font-size:0.85rem;" />
</div>
<div style="display:flex; gap:8px;">
<button id="btn-save-shape" class="btn btn-primary btn-sm" style="height:34px; padding:0 16px; font-weight:600; display:inline-flex; align-items:center; gap:6px;">
⭐ Salva Forma
</button>
</div>
</div>
</div>
<!-- Shapes Gallery -->
<div style="display:grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap:14px;">
${shapes.length > 0 ? shapes.map(sh => `
<div class="user-shape-card" style="background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-md); padding:12px; display:flex; flex-direction:column; position:relative; box-shadow:var(--shadow-sm);">
<div style="position:absolute; top:8px; right:8px; display:flex; gap:4px;">
<button class="btn-fav-shape" data-id="${sh.id}" style="background:none; border:none; cursor:pointer; font-size:16px;" title="Aggiungi/Rimuovi Preferito">${sh.is_favorite ? '⭐' : '☆'}</button>
<button class="btn-delete-shape" data-id="${sh.id}" style="background:var(--bg-tertiary); border:1px solid var(--border-subtle); color:var(--danger); border-radius:50%; width:24px; height:24px; cursor:pointer; display:flex; align-items:center; justify-content:center; font-size:11px;" title="Elimina Forma">✕</button>
</div>
<div style="width:100%; height:70px; display:flex; align-items:center; justify-content:center; background:var(--bg-tertiary); border-radius:var(--radius-sm); margin-bottom:8px; border:1px dashed var(--border-subtle);">
<div style="width:50px; height:34px; border-radius:${sh.shape_type === 'circle' ? '50%' : '3px'}; background:${sh.fill_color === 'transparent' ? 'transparent' : sh.fill_color}; border:${sh.border_width}px solid ${sh.border_color}; box-sizing:border-box;"></div>
</div>
<div style="font-weight:600; font-size:0.85rem; color:var(--text-primary); margin-bottom:4px;">
${App.escapeHtml(sh.name)}
</div>
<div style="font-size:0.74rem; color:var(--text-muted);">
${sh.shape_type === 'circle' ? 'Cerchio' : 'Rettangolo'} • Bordo ${sh.border_width}px
</div>
</div>
`).join('') : `
<div style="grid-column: 1 / -1; padding:20px; text-align:center; color:var(--text-muted); font-style:italic;">
Nessuna forma personalizzata salvata.
</div>
`}
</div>
</div>
<!-- Section 2: Signatures -->
<div class="card" style="margin-bottom: var(--space-lg);">
<div class="card-title" style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
<span>✍️ Immagini e Firme Utente</span>
<span style="font-size:0.8rem; font-weight:normal; color:var(--text-muted);">Saranno disponibili per l'apposizione rapida sui PDF</span>
</div>
<div style="background: var(--bg-tertiary); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 16px; margin-bottom: 20px;">
<h4 style="margin-top:0; margin-bottom:12px; font-size:0.95rem; color:var(--text-primary);">Aggiungi nuova immagine / firma</h4>
<div style="display:flex; gap:12px; align-items:flex-end; flex-wrap:wrap;">
<div style="flex:1; min-width:220px;">
<label style="font-size:0.8rem; font-weight:600; color:var(--text-secondary); margin-bottom:4px; display:block;">Nome Firma / Immagine</label>
<input type="text" id="user-sig-name" class="form-input" placeholder="Es. Firma Gabriele Cimaschi, Timbro Aziendale..." style="height:34px; font-size:0.85rem;" />
</div>
<div style="flex:1; min-width:220px;">
<label style="font-size:0.8rem; font-weight:600; color:var(--text-secondary); margin-bottom:4px; display:block;">Seleziona File Immagine (PNG, JPG, SVG)</label>
<input type="file" id="user-sig-file" accept="image/*" class="form-input" style="height:34px; padding:3px 8px; font-size:0.8rem;" />
</div>
<button id="btn-save-user-sig" class="btn btn-primary btn-sm" style="height:34px; padding:0 16px; font-weight:600; display:inline-flex; align-items:center; gap:6px;">
➕ Salva in SQLite
</button>
</div>
</div>
<!-- Signatures Gallery Grid -->
<div id="user-sig-grid" style="display:grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap:16px;">
${signatures.length > 0 ? signatures.map(sig => `
<div class="user-sig-card" style="background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-md); padding:12px; display:flex; flex-direction:column; align-items:center; position:relative; box-shadow:var(--shadow-sm);">
<button class="btn-delete-sig" data-id="${sig.id}" style="position:absolute; top:8px; right:8px; background:var(--bg-tertiary); border:1px solid var(--border-subtle); color:var(--danger); border-radius:50%; width:26px; height:26px; cursor:pointer; display:flex; align-items:center; justify-content:center; font-size:12px;" title="Elimina Firma">✕</button>
<div style="width:100%; height:110px; display:flex; align-items:center; justify-content:center; background:#ffffff; border-radius:var(--radius-sm); border:1px solid var(--border-subtle); padding:8px; margin-bottom:8px;">
<img src="${sig.image_data}" alt="${App.escapeHtml(sig.name)}" style="max-width:100%; max-height:100%; object-fit:contain;" />
</div>
<div style="font-weight:600; font-size:0.85rem; color:var(--text-primary); text-align:center; word-break:break-word;">
${App.escapeHtml(sig.name)}
</div>
<div style="font-size:0.72rem; color:var(--text-muted); margin-top:2px;">
${App.formatDateTime(sig.created_at)}
</div>
</div>
`).join('') : `
<div style="grid-column: 1 / -1; padding:32px; text-align:center; color:var(--text-muted); font-style:italic;">
Nessuna firma salvata. Carica la prima immagine con il modulo qui sopra.
</div>
`}
</div>
</div>
</div>
`;
this.bindEvents(container);
},
bindEvents(container) {
// Shapes handlers
const btnSaveShape = container.querySelector('#btn-save-shape');
if (btnSaveShape) {
btnSaveShape.addEventListener('click', async () => {
const name = container.querySelector('#shape-name').value.trim();
const shapeType = container.querySelector('#shape-type').value;
const isTransparent = container.querySelector('#shape-transparent-fill').checked;
const fillColor = isTransparent ? 'transparent' : container.querySelector('#shape-fill-color').value;
const borderColor = container.querySelector('#shape-border-color').value;
const borderWidth = parseInt(container.querySelector('#shape-border-width').value || 3, 10);
if (!name) {
Toast.warning('Inserisci un nome per la forma.');
return;
}
try {
btnSaveShape.disabled = true;
await App.api('/api/config/user-shapes', {
method: 'POST',
body: JSON.stringify({
name,
shape_type: shapeType,
fill_color: fillColor,
border_color: borderColor,
border_width: borderWidth,
is_favorite: 1
})
});
Toast.success('Forma salvata!');
this.render();
} catch (err) {
Toast.error('Errore salvataggio forma: ' + err.message);
btnSaveShape.disabled = false;
}
});
}
container.querySelectorAll('.btn-fav-shape').forEach(btn => {
btn.addEventListener('click', async (e) => {
e.stopPropagation();
try {
await App.api(`/api/config/user-shapes/${btn.dataset.id}/toggle-favorite`, { method: 'PUT' });
this.render();
} catch (err) {
Toast.error('Errore aggiornamento preferiti: ' + err.message);
}
});
});
container.querySelectorAll('.btn-delete-shape').forEach(btn => {
btn.addEventListener('click', async (e) => {
e.stopPropagation();
const ok = await App.confirm('Elimina Forma', 'Eliminare questa forma personalizzata?', { confirmText: 'Elimina', cancelText: 'Annulla' });
if (ok) {
try {
await App.api(`/api/config/user-shapes/${btn.dataset.id}`, { method: 'DELETE' });
Toast.success('Forma eliminata!');
this.render();
} catch (err) {
Toast.error('Errore eliminazione: ' + err.message);
}
}
});
});
// Signatures handlers
const btnSave = container.querySelector('#btn-save-user-sig');
const inputName = container.querySelector('#user-sig-name');
const inputFile = container.querySelector('#user-sig-file');
if (btnSave && inputFile) {
btnSave.addEventListener('click', async () => {
const name = inputName.value.trim();
const file = inputFile.files[0];
if (!name) {
Toast.warning('Inserisci un nome descrittivo per la firma/immagine.');
return;
}
if (!file) {
Toast.warning('Seleziona un file immagine.');
return;
}
const reader = new FileReader();
reader.onload = async () => {
const base64Data = reader.result;
try {
btnSave.disabled = true;
btnSave.textContent = 'Salvataggio...';
const res = await App.api('/api/config/user-signatures', {
method: 'POST',
body: JSON.stringify({
name,
image_data: base64Data
})
});
Toast.success(res.message || 'Firma salvata!');
this.render();
} catch (err) {
Toast.error('Errore durante il salvataggio: ' + err.message);
btnSave.disabled = false;
btnSave.textContent = '➕ Salva in SQLite';
}
};
reader.readAsDataURL(file);
});
}
container.querySelectorAll('.btn-delete-sig').forEach(btn => {
btn.addEventListener('click', async (e) => {
e.stopPropagation();
const id = btn.dataset.id;
const ok = await App.confirm('Elimina Firma', 'Sei sicuro di voler eliminare questa firma dal database?', { confirmText: 'Elimina', cancelText: 'Annulla' });
if (ok) {
try {
await App.api(`/api/config/user-signatures/${id}`, { method: 'DELETE' });
Toast.success('Firma eliminata!');
this.render();
} catch (err) {
Toast.error('Errore eliminazione: ' + err.message);
}
}
});
});
}
};