feat: update language support and enhance API functionality

- Changed the default language in `index.html` from Russian to English, updating the title and button aria-labels for improved accessibility.
- Refactored the `buildFetchOptions` function in `api.js` to include an optional external abort signal, enhancing request management.
- Updated `fetchDuties` and `fetchCalendarEvents` to support request cancellation using the new abort signal, improving error handling.
- Added unit tests for the API functions to ensure proper functionality, including handling of 403 errors and request cancellations.
- Enhanced CSS styles for duty markers to improve visual consistency.
- Removed unused code and improved the overall structure of the JavaScript files for better maintainability.
This commit is contained in:
2026-03-02 12:40:49 +03:00
parent b906bfa777
commit a4d8d085c6
17 changed files with 822 additions and 181 deletions

View File

@@ -29,6 +29,9 @@ export function calendarEventsByDate(events) {
return byDate;
}
/** Max days to iterate per duty; prevents infinite loop on corrupted API data (end_at < start_at). */
const MAX_DAYS_PER_DUTY = 366;
/**
* Group duties by local date (start_at/end_at are UTC).
* @param {object[]} duties - Duties with start_at, end_at
@@ -39,14 +42,17 @@ export function dutiesByDate(duties) {
duties.forEach((d) => {
const start = new Date(d.start_at);
const end = new Date(d.end_at);
if (end < start) return;
const endLocal = localDateString(end);
let t = new Date(start);
while (true) {
const key = localDateString(t);
let cursor = new Date(start);
let iterations = 0;
while (iterations <= MAX_DAYS_PER_DUTY) {
const key = localDateString(cursor);
if (!byDate[key]) byDate[key] = [];
byDate[key].push(d);
if (key === endLocal) break;
t.setDate(t.getDate() + 1);
cursor.setDate(cursor.getDate() + 1);
iterations++;
}
});
return byDate;
@@ -104,14 +110,8 @@ export function renderCalendar(
start_at: x.start_at,
end_at: x.end_at
}));
cell.setAttribute(
"data-day-duties",
JSON.stringify(dayPayload).replace(/"/g, "&quot;")
);
cell.setAttribute(
"data-day-events",
JSON.stringify(eventSummaries).replace(/"/g, "&quot;")
);
cell.setAttribute("data-day-duties", JSON.stringify(dayPayload));
cell.setAttribute("data-day-events", JSON.stringify(eventSummaries));
}
const ariaParts = [];