diff --git a/js/time-balance.js b/js/time-balance.js index 7c30164b6..33cc60c2f 100644 --- a/js/time-balance.js +++ b/js/time-balance.js @@ -64,12 +64,26 @@ function _getOverallBalanceStartDate() return savedPreferences['overall-balance-start-date']; } -function _getHoursPerDay() +// function _getHoursPerDay() +// { +// const savedPreferences = getUserPreferences(); +// return savedPreferences['hours-per-day']; +// } + +function _getHoursForDay(dayIndex) { const savedPreferences = getUserPreferences(); - return savedPreferences['hours-per-day']; + const days = [ + 'sunday', + 'monday', + 'tuesday', + 'wednesday', + 'thursday', + 'friday', + 'saturday' + ]; + return savedPreferences[`hours-${days[dayIndex]}`] || '08:00'; } - /** * Given an array of times from a day in the flexible calendar, returns the * day total according to same calculation rules as those of the calendar. @@ -170,7 +184,7 @@ async function computeAllTimeBalanceUntil(limitDate) const totals = _getDayTotalsFromStores(firstDate, limitDate); const preferences = getUserPreferences(); - const hoursPerDay = _getHoursPerDay(); + // const hoursPerDay = _getHoursPerDay(); let allTimeTotal = '00:00'; const date = new Date(firstDate); const limitDateStr = getDateStr(limitDate); @@ -179,8 +193,10 @@ async function computeAllTimeBalanceUntil(limitDate) { if (showDay(date.getFullYear(), date.getMonth(), date.getDate(), preferences)) { + const dayIndex = date.getDay(); + const dayHours = _getHoursForDay(dayIndex); const dayTotal = dateStr in totals ? totals[dateStr] : '00:00'; - const dayBalance = subtractTime(hoursPerDay, dayTotal); + const dayBalance = subtractTime(dayHours, dayTotal); allTimeTotal = sumTime(dayBalance, allTimeTotal); } date.setDate(date.getDate() + 1); diff --git a/js/user-preferences.js b/js/user-preferences.js index 6d6b4464c..4ee7cec52 100644 --- a/js/user-preferences.js +++ b/js/user-preferences.js @@ -32,7 +32,14 @@ const defaultPreferences = { 'close-to-tray': true, 'minimize-to-tray': true, 'hide-non-working-days': false, - 'hours-per-day': '08:00', + 'hours-sunday': '08:00', + 'hours-monday': '08:00', + 'hours-tuesday': '08:00', + 'hours-wednesday': '08:00', + 'hours-thursday': '08:00', + 'hours-friday': '08:00', + 'hours-saturday': '08:00', + 'hours-per-day': '08:00', //probably need to remove 'enable-prefill-break-time': false, 'break-time-interval': '00:30', 'notification': true, @@ -76,6 +83,13 @@ const timeInputs = [ 'notifications-interval', 'hours-per-day', 'break-time-interval', + 'hours-sunday', + 'hours-monday', + 'hours-tuesday', + 'hours-wednesday', + 'hours-thursday', + 'hours-friday', + 'hours-saturday', ]; const isNotBoolean = (val) => typeof val !== 'boolean'; @@ -111,7 +125,9 @@ function savePreferences(preferencesOptions, filePath = getPreferencesFilePath() { try { - getFs().writeFileSync(filePath, JSON.stringify(preferencesOptions)); + const preferencesToSave = { ...defaultPreferences, ...preferencesOptions }; + getFs().writeFileSync(filePath, JSON.stringify(preferencesToSave)); + } catch (err) { @@ -190,6 +206,13 @@ function initPreferencesFileIfNotExistsOrInvalid(filePath = getPreferencesFilePa 'notifications-interval' : () => isNotificationInterval(value), 'hours-per-day' : () => validateTime(value), 'break-time-interval' : () => validateTime(value), + 'hours-sunday': () => validateTime(value), + 'hours-monday': () => validateTime(value), + 'hours-tuesday': () => validateTime(value), + 'hours-wednesday': () => validateTime(value), + 'hours-thursday': () => validateTime(value), + 'hours-friday': () => validateTime(value), + 'hours-saturday': () => validateTime(value), }; if (!timeValidationEnum[key]()) { diff --git a/renderer/classes/BaseCalendar.js b/renderer/classes/BaseCalendar.js index 1c1ed0d67..df472ab11 100644 --- a/renderer/classes/BaseCalendar.js +++ b/renderer/classes/BaseCalendar.js @@ -336,6 +336,20 @@ class BaseCalendar return this._preferences['hours-per-day']; } + _getHoursForDay(dayIndex) + { + const days = [ + 'sunday', + 'monday', + 'tuesday', + 'wednesday', + 'thursday', + 'friday', + 'saturday' + ]; + return this._preferences[`hours-${days[dayIndex]}`] || '08:00'; + } + /** * Returns if "hide non-working days" was set in preferences. * @return {Boolean} @@ -659,8 +673,10 @@ class BaseCalendar } if (timesAreProgressing) { - const lastTime = validatedTimes[validatedTimes.length-1]; - const remainingTime = subtractTime(dayTotal, this._getHoursPerDay()); + const lastTime = validatedTimes[validatedTimes.length - 1]; + const dayIndex = new Date(this._getTodayYear(), this._getTodayMonth(), this._getTodayDate()).getDay(); + const dayHours = this._getHoursForDay(dayIndex); + const remainingTime = subtractTime(dayTotal, dayHours); leaveBy = sumTime(lastTime, remainingTime); } } diff --git a/renderer/classes/FlexibleDayCalendar.js b/renderer/classes/FlexibleDayCalendar.js index 6baa64e2c..1f85d905b 100644 --- a/renderer/classes/FlexibleDayCalendar.js +++ b/renderer/classes/FlexibleDayCalendar.js @@ -2,7 +2,6 @@ import { isNegative, - multiplyTime, subtractTime, sumTime, validateTime @@ -49,6 +48,26 @@ class FlexibleDayCalendar extends BaseCalendar $('#calendar').html(body); $('html').attr('data-view', 'flexible-day'); } + /** + * Returns the working hours for the specified day of the week. + */ + _getHoursForDay(dayIndex) + { + const dayKeys = [ + 'hours-sunday', + 'hours-monday', + 'hours-tuesday', + 'hours-wednesday', + 'hours-thursday', + 'hours-friday', + 'hours-saturday' + ]; + if (dayIndex < 0 || dayIndex > 6) + { + throw new Error('Invalid dayIndex provided to _getHoursForDay.'); + } + return this._preferences[dayKeys[dayIndex]] || '08:00'; + } /** * Returns the header of the page, with the image, name and a message. @@ -385,9 +404,8 @@ class FlexibleDayCalendar extends BaseCalendar { const yesterday = new Date(this._calendarDate); yesterday.setDate(this._calendarDate.getDate() - 1); - let workingDaysToCompute = 0, - monthTotalWorked = '00:00'; - let countDays = false; + + let monthTotalWorked = '00:00'; const limit = this._getCountToday() ? this._getCalendarDate() : (yesterday.getMonth() !== this._getCalendarMonth() ? 0 : yesterday.getDate()); for (let day = 1; day <= limit; ++day) @@ -396,20 +414,19 @@ class FlexibleDayCalendar extends BaseCalendar { continue; } - const dayTotal = this._getDayTotal(this._getCalendarYear(), this._getCalendarMonth(), day); if (dayTotal !== undefined) { - countDays = true; - monthTotalWorked = sumTime(monthTotalWorked, dayTotal); - } - if (countDays) - { - workingDaysToCompute += 1; + + const dayIndex = new Date(this._getCalendarYear(), this._getCalendarMonth(), day).getDay(); // Get current day + const hoursForDay = this._getHoursForDay(dayIndex); + const dayBalance = subtractTime(hoursForDay, dayTotal); + monthTotalWorked = sumTime(monthTotalWorked, dayBalance); } + } - const monthTotalToWork = multiplyTime(this._getHoursPerDay(), workingDaysToCompute * -1); - const balance = sumTime(monthTotalToWork, monthTotalWorked); + + const balance = monthTotalWorked; const balanceElement = $('#month-balance'); if (balanceElement) { @@ -417,6 +434,7 @@ class FlexibleDayCalendar extends BaseCalendar balanceElement.removeClass('text-success text-danger'); balanceElement.addClass(isNegative(balance) ? 'text-danger' : 'text-success'); } + } /** @@ -438,7 +456,9 @@ class FlexibleDayCalendar extends BaseCalendar const dayTotal = $('.day-total span').html(); if (dayTotal !== undefined && dayTotal.length > 0) { - const dayBalance = subtractTime(this._getHoursPerDay(), dayTotal); + const dayIndex = new Date(this._getTodayYear(), this._getTodayMonth(), this._getTodayDate()).getDay(); + const dayHours = this._getHoursForDay(dayIndex); + const dayBalance = subtractTime(dayHours, dayTotal); $('#leave-day-balance').val(dayBalance); $('#leave-day-balance').removeClass('text-success text-danger'); $('#leave-day-balance').addClass(isNegative(dayBalance) ? 'text-danger' : 'text-success'); diff --git a/renderer/classes/FlexibleMonthCalendar.js b/renderer/classes/FlexibleMonthCalendar.js index 4bb413de1..92e377da8 100644 --- a/renderer/classes/FlexibleMonthCalendar.js +++ b/renderer/classes/FlexibleMonthCalendar.js @@ -60,7 +60,20 @@ class FlexibleMonthCalendar extends BaseCalendar } return targetDate; } - + /* Returns the working hours for the specified day of the week. */ + _getHoursForDay(dayIndex) + { + const dayKeys = [ + 'hours-sunday', + 'hours-monday', + 'hours-tuesday', + 'hours-wednesday', + 'hours-thursday', + 'hours-friday', + 'hours-saturday' + ]; + return this._preferences[dayKeys[dayIndex]] || '08:00'; // Default to '08:00' if no preference is set + } /* * Generates the calendar HTML view. */ @@ -503,15 +516,17 @@ class FlexibleMonthCalendar extends BaseCalendar { const now = new Date(); const monthLength = getMonthLength(this._getCalendarYear(), this._getCalendarMonth()); - let workingDaysToCompute = 0; + const workingDaysToCompute = []; let monthTotalWorked = '00:00'; - let countDays = false; let isNextDay = false; for (let day = 1; day <= monthLength; ++day) { - const isToday = (now.getDate() === day && now.getMonth() === this._getCalendarMonth() && now.getFullYear() === this._getCalendarYear()); - // balance should consider preferences and count or not today + const isToday = now.getDate() === day && + now.getMonth() === this._getCalendarMonth() && + now.getFullYear() === this._getCalendarYear(); + + // balance should consider preferences and count or notyyyyyyyyyyyyyyyyyyyyyyyy today if (isToday && !this._getCountToday() || isNextDay && this._getCountToday()) { break; @@ -524,18 +539,23 @@ class FlexibleMonthCalendar extends BaseCalendar } const dayTotal = this._getDayTotal(this._getCalendarYear(), this._getCalendarMonth(), day); - if (dayTotal !== undefined && dayTotal.length !== 0) + if (dayTotal) { - countDays = true; monthTotalWorked = sumTime(monthTotalWorked, dayTotal); - } - if (countDays) - { - workingDaysToCompute += 1; + + const dayIndex = new Date(this._getCalendarYear(), this._getCalendarMonth(), day).getDay(); + workingDaysToCompute.push(dayIndex); } } - const monthTotalToWork = multiplyTime(this._getHoursPerDay(), workingDaysToCompute * -1); + + const monthTotalToWork = workingDaysToCompute.reduce((total, dayIndex) => + { + return sumTime(total, multiplyTime(this._getHoursForDay(dayIndex), -1)); + }, '00:00'); + const balance = sumTime(monthTotalToWork, monthTotalWorked); + + const balanceElement = $('#month-balance'); if (balanceElement) { @@ -546,6 +566,8 @@ class FlexibleMonthCalendar extends BaseCalendar this._updateAllTimeBalance(); } + + /* * Updates data displayed based on the database. */ @@ -630,7 +652,8 @@ class FlexibleMonthCalendar extends BaseCalendar const dayTotal = $('#' + dateKey).parent().find(' .day-total span').html(); if (dayTotal !== undefined && dayTotal.length > 0) { - const dayBalance = subtractTime(this._getHoursPerDay(), dayTotal); + const dayIndex = new Date(this._getTodayYear(), this._getTodayMonth(), this._getTodayDate()).getDay(); + const dayBalance = subtractTime(this._getHoursForDay(dayIndex), dayTotal); $('#leave-day-balance').html(dayBalance); $('#leave-day-balance').removeClass('text-success text-danger'); $('#leave-day-balance').addClass(isNegative(dayBalance) ? 'text-danger' : 'text-success'); diff --git a/src/preferences.html b/src/preferences.html index d5f663f09..98ed0840c 100644 --- a/src/preferences.html +++ b/src/preferences.html @@ -36,14 +36,16 @@ -
+ +
+
Hours per day
+
+
+
+

