225 lines
9.9 KiB
JavaScript
225 lines
9.9 KiB
JavaScript
/**
|
|
* ShortcutsView — Gestione Shortcuts e Scorciatoie da Tastiera
|
|
* Visualizza e permette la personalizzazione delle scorciatoie da tastiera.
|
|
*/
|
|
const ShortcutsView = {
|
|
editingId: null,
|
|
|
|
render() {
|
|
const container = document.getElementById('view-container');
|
|
const shortcuts = this.getShortcutsList();
|
|
|
|
container.innerHTML = `
|
|
<div class="view-header" style="padding:var(--space-md) var(--space-xl); display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:var(--space-sm); border-bottom:1px solid var(--border-subtle);">
|
|
<div style="display:flex; align-items:center; gap:var(--space-sm);">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:22px; height:22px; color:var(--accent-primary);">
|
|
<rect x="2" y="4" width="20" height="16" rx="2" />
|
|
<path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M6 12h.01M10 12h.01M14 12h.01M18 12h.01M8 16h8" />
|
|
</svg>
|
|
<span style="font-weight:700; font-size:1.1rem;">Shortcuts e Scorciatoie da Tastiera</span>
|
|
</div>
|
|
<button id="sc-reset-all" class="btn btn-ghost btn-sm" style="font-size:0.82rem;">
|
|
🔄 Ripristina Predefiniti
|
|
</button>
|
|
</div>
|
|
|
|
<div style="padding:var(--space-lg) var(--space-xl); max-width:960px;">
|
|
<p style="color:var(--text-secondary); font-size:0.88rem; margin-bottom:var(--space-lg); line-height:1.5;">
|
|
Personalizza o abilita/disabilita le scorciatoie da tastiera rapide per velocizzare la gestione dei ticket e delle consuntivazioni.
|
|
</p>
|
|
|
|
<div style="display:flex; flex-direction:column; gap:var(--space-md);">
|
|
${shortcuts.map(sc => this.buildShortcutRow(sc)).join('')}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal per la registrazione di una nuova combinazione -->
|
|
<div id="sc-modal" class="modal" style="display:none;">
|
|
<div class="modal-content" style="max-width:440px; text-align:center;">
|
|
<h3 style="margin-bottom:var(--space-xs); font-size:1.1rem;" id="sc-modal-title">Modifica Shortcut</h3>
|
|
<p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:var(--space-lg);">
|
|
Premi la combinazione di tasti desiderata sulla tastiera (es. <kbd style="background:var(--bg-tertiary); padding:2px 6px; border-radius:4px; border:1px solid var(--border-subtle);">Ctrl</kbd> + <kbd style="background:var(--bg-tertiary); padding:2px 6px; border-radius:4px; border:1px solid var(--border-subtle);">Invio</kbd>).
|
|
</p>
|
|
|
|
<div id="sc-key-recorder" tabindex="0" style="padding:var(--space-lg); background:var(--bg-tertiary); border:2px dashed var(--accent-primary); border-radius:var(--radius-md); font-size:1.2rem; font-weight:700; color:var(--accent-primary); outline:none; cursor:pointer; min-height:70px; display:flex; align-items:center; justify-content:center;">
|
|
Premi i tasti...
|
|
</div>
|
|
|
|
<div style="display:flex; justify-content:flex-end; gap:var(--space-sm); margin-top:var(--space-lg);">
|
|
<button id="sc-modal-cancel" class="btn btn-ghost btn-sm">Annulla</button>
|
|
<button id="sc-modal-save" class="btn btn-primary btn-sm" disabled>Conferma</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
this.bindEvents();
|
|
},
|
|
|
|
getShortcutsList() {
|
|
const list = [
|
|
{
|
|
id: 'create_ticket_open',
|
|
name: 'Crea Nuovo Ticket',
|
|
description: 'Apre la pagina di creazione di un nuovo ticket',
|
|
context: 'Globale',
|
|
defaultCombo: { ctrl: true, alt: true, shift: false, key: 'n', enabled: true }
|
|
},
|
|
{
|
|
id: 'create_ticket_submit',
|
|
name: 'Invia Nuovo Ticket',
|
|
description: 'Invia il modulo e crea un nuovo ticket',
|
|
context: 'Pagina Nuovo Ticket',
|
|
defaultCombo: { ctrl: true, alt: false, shift: false, key: 'Enter', enabled: true }
|
|
},
|
|
{
|
|
id: 'quick_edit_save',
|
|
name: 'Salva Modifiche / Invia Nota',
|
|
description: 'Salva le modifiche apportate nei campi di Modifica Rapida o invia la nota interna nel dettaglio ticket',
|
|
context: 'Pagina Dettaglio Ticket',
|
|
defaultCombo: { ctrl: true, alt: false, shift: false, key: 'Enter', enabled: true }
|
|
},
|
|
{
|
|
id: 'pdf_save_edited',
|
|
name: 'Salva Immagine / Allegato / PDF Modificato',
|
|
description: 'Scarica l\'immagine o il PDF con le correzioni e annotazioni apportate',
|
|
context: 'Anteprima Allegato / PDF / Immagine',
|
|
defaultCombo: { ctrl: true, alt: false, shift: false, key: 'Enter', enabled: true }
|
|
}
|
|
];
|
|
|
|
return list.map(item => {
|
|
const current = App.getShortcut(item.id);
|
|
return {
|
|
...item,
|
|
currentCombo: current
|
|
};
|
|
});
|
|
},
|
|
|
|
buildShortcutRow(sc) {
|
|
const combo = sc.currentCombo;
|
|
const label = App.formatShortcutLabel(combo);
|
|
const keysHtml = label.split(' + ').map(k => `
|
|
<kbd style="background:var(--bg-tertiary); color:var(--text-primary); border:1px solid var(--border-subtle); border-bottom-width:2px; padding:3px 8px; border-radius:4px; font-family:monospace; font-weight:700; font-size:0.85rem; box-shadow:0 1px 2px rgba(0,0,0,0.15);">
|
|
${App.escapeHtml(k)}
|
|
</kbd>
|
|
`).join(' + ');
|
|
|
|
return `
|
|
<div class="card" style="display:flex; align-items:center; justify-content:space-between; padding:var(--space-md) var(--space-lg); gap:var(--space-md); border:1px solid var(--border-subtle); flex-wrap:wrap;">
|
|
<div style="flex:1; min-width:240px;">
|
|
<div style="font-weight:700; font-size:0.95rem; color:var(--text-primary); display:flex; align-items:center; gap:var(--space-xs);">
|
|
${App.escapeHtml(sc.name)}
|
|
<span class="badge" style="background:var(--bg-tertiary); color:var(--text-muted); font-size:0.72rem; font-weight:600; padding:2px 8px; border-radius:10px;">
|
|
${App.escapeHtml(sc.context)}
|
|
</span>
|
|
</div>
|
|
<div style="font-size:0.82rem; color:var(--text-secondary); margin-top:3px;">
|
|
${App.escapeHtml(sc.description)}
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display:flex; align-items:center; gap:var(--space-lg);">
|
|
<div style="display:flex; align-items:center; gap:4px;">
|
|
${combo.enabled ? keysHtml : '<span style="color:var(--text-muted); font-size:0.82rem; font-style:italic;">Disabilitato</span>'}
|
|
</div>
|
|
|
|
<div style="display:flex; align-items:center; gap:var(--space-xs);">
|
|
<button class="btn btn-ghost btn-sm sc-edit-btn" data-id="${sc.id}" style="height:32px; padding:4px 10px; font-size:0.82rem;">
|
|
✏️ Modifica
|
|
</button>
|
|
<label class="html-toggle-container" style="margin:0; cursor:pointer;" title="Abilita/Disabilita shortcut">
|
|
<input type="checkbox" class="sc-toggle-input" data-id="${sc.id}" ${combo.enabled ? 'checked' : ''} />
|
|
<span style="font-size:0.8rem; color:var(--text-secondary);">${combo.enabled ? 'Attivo' : 'Off'}</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
},
|
|
|
|
bindEvents() {
|
|
// Reset all
|
|
document.getElementById('sc-reset-all')?.addEventListener('click', () => {
|
|
if (confirm('Ripristinare tutte le scorciatoie da tastiera ai valori predefiniti?')) {
|
|
localStorage.removeItem('otrs_shortcuts');
|
|
Toast.success('Shortcuts ripristinati ai valori predefiniti');
|
|
this.render();
|
|
}
|
|
});
|
|
|
|
// Toggles
|
|
document.querySelectorAll('.sc-toggle-input').forEach(toggle => {
|
|
toggle.addEventListener('change', (e) => {
|
|
const id = e.target.dataset.id;
|
|
const current = App.getShortcut(id);
|
|
current.enabled = e.target.checked;
|
|
App.saveCustomShortcut(id, current);
|
|
Toast.success(`Shortcut ${current.enabled ? 'abilitato' : 'disabilitato'}`);
|
|
this.render();
|
|
});
|
|
});
|
|
|
|
// Edit button
|
|
let recordedCombo = null;
|
|
const modal = document.getElementById('sc-modal');
|
|
const recorder = document.getElementById('sc-key-recorder');
|
|
const saveBtn = document.getElementById('sc-modal-save');
|
|
const cancelBtn = document.getElementById('sc-modal-cancel');
|
|
|
|
document.querySelectorAll('.sc-edit-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
this.editingId = btn.dataset.id;
|
|
const item = this.getShortcutsList().find(x => x.id === this.editingId);
|
|
document.getElementById('sc-modal-title').textContent = `Modifica: ${item ? item.name : ''}`;
|
|
recordedCombo = null;
|
|
recorder.innerHTML = 'Premi i tasti sulla tastiera...';
|
|
saveBtn.disabled = true;
|
|
modal.style.display = 'flex';
|
|
recorder.focus();
|
|
});
|
|
});
|
|
|
|
cancelBtn?.addEventListener('click', () => {
|
|
modal.style.display = 'none';
|
|
});
|
|
|
|
recorder?.addEventListener('keydown', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
// Ignore lone modifier presses
|
|
if (['Control', 'Alt', 'Shift', 'Meta'].includes(e.key)) {
|
|
return;
|
|
}
|
|
|
|
const ctrl = e.ctrlKey || e.metaKey;
|
|
const alt = e.altKey;
|
|
const shift = e.shiftKey;
|
|
let key = e.key;
|
|
|
|
if (key === ' ') key = 'Space';
|
|
|
|
recordedCombo = { ctrl, alt, shift, key, enabled: true };
|
|
const label = App.formatShortcutLabel(recordedCombo);
|
|
recorder.innerHTML = label.split(' + ').map(k => `
|
|
<kbd style="background:var(--bg-secondary); color:var(--accent-primary); border:1px solid var(--accent-primary); padding:4px 10px; border-radius:6px; font-family:monospace; margin:0 2px;">
|
|
${App.escapeHtml(k)}
|
|
</kbd>
|
|
`).join(' + ');
|
|
|
|
saveBtn.disabled = false;
|
|
});
|
|
|
|
saveBtn?.addEventListener('click', () => {
|
|
if (this.editingId && recordedCombo) {
|
|
App.saveCustomShortcut(this.editingId, recordedCombo);
|
|
Toast.success('Combinazione salvata!');
|
|
modal.style.display = 'none';
|
|
this.render();
|
|
}
|
|
});
|
|
}
|
|
};
|