fix: la pressione del pulsante di chiusura dei ticket nel gruppo 'chiudi a fine giornata' non provoca più l'attivazione dell'azione di consuntivazione a fine giornata
This commit is contained in:
@@ -354,6 +354,25 @@ body {
|
|||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-brand-action {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-brand-action:hover {
|
||||||
|
color: var(--error);
|
||||||
|
background: var(--error-bg);
|
||||||
|
border-color: rgba(220, 38, 38, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.nav-menu {
|
.nav-menu {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: var(--space-md);
|
padding: var(--space-md);
|
||||||
|
|||||||
+11
-1
@@ -18,10 +18,20 @@
|
|||||||
<body>
|
<body>
|
||||||
<!-- Sidebar Navigation -->
|
<!-- Sidebar Navigation -->
|
||||||
<nav class="sidebar" id="sidebar">
|
<nav class="sidebar" id="sidebar">
|
||||||
<div class="sidebar-brand" style="border-bottom:none; padding-bottom:4px;">
|
<div class="sidebar-brand" style="border-bottom:none; padding-bottom:4px; display:flex; align-items:center; justify-content:space-between;">
|
||||||
|
<div style="display:flex; align-items:center; gap:var(--space-md);">
|
||||||
<div class="brand-icon">⚡</div>
|
<div class="brand-icon">⚡</div>
|
||||||
<span class="brand-text">OTRS Turbo</span>
|
<span class="brand-text">OTRS Turbo</span>
|
||||||
</div>
|
</div>
|
||||||
|
<button id="btn-close-end-of-day" class="btn-brand-action" title="Chiudi i ticket inseriti nel gruppo CHIUDI A FINE GIORNATA">
|
||||||
|
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:18px; height:18px;">
|
||||||
|
<path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" />
|
||||||
|
<rect x="9" y="3" width="6" height="4" rx="1" />
|
||||||
|
<path d="M9 12h6M9 16h4" />
|
||||||
|
<line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="sidebar-timer" id="daily-timer"
|
<div class="sidebar-timer" id="daily-timer"
|
||||||
style="padding:var(--space-md) var(--space-lg) var(--space-lg) var(--space-lg); border-bottom:1px solid var(--border-subtle); font-size:0.75rem; color:var(--text-secondary); font-family:monospace; line-height:1.2;">
|
style="padding:var(--space-md) var(--space-lg) var(--space-lg) var(--space-lg); border-bottom:1px solid var(--border-subtle); font-size:0.75rem; color:var(--text-secondary); font-family:monospace; line-height:1.2;">
|
||||||
<span class="timer-display">0 / 480 | 480</span>
|
<span class="timer-display">0 / 480 | 480</span>
|
||||||
|
|||||||
@@ -204,6 +204,34 @@ const App = {
|
|||||||
refreshBtn.addEventListener('click', () => this.refreshLookups());
|
refreshBtn.addEventListener('click', () => this.refreshLookups());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind button close end of day tickets
|
||||||
|
const btnCloseEod = document.getElementById('btn-close-end-of-day');
|
||||||
|
if (btnCloseEod) {
|
||||||
|
btnCloseEod.addEventListener('click', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
const confirmed = await this.confirm(
|
||||||
|
'Chiusura Ticket Fine Giornata',
|
||||||
|
'Sei sicuro di voler chiudere tutti i ticket nel gruppo "CHIUDI A FINE GIORNATA"?\nI ticket rimarranno nel gruppo.'
|
||||||
|
);
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await this.api('/api/groups/close-end-of-day', { method: 'POST' });
|
||||||
|
if (res.count > 0) {
|
||||||
|
Toast.success(res.message);
|
||||||
|
} else {
|
||||||
|
Toast.info(res.message);
|
||||||
|
}
|
||||||
|
this.updateSidebarBadges();
|
||||||
|
this.route();
|
||||||
|
} catch (err) {
|
||||||
|
Toast.error('Errore durante la chiusura dei ticket: ' + err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Initial route
|
// Initial route
|
||||||
window.addEventListener('resize', () => this.updateHeaderHeight());
|
window.addEventListener('resize', () => this.updateHeaderHeight());
|
||||||
setTimeout(() => this.updateHeaderHeight(), 100);
|
setTimeout(() => this.updateHeaderHeight(), 100);
|
||||||
@@ -622,6 +650,9 @@ const App = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const triggerAction = async (e) => {
|
const triggerAction = async (e) => {
|
||||||
|
if (e && e.target && e.target.closest('#btn-close-end-of-day')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const confirmed = await this.confirm(
|
const confirmed = await this.confirm(
|
||||||
|
|||||||
+79
-1
@@ -1,7 +1,7 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const pool = require('../db');
|
const pool = require('../db');
|
||||||
const { db } = require('../activityDb');
|
const { db, logAttivita } = require('../activityDb');
|
||||||
|
|
||||||
// Helper to fetch details of a list of tickets from OTRS DB
|
// Helper to fetch details of a list of tickets from OTRS DB
|
||||||
async function fetchTicketsDetails(ticketIds) {
|
async function fetchTicketsDetails(ticketIds) {
|
||||||
@@ -246,5 +246,83 @@ router.delete('/:id/tickets/:ticket_id', (req, res) => {
|
|||||||
res.status(500).json({ error: 'Errore nella rimozione del ticket', message: err.message });
|
res.status(500).json({ error: 'Errore nella rimozione del ticket', message: err.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// POST /api/groups/close-end-of-day - Close all tickets in 'CHIUDI A FINE GIORNATA' group
|
||||||
|
router.post('/close-end-of-day', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const operatorId = parseInt(req.headers['x-agent-id'] || '1', 10);
|
||||||
|
|
||||||
|
// Find default group 'CHIUDI A FINE GIORNATA'
|
||||||
|
const closeGroup = db.prepare('SELECT id FROM ticket_groups WHERE UPPER(nome) = ?').get('CHIUDI A FINE GIORNATA');
|
||||||
|
if (!closeGroup) {
|
||||||
|
return res.status(404).json({ error: 'Gruppo "CHIUDI A FINE GIORNATA" non trovato' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupMembers = db.prepare('SELECT ticket_id FROM ticket_group_members WHERE group_id = ?').all(closeGroup.id);
|
||||||
|
if (groupMembers.length === 0) {
|
||||||
|
return res.json({ success: true, count: 0, message: 'Nessun ticket presente nel gruppo "CHIUDI A FINE GIORNATA"' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupTicketIds = groupMembers.map(m => m.ticket_id);
|
||||||
|
const placeholders = groupTicketIds.map((_, i) => `$${i + 1}`).join(', ');
|
||||||
|
|
||||||
|
// Get open tickets in group (not already in a closed state)
|
||||||
|
const openTicketsQuery = `
|
||||||
|
SELECT t.id
|
||||||
|
FROM ticket t
|
||||||
|
JOIN ticket_state ts ON t.ticket_state_id = ts.id
|
||||||
|
JOIN ticket_state_type tst ON ts.type_id = tst.id
|
||||||
|
WHERE t.id IN (${placeholders})
|
||||||
|
AND LOWER(tst.name) NOT LIKE '%closed%'
|
||||||
|
`;
|
||||||
|
const openRes = await pool.query(openTicketsQuery, groupTicketIds);
|
||||||
|
const openTicketIds = openRes.rows.map(r => r.id);
|
||||||
|
|
||||||
|
if (openTicketIds.length === 0) {
|
||||||
|
return res.json({ success: true, count: 0, message: 'Tutti i ticket nel gruppo "CHIUDI A FINE GIORNATA" risultano già chiusi' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve closed state ID (closed successful / closed fallback)
|
||||||
|
const closedStateRes = await pool.query(
|
||||||
|
`SELECT id FROM ticket_state WHERE name = 'closed successful' OR name = 'chiuso con successo' LIMIT 1`
|
||||||
|
);
|
||||||
|
let stateId;
|
||||||
|
if (closedStateRes.rows.length > 0) {
|
||||||
|
stateId = closedStateRes.rows[0].id;
|
||||||
|
} else {
|
||||||
|
const fallbackRes = await pool.query(
|
||||||
|
`SELECT ts.id FROM ticket_state ts JOIN ticket_state_type tst ON ts.type_id = tst.id WHERE tst.name = 'closed' LIMIT 1`
|
||||||
|
);
|
||||||
|
stateId = fallbackRes.rows[0]?.id || 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatePlaceholders = openTicketIds.map((_, i) => `$${i + 1}`).join(', ');
|
||||||
|
const updateQuery = `
|
||||||
|
UPDATE ticket
|
||||||
|
SET ticket_state_id = $${openTicketIds.length + 1},
|
||||||
|
ticket_lock_id = 1,
|
||||||
|
change_time = NOW(),
|
||||||
|
change_by = $${openTicketIds.length + 2}
|
||||||
|
WHERE id IN (${updatePlaceholders})
|
||||||
|
`;
|
||||||
|
await pool.query(updateQuery, [...openTicketIds, stateId, operatorId]);
|
||||||
|
|
||||||
|
// Log activity
|
||||||
|
logAttivita({
|
||||||
|
agente_id: operatorId,
|
||||||
|
titolo_azione: 'Chiusura ticket gruppo CHIUDI A FINE GIORNATA',
|
||||||
|
azione: { closed_count: openTicketIds.length, ticket_ids: openTicketIds },
|
||||||
|
esito: 'successo',
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
count: openTicketIds.length,
|
||||||
|
message: `${openTicketIds.length} ticket del gruppo "CHIUDI A FINE GIORNATA" chius${openTicketIds.length === 1 ? 'o' : 'i'} con successo!`
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Errore nella chiusura ticket fine giornata:', err);
|
||||||
|
res.status(500).json({ error: 'Errore nella chiusura ticket', message: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user