Hide non-working days (Month View)

-
-

Hours per day

- -

Enable prefilling of break time

diff --git a/src/preferences.js b/src/preferences.js index 7aeb1a276..33a344573 100644 --- a/src/preferences.js +++ b/src/preferences.js @@ -65,7 +65,10 @@ function refreshContent() function changeValue(type, newVal) { + preferences[type] = newVal; + + window.mainApi.notifyNewPreferences(preferences); } @@ -182,6 +185,81 @@ function renderPreferencesWindow() { notificationsInterval.prop('disabled', !repetition.is(':checked')); }); + + const days = [ + { id: 'sunday', label: 'sun' }, + { id: 'monday', label: 'mon' }, + { id: 'tuesday', label: 'tue' }, + { id: 'wednesday', label: 'wed' }, + { id: 'thursday', label: 'thu' }, + { id: 'friday', label: 'fri' }, + { id: 'saturday', label: 'sat' }, + ]; + + const hoursContainer = $('#hours-container'); + + function updateHoursInputs() + { + hoursContainer.empty(); + + days.forEach(day => + { + const isChecked = $(`#${day.id}`).is(':checked'); + if (isChecked) + { + const inputId = `hours-${day.id}`; + const storedValue = usersStyles[inputId] || '08:00'; // Default or saved value + + const inputHTML = ` +
+

${day.label}

+ +
+ `; + hoursContainer.append(inputHTML); + + $(`#${inputId}`).on('change', function() + { + if (this.checkValidity()) + { + changeValue(inputId, this.value); + } + }); + } + }); + window.mainApi.getLanguageDataPromise().then(languageData => + { + translatePage(usersStyles.language, languageData.data, 'Preferences'); + }); + } + + days.forEach(day => + { + const checkbox = $(`#${day.id}`); + if (checkbox) + { + checkbox.prop('checked', usersStyles[`working-days-${day.id}`] || false); + + checkbox.on('change', function() + { + changeValue(`working-days-${day.id}`, this.checked); + updateHoursInputs(); + }); + } + }); + + updateHoursInputs(); + } /* istanbul ignore next */ $(() =>