feat: enhance UI components and error handling

- Updated HTML structure for navigation buttons in the calendar, adding SVG icons for improved visual clarity.
- Introduced a new muted text style in CSS for better presentation of empty duty list messages.
- Enhanced calendar CSS for navigation buttons and day indicators, improving layout and responsiveness.
- Improved error handling in the UI by adding retry functionality to the error display, allowing users to retry actions directly from the error message.
- Updated internationalization messages to include a retry option for error handling.
- Added unit tests to verify the new error handling behavior and UI updates.
This commit is contained in:
2026-03-02 20:21:33 +03:00
parent 37d4226beb
commit 54446d7b0f
12 changed files with 154 additions and 19 deletions

View File

@@ -15,6 +15,7 @@ import {
getNextBtn
} from "./dom.js";
import { t } from "./i18n.js";
import { escapeHtml } from "./utils.js";
/**
* Show access-denied view and hide calendar/list/loading/error.
@@ -63,16 +64,37 @@ export function hideAccessDenied() {
if (dutyListEl) dutyListEl.hidden = false;
}
/** Warning icon SVG for error state (24×24). */
const ERROR_ICON_SVG =
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="error-icon" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>';
/**
* Show error message and hide loading.
* @param {string} msg - Error text
* @param {(() => void)|null} [onRetry] - Optional callback for Retry button
*/
export function showError(msg) {
export function showError(msg, onRetry) {
const errorEl = getErrorEl();
const loadingEl = getLoadingEl();
if (errorEl) {
errorEl.textContent = msg;
const retryLabel = t(state.lang, "error.retry");
const safeMsg = typeof msg === "string" ? msg : t(state.lang, "error_generic");
let html =
ERROR_ICON_SVG +
'<p class="error-text">' +
escapeHtml(safeMsg) +
"</p>";
if (typeof onRetry === "function") {
html += '<button type="button" class="error-retry">' + escapeHtml(retryLabel) + "</button>";
}
errorEl.innerHTML = html;
errorEl.hidden = false;
const retryBtn = errorEl.querySelector(".error-retry");
if (retryBtn && typeof onRetry === "function") {
retryBtn.addEventListener("click", () => {
onRetry();
});
}
}
if (loadingEl) loadingEl.classList.add("hidden");
}