- "script": "const weekTitle = root_element.querySelector('[data-label=\"week-total\"]');\nconst monthTitle = root_element.querySelector('[data-label=\"month-total\"]');\nconst hoursBalanceTitle = root_element.querySelector('[data-label=\"hours-balance\"]');\nconst vacationTitle = root_element.querySelector('[data-label=\"vacation-remaining\"]');\n\nconst weekTotal = root_element.querySelector('[data-field=\"week-total\"]');\nconst weekStatus = root_element.querySelector('[data-field=\"week-status\"]');\nconst monthTotal = root_element.querySelector('[data-field=\"month-total\"]');\nconst monthStatus = root_element.querySelector('[data-field=\"month-status\"]');\nconst hoursBalance = root_element.querySelector('[data-field=\"hours-balance\"]');\nconst vacationCard = root_element.querySelector('[data-card=\"vacation\"]');\nconst vacationRemaining = root_element.querySelector('[data-field=\"vacation-remaining\"]');\nconst vacationStatus = root_element.querySelector('[data-field=\"vacation-status\"]');\n\nif (weekTitle) {\n weekTitle.textContent = __('Week Total');\n}\nif (monthTitle) {\n monthTitle.textContent = __('Month Total');\n}\nif (hoursBalanceTitle) {\n hoursBalanceTitle.textContent = __('Hours Balance');\n}\nif (vacationTitle) {\n vacationTitle.textContent = __('Vacation Remaining');\n}\n\nfunction formatMinutes(totalMinutes) {\n const minutes = Math.max(0, Math.round(totalMinutes || 0));\n const hours = Math.floor(minutes / 60);\n const remainder = minutes % 60;\n return `${hours}:${String(remainder).padStart(2, '0')}`;\n}\n\nfunction formatSignedMinutes(totalMinutes) {\n const value = Math.round(totalMinutes || 0);\n const sign = value < 0 ? '-' : '';\n return `${sign}${formatMinutes(Math.abs(value))}`;\n}\n\nfunction formatVacationDays(value) {\n const rounded = Math.round(Number(value || 0) * 100) / 100;\n return Number.isFinite(rounded) ? rounded.toString() : '0';\n}\n\nfunction updateWeeklyStatus(totalMinutes, targetHours, targetPeriod) {\n if (targetPeriod === 'Monthly') {\n weekStatus.textContent = '';\n weekStatus.style.display = 'none';\n return;\n }\n\n weekStatus.style.display = '';\n if (!targetHours) {\n weekStatus.textContent = __('Weekly target not set.');\n weekStatus.style.color = '#6c757d';\n return;\n }\n const targetMinutes = Math.round(Number(targetHours) * 60);\n const diff = totalMinutes - targetMinutes;\n if (diff >= 0) {\n weekStatus.textContent = `${__('Over by')}: ${formatMinutes(diff)}`;\n weekStatus.style.color = '#28a745';\n } else {\n weekStatus.textContent = `${__('Remaining')}: ${formatMinutes(Math.abs(diff))}`;\n weekStatus.style.color = '#dc3545';\n }\n}\n\nfunction updateMonthlyStatus(totalMinutes, targetHours, targetPeriod) {\n if (targetPeriod === 'Weekly') {\n monthStatus.textContent = '';\n monthStatus.style.display = 'none';\n return;\n }\n\n monthStatus.style.display = '';\n if (!targetHours) {\n monthStatus.textContent = __('Monthly target not set.');\n monthStatus.style.color = '#6c757d';\n return;\n }\n const targetMinutes = Math.round(Number(targetHours) * 60);\n const diff = totalMinutes - targetMinutes;\n if (diff >= 0) {\n monthStatus.textContent = `${__('Over by')}: ${formatMinutes(diff)}`;\n monthStatus.style.color = '#28a745';\n } else {\n monthStatus.textContent = `${__('Remaining')}: ${formatMinutes(Math.abs(diff))}`;\n monthStatus.style.color = '#dc3545';\n }\n}\n\nfunction updateVacationSummary(vacation) {\n if (!vacation || !vacation.enabled) {\n vacationCard.style.display = 'none';\n return;\n }\n\n vacationCard.style.display = '';\n if (!vacation.valid) {\n vacationRemaining.textContent = '--';\n vacationStatus.textContent = vacation.error || __('Vacation balance is unavailable.');\n vacationStatus.style.color = '#dc3545';\n return;\n }\n\n const remaining = Number(vacation.remaining_days || 0);\n const used = Number(vacation.used_days || 0);\n const allowance = Number(vacation.allowance_days || 0);\n\n vacationRemaining.textContent = formatVacationDays(remaining);\n vacationStatus.textContent = __('Used {0} of {1} days.', [\n formatVacationDays(used),\n formatVacationDays(allowance),\n ]);\n vacationStatus.style.color = remaining < 0 ? '#dc3545' : '#6c757d';\n}\n\nfunction showProfileMissing(message) {\n const text = message || __('Time Tracking Profile is required.');\n weekStatus.textContent = text;\n weekStatus.style.display = '';\n weekStatus.style.color = '#dc3545';\n monthStatus.textContent = '';\n monthStatus.style.display = 'none';\n vacationCard.style.display = 'none';\n}\n\nfunction updateSummary(data) {\n const weeklyMinutes = Number(data.weekly_total_minutes || 0);\n const monthlyMinutes = Number(data.monthly_total_minutes || 0);\n const hoursBalanceMinutes = Number(data.hours_balance_minutes || 0);\n const targetPeriod = data.target_period || null;\n\n weekTotal.textContent = formatMinutes(weeklyMinutes);\n monthTotal.textContent = formatMinutes(monthlyMinutes);\n hoursBalance.textContent = formatSignedMinutes(hoursBalanceMinutes);\n\n updateWeeklyStatus(weeklyMinutes, data.weekly_target_hours, targetPeriod);\n updateMonthlyStatus(monthlyMinutes, data.monthly_target_hours, targetPeriod);\n updateVacationSummary(data.vacation);\n}\n\nfrappe.call({\n method: 'time_tracking.time_tracking.workspace_summary.get_workspace_summary',\n callback: function (r) {\n const message = r.message || {};\n if (message.profile_missing) {\n showProfileMissing(message.message);\n return;\n }\n updateSummary(message);\n },\n});\n",
0 commit comments