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

232 lines
10 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: 'ricerca_spudorata_open',
name: 'Apri Ricerca Spudorata',
description: 'Naviga direttamente alla pagina di Ricerca Spudorata o mette a fuoco il campo di ricerca',
context: 'Globale',
defaultCombo: { ctrl: true, alt: false, shift: true, key: 'f', 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();
}
});
}
};