feat: aggiunta consuntivazione automatica tramite variabili env. style: aggiunto bordino anche attorno al tempo nel caso di overperformance
This commit is contained in:
@@ -2421,4 +2421,75 @@ body {
|
||||
margin: var(--space-sm);
|
||||
padding: 12px !important;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
@keyframes autotime-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 5px rgba(16, 112, 202, 0.4);
|
||||
border-color: rgba(16, 112, 202, 0.5);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 15px rgba(16, 112, 202, 0.8), inset 0 0 5px rgba(16, 112, 202, 0.3);
|
||||
border-color: rgba(16, 112, 202, 0.9);
|
||||
background: rgba(16, 112, 202, 0.15);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 5px rgba(16, 112, 202, 0.4);
|
||||
border-color: rgba(16, 112, 202, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-brand.clickable-auto-time {
|
||||
cursor: pointer;
|
||||
animation: autotime-pulse 2.5s infinite ease-in-out;
|
||||
border: 1px dashed var(--accent-primary) !important;
|
||||
border-radius: var(--radius-md);
|
||||
margin: var(--space-xs) var(--space-sm);
|
||||
padding: 8px 12px !important;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-brand.clickable-auto-time:hover {
|
||||
filter: brightness(1.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.sidebar-timer.clickable-auto-time {
|
||||
cursor: pointer;
|
||||
animation: autotime-pulse 2.5s infinite ease-in-out;
|
||||
border: 1px dashed var(--accent-primary) !important;
|
||||
border-radius: var(--radius-md);
|
||||
margin: var(--space-xs) var(--space-sm);
|
||||
padding: 8px 12px !important;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-timer.clickable-auto-time:hover {
|
||||
filter: brightness(1.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@keyframes overperformance-alarm-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 5px rgba(239, 68, 68, 0.4);
|
||||
border-color: rgba(239, 68, 68, 0.6);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 15px rgba(239, 68, 68, 0.8), inset 0 0 5px rgba(239, 68, 68, 0.3);
|
||||
border-color: rgba(239, 68, 68, 0.9);
|
||||
background: rgba(239, 68, 68, 0.15) !important;
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 5px rgba(239, 68, 68, 0.4);
|
||||
border-color: rgba(239, 68, 68, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-timer.overperformance-alarm {
|
||||
animation: overperformance-alarm-pulse 1.5s infinite ease-in-out;
|
||||
border: 1px solid rgba(239, 68, 68, 0.6) !important;
|
||||
border-radius: var(--radius-md);
|
||||
margin: var(--space-xs) var(--space-sm);
|
||||
padding: 8px 12px !important;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
+69
-1
@@ -186,10 +186,13 @@ const App = {
|
||||
if (cached) {
|
||||
try {
|
||||
this.lookups = JSON.parse(cached);
|
||||
if (!this.lookups.config || this.lookups.config.autoTimeMinHour === undefined) {
|
||||
throw new Error('Outdated config cache (missing autoTimeMinHour)');
|
||||
}
|
||||
this.lookupsLoaded = true;
|
||||
return;
|
||||
} catch (e) {
|
||||
console.warn('Failed to parse cached lookups, reloading...', e);
|
||||
console.warn('Failed to parse cached lookups, reloading...', e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,6 +439,71 @@ const App = {
|
||||
}
|
||||
}
|
||||
|
||||
if (timerEl) {
|
||||
if (hasOverperformance) {
|
||||
timerEl.classList.add('overperformance-alarm');
|
||||
} else {
|
||||
timerEl.classList.remove('overperformance-alarm');
|
||||
}
|
||||
}
|
||||
|
||||
const minTimeStr = this.lookups.config?.autoTimeMinHour || '18:00';
|
||||
const [minHour, minMin] = minTimeStr.split(':').map(x => parseInt(x, 10));
|
||||
|
||||
const now = new Date();
|
||||
const currentHour = now.getHours();
|
||||
const currentMin = now.getMinutes();
|
||||
|
||||
let isPastTime = false;
|
||||
if (currentHour > minHour) {
|
||||
isPastTime = true;
|
||||
} else if (currentHour === minHour && currentMin >= minMin) {
|
||||
isPastTime = true;
|
||||
}
|
||||
|
||||
const triggerAction = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const confirmed = confirm(`Sei sicuro di voler effettuare la consuntivazione automatica di ${remaining} minuti rimanenti di oggi? Verrà creato un ticket chiuso a tuo carico.`);
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
Toast.success('Consuntivazione in corso...');
|
||||
const res = await this.api('/api/tickets/auto-time', { method: 'POST' });
|
||||
Toast.success(res.message || 'Consuntivazione completata!');
|
||||
this.updateDailyTimer();
|
||||
this.route();
|
||||
} catch (err) {
|
||||
Toast.error('Errore consuntivazione automatica: ' + err.message);
|
||||
}
|
||||
};
|
||||
|
||||
if (isPastTime && remaining > 0) {
|
||||
if (brandEl) {
|
||||
brandEl.classList.add('clickable-auto-time');
|
||||
brandEl.setAttribute('title', `Consuntivazione Automatica fine giornata (${remaining} m)`);
|
||||
brandEl.onclick = triggerAction;
|
||||
}
|
||||
|
||||
if (timerEl) {
|
||||
timerEl.classList.add('clickable-auto-time');
|
||||
timerEl.setAttribute('title', `Consuntivazione Automatica fine giornata (${remaining} m). Clicca per eseguire.`);
|
||||
timerEl.onclick = triggerAction;
|
||||
}
|
||||
} else {
|
||||
if (brandEl) {
|
||||
brandEl.classList.remove('clickable-auto-time');
|
||||
brandEl.removeAttribute('title');
|
||||
brandEl.onclick = null;
|
||||
}
|
||||
|
||||
if (timerEl) {
|
||||
timerEl.classList.remove('clickable-auto-time');
|
||||
timerEl.removeAttribute('title');
|
||||
timerEl.onclick = null;
|
||||
}
|
||||
}
|
||||
|
||||
let phrase = "";
|
||||
const phraseThreshold = this.lookups.config?.phraseThreshold || 70;
|
||||
const isDemotivational = percentage >= phraseThreshold;
|
||||
|
||||
Reference in New Issue
Block a user