refactor: centralize shared date/name helpers in utils.js (#12)

New js/utils.js exports toISO, monthPrefix, getWeekday, formatDateDE and
buildNameMap. algorithm.js, calendar.js, excel.js and history.js now import
these instead of re-implementing ISO construction, weekday math, DD.MM.YYYY
formatting and the id→name map inline. Adds tests/utils.test.mjs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ferdi2goandClaude Opus 4.8 committed 2026-06-03 13:01:28 +00:00
1 parent 0efb9cb853
commit fd9870871b
7 files changed
+71 -26

No files matched your search

+8 -7
View File
@@ -2,6 +2,7 @@ import { state, saveState } from './app.js';
import { getWorkdays, generatePlan } from './algorithm.js';
import { toggleHoliday, toggleClosure, addHistoryEntries, removeHistoryEntry } from './data.js';
import { exportSchedule, downloadBlob } from './excel.js';
import { toISO, monthPrefix, getWeekday, buildNameMap } from './utils.js';
const MONTHS = ['Januar','Februar','März','April','Mai','Juni',
'Juli','August','September','Oktober','November','Dezember'];
@@ -122,7 +123,7 @@ export function renderCalendar() {
}
const workdays = getWorkdays(currentYear, currentMonth,
state.calendar.holidays, state.calendar.companyClosures);
const prefix = currentYear + '-' + String(currentMonth).padStart(2, '0');
const prefix = monthPrefix(currentYear, currentMonth);
// Preserve manual picks for this month; only auto-entries get regenerated.
const manualEntries = state.history.filter(h => h.date.startsWith(prefix) && h.manual);
const historyWithoutMonth = state.history.filter(h => !h.date.startsWith(prefix));
@@ -148,7 +149,7 @@ export function renderCalendar() {
}
function loadPlanFromHistory() {
const prefix = currentYear + '-' + String(currentMonth).padStart(2,'0');
const prefix = monthPrefix(currentYear, currentMonth);
currentPlan = state.history
.filter(h => h.date.startsWith(prefix))
.map(h => ({ ...h }));
@@ -158,7 +159,7 @@ function renderGrid(wrapper, exportBtn) {
wrapper.replaceChildren();
const daysInMonth = new Date(currentYear, currentMonth, 0).getDate();
const empNameById = Object.fromEntries(state.employees.map(e => [e.id, e.name]));
const empNameById = buildNameMap(state.employees);
const updateExportVisibility = () => {
exportBtn.style.display = currentPlan.length > 0 ? 'inline-block' : 'none';
};
@@ -166,8 +167,8 @@ function renderGrid(wrapper, exportBtn) {
// Build one day cell. Self-contained so a change to a single day re-renders
// just that cell (cell.replaceWith) instead of rebuilding the whole grid.
function makeDayCell(d) {
const iso = currentYear + '-' + String(currentMonth).padStart(2,'0') + '-' + String(d).padStart(2,'0');
const wdNum = (() => { const x = new Date(iso + 'T12:00:00Z').getUTCDay(); return x === 0 ? 7 : x; })();
const iso = toISO(currentYear, currentMonth, d);
const wdNum = getWeekday(iso);
const isWeekend = wdNum >= 6;
const isHoliday = state.calendar.holidays.includes(iso);
const isClosure = state.calendar.companyClosures.includes(iso);
@@ -291,8 +292,8 @@ function renderGrid(wrapper, exportBtn) {
bodyGrid.className = 'calendar-body-grid';
// Offset empty cells before first day
const firstISO = currentYear + '-' + String(currentMonth).padStart(2,'0') + '-01';
const firstWD = (() => { const d = new Date(firstISO + 'T12:00:00Z').getUTCDay(); return d === 0 ? 7 : d; })();
const firstISO = toISO(currentYear, currentMonth, 1);
const firstWD = getWeekday(firstISO);
for (let i = 1; i < firstWD; i++) {
const empty = document.createElement('div');
empty.className = 'cal-day empty-cell';