/**
* TimeAccountingView — Storico consuntivazione
* Shows a summary of notes/emails entered by the selected agent on the selected day,
* with options on the right side to edit consumed time or backdate the article.
* Includes interactive day pills strip, configurable WORK_DAYS, and show/hide non-working days toggle.
*/
const TimeAccountingView = {
selectedAgentId: null,
selectedDate: null,
selectedArticleId: null,
summaryData: null,
showNonWorkingDays: localStorage.getItem('otrs_ta_show_non_working_days') !== 'false',
formatLocalISO(d) {
if (!d) return '';
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${y}-${m}-${day}`;
},
async render() {
const container = document.getElementById('view-container');
container.innerHTML = '
${articles.length === 0 ? `
Nessuna nota o email trovata in questa data per l'agente selezionato.
` : articles.map(a => this.buildArticleCard(a)).join('')}
${selectedArticle ? this.buildDetailPanel(selectedArticle) : `
Seleziona una nota o email a sinistra per gestirne il tempo o retrodatare.
`}
`;
},
buildDayStrip(data) {
const selectedParts = this.selectedDate.split('-').map(Number);
const selected = new Date(selectedParts[0], selectedParts[1] - 1, selectedParts[2]);
const todayStr = this.formatLocalISO(new Date());
const weekdays = ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'];
const months = ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'];
const dailyTotals = (data && data.daily_totals) || {};
const targetMins = (data && data.target_minutes) || 480;
const workDays = (data && data.work_days) || [1, 2, 3, 4, 5];
// Generate 2 full weeks of days (15 calendar days if showing all, or 10-11 working days if hiding non-working days)
const dateList = [];
if (this.showNonWorkingDays) {
for (let offset = -7; offset <= 7; offset++) {
const d = new Date(selected);
d.setDate(d.getDate() + offset);
dateList.push(d);
}
} else {
const before = [];
let dPrev = new Date(selected);
while (before.length < 5) {
dPrev.setDate(dPrev.getDate() - 1);
if (workDays.includes(dPrev.getDay())) {
before.unshift(new Date(dPrev));
}
}
const current = new Date(selected);
const after = [];
let dNext = new Date(selected);
while (after.length < 5) {
dNext.setDate(dNext.getDate() + 1);
if (workDays.includes(dNext.getDay())) {
after.push(new Date(dNext));
}
}
dateList.push(...before, current, ...after);
}
const days = [];
for (const d of dateList) {
const isoStr = this.formatLocalISO(d);
const dayOfWeek = d.getDay(); // 0 = Sun, 1 = Mon ... 6 = Sat
const isWorkDay = workDays.includes(dayOfWeek);
const isSelected = isoStr === this.selectedDate;
const isToday = isoStr === todayStr;
const dayNum = d.getDate();
const weekdayName = weekdays[dayOfWeek];
const monthName = months[d.getMonth()];
const dayMins = Math.round(dailyTotals[isoStr] || 0);
const formattedTime = this.formatPillTime(dayMins);
const isTargetReached = dayMins >= targetMins;
// Color coding computation
let bgStyle = 'var(--bg-card)';
let borderStyle = '1px solid var(--border-subtle)';
let textColor = 'var(--text-primary)';
let badgeBg = 'var(--bg-tertiary)';
let badgeColor = 'var(--text-muted)';
if (!isWorkDay) {
// Non-working days (e.g., weekends)
if (isSelected) {
bgStyle = 'var(--accent-primary)';
borderStyle = '1px solid var(--accent-primary)';
textColor = '#ffffff';
badgeBg = 'rgba(255,255,255,0.25)';
badgeColor = '#ffffff';
} else if (isToday) {
bgStyle = 'rgba(79, 70, 229, 0.12)';
borderStyle = '1px solid var(--border-subtle)';
textColor = 'var(--accent-primary)';
}
} else {
// Working days (configured in WORK_DAYS)
if (isTargetReached) {
// Verdino (green)
if (isSelected) {
bgStyle = '#15803d';
borderStyle = '1px solid #166534';
textColor = '#ffffff';
badgeBg = 'rgba(255,255,255,0.28)';
badgeColor = '#ffffff';
} else {
bgStyle = 'rgba(34, 197, 94, 0.16)';
borderStyle = '1px solid rgba(34, 197, 94, 0.5)';
textColor = '#15803d';
badgeBg = 'rgba(34, 197, 94, 0.25)';
badgeColor = '#15803d';
}
} else {
// Rosso (red)
if (isSelected) {
bgStyle = '#b91c1c';
borderStyle = '1px solid #991b1b';
textColor = '#ffffff';
badgeBg = 'rgba(255,255,255,0.28)';
badgeColor = '#ffffff';
} else {
bgStyle = 'rgba(239, 68, 68, 0.14)';
borderStyle = '1px solid rgba(239, 68, 68, 0.4)';
textColor = '#b91c1c';
badgeBg = 'rgba(239, 68, 68, 0.22)';
badgeColor = '#b91c1c';
}
}
}
days.push(`
${App.escapeHtml(a.a_subject || '(senza oggetto)')}
${bodyPreview || 'Nessun testo '}
${App.escapeHtml(a.channel || a.sender_type || 'Nota')}
⏱️ ${timeMins} min
`;
},
buildDetailPanel(a) {
const timeMins = parseFloat(a.time_unit || 0);
const localISO = (dateStr) => {
if (!dateStr) return '';
const d = new Date(dateStr);
return new Date(d.getTime() - d.getTimezoneOffset() * 60000).toISOString().slice(0, 16);
};
return `