feat: aggiunto shortcut per aggiungere il ticket in una nuova scheda. style: spostata consuntivazione accanto ad utente cliente.

This commit is contained in:
Gabriele Cimaschi committed 2026-09-23 14:15:24 +02:00
1 parent 107ba10689
commit ce904756dd
6 files changed
+151 -9

No files matched your search

+84 -2
View File
@@ -12,6 +12,39 @@ const TimeAccountingView = {
viewMode: 'list', // 'list' | 'calendar'
showAllTime: false,
showNonWorkingDays: localStorage.getItem('otrs_ta_show_non_working_days') !== 'false',
groupByTicket: localStorage.getItem('otrs_ta_group_by_ticket') !== 'false',
groupArticles(articles) {
if (!articles || articles.length === 0) return [];
if (!this.groupByTicket) {
return articles.map(a => ({ type: 'single', article: a }));
}
const groups = [];
let currentGroup = null;
for (const a of articles) {
const ticketId = a.ticket_id;
if (currentGroup && currentGroup.ticket_id === ticketId) {
currentGroup.articles.push(a);
} else {
if (currentGroup) {
groups.push(currentGroup);
}
currentGroup = {
type: 'group',
ticket_id: ticketId,
ticket_tn: a.ticket_tn,
ticket_title: a.ticket_title,
articles: [a]
};
}
}
if (currentGroup) {
groups.push(currentGroup);
}
return groups;
},
formatLocalISO(d) {
if (!d) return '';
@@ -174,6 +207,14 @@ const TimeAccountingView = {
<div style="display:flex; gap:var(--space-xs); align-self:flex-end;">
<button id="ta-btn-today" class="btn btn-ghost btn-sm" style="height:36px;">Oggi</button>
<button id="ta-btn-toggle-groupby" class="btn btn-ghost btn-sm" style="height:36px; display:flex; align-items:center; gap:6px; background:${this.groupByTicket ? 'var(--accent-primary)' : 'var(--bg-tertiary)'}; color:${this.groupByTicket ? '#ffffff' : 'var(--text-secondary)'}; border:1px solid var(--border-subtle);" title="Raggruppa le note adiacenti appartenenti allo stesso ticket">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:15px; height:15px;">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
<polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
<line x1="12" y1="22.08" x2="12" y2="12"/>
</svg>
<span>${this.groupByTicket ? 'Raggruppa per Ticket (Attivo)' : 'Raggruppa per Ticket'}</span>
</button>
<button id="ta-btn-toggle-nonwork" class="btn btn-ghost btn-sm" style="height:36px; display:flex; align-items:center; gap:6px;">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:15px; height:15px;">
${this.showNonWorkingDays
@@ -193,7 +234,7 @@ const TimeAccountingView = {
<div style="display:flex; gap:var(--space-md); padding:var(--space-md) var(--space-xl); align-items:flex-start; min-height:calc(100vh - 280px);">
<!-- Left Side: List of Articles -->
<div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:var(--space-sm);">
<div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:var(--space-md);">
${articles.length === 0 ? `
<div class="empty-state" style="padding:var(--space-2xl); text-align:center; color:var(--text-muted); background:var(--bg-card); border-radius:var(--radius-md); border:1px solid var(--border-subtle);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:48px; height:48px; margin:0 auto var(--space-md); display:block; opacity:0.4;">
@@ -201,7 +242,7 @@ const TimeAccountingView = {
</svg>
<p style="font-size:0.9rem; font-weight:500;">Nessuna nota o email trovata in questa data per l'agente selezionato.</p>
</div>
` : articles.map(a => this.buildArticleCard(a)).join('')}
` : this.groupArticles(articles).map(g => g.type === 'single' ? this.buildArticleCard(g.article) : this.buildGroupCard(g)).join('')}
</div>
<!-- Right Side: Details & Actions Panel -->
@@ -574,6 +615,40 @@ const TimeAccountingView = {
`;
},
buildGroupCard(group) {
const totalGroupMins = Math.round(group.articles.reduce((acc, a) => acc + (parseFloat(a.time_unit) || 0), 0));
const formattedGroupTime = this.formatPillTime(totalGroupMins);
const hasSelectedArticle = group.articles.some(a => String(a.article_id) === String(this.selectedArticleId));
return `
<div class="card ta-group-card" style="border:1px solid ${hasSelectedArticle ? 'var(--accent-primary)' : 'var(--border-subtle)'}; background:var(--bg-card); border-radius:var(--radius-md); overflow:hidden;">
<!-- Header del Gruppo Ticket -->
<div style="padding:10px 14px; background:var(--bg-secondary); border-bottom:1px solid var(--border-subtle); display:flex; align-items:center; justify-content:space-between; gap:10px;">
<div style="display:flex; align-items:center; gap:8px; min-width:0;">
<span style="font-size:0.9rem;">🎫</span>
<a href="#/tickets/${group.ticket_id}" class="ticket-tn-link" onclick="event.stopPropagation();" style="font-weight:700; font-size:0.9rem;">#${App.escapeHtml(group.ticket_tn || group.ticket_id)}</a>
<span style="font-size:0.85rem; font-weight:600; color:var(--text-primary); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;" title="${App.escapeHtml(group.ticket_title || '')}">
${App.escapeHtml(group.ticket_title || '(senza titolo)')}
</span>
</div>
<div style="display:flex; align-items:center; gap:8px; flex-shrink:0;">
<span class="badge" style="background:var(--bg-tertiary); color:var(--text-secondary); font-size:0.75rem; font-weight:600;">
${group.articles.length} ${group.articles.length === 1 ? 'nota' : 'note'}
</span>
<span class="badge" style="background:${totalGroupMins > 0 ? 'rgba(34,197,94,0.18)' : 'var(--bg-tertiary)'}; color:${totalGroupMins > 0 ? '#15803d' : 'var(--text-muted)'}; font-weight:700; font-size:0.78rem;">
⏱️ Totale: ${formattedGroupTime} (${totalGroupMins} m)
</span>
</div>
</div>
<!-- Note del Gruppo -->
<div style="display:flex; flex-direction:column; gap:6px; padding:10px;">
${group.articles.map(a => this.buildArticleCard(a)).join('')}
</div>
</div>
`;
},
buildDetailPanel(a) {
const timeMins = parseFloat(a.time_unit || 0);
@@ -687,6 +762,13 @@ const TimeAccountingView = {
this.loadData();
});
// Toggle group by ticket button
document.getElementById('ta-btn-toggle-groupby')?.addEventListener('click', () => {
this.groupByTicket = !this.groupByTicket;
localStorage.setItem('otrs_ta_group_by_ticket', this.groupByTicket);
this.loadData();
});
// Day pill clicks (also handles calendar cell clicks)
document.querySelectorAll('.ta-day-pill[data-date]').forEach(pill => {
pill.addEventListener('click', () => {