feat: enhance CI workflow and update webapp styles
Some checks failed
CI / lint-and-test (push) Failing after 45s
Some checks failed
CI / lint-and-test (push) Failing after 45s
- Added Node.js setup and webapp testing steps to the CI workflow for improved integration. - Updated HTML to link multiple CSS files for better modularity and organization of styles. - Removed deprecated `style.css` and introduced new CSS files for base styles, calendar, day detail, hints, markers, states, and duty list to enhance maintainability and readability. - Implemented new styles for improved presentation of duty information and user interactions. - Added unit tests for new API functions and contact link rendering to ensure functionality and reliability.
This commit is contained in:
88
webapp/js/contactHtml.js
Normal file
88
webapp/js/contactHtml.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Shared HTML builder for contact links (phone, Telegram) used by day detail,
|
||||
* current duty, and duty list.
|
||||
*/
|
||||
|
||||
import { t } from "./i18n.js";
|
||||
import { escapeHtml } from "./utils.js";
|
||||
|
||||
/**
|
||||
* Build HTML for contact links (phone, Telegram username).
|
||||
* Validates phone/username, builds tel: and t.me hrefs, wraps in spans/links.
|
||||
*
|
||||
* @param {'ru'|'en'} lang - UI language for labels (when showLabels is true)
|
||||
* @param {string|null|undefined} phone - Phone number
|
||||
* @param {string|null|undefined} username - Telegram username with or without leading @
|
||||
* @param {object} options - Rendering options
|
||||
* @param {string} options.classPrefix - CSS class prefix (e.g. "day-detail-contact", "duty-contact")
|
||||
* @param {boolean} [options.showLabels=true] - Whether to show "Phone:" / "Telegram:" labels
|
||||
* @param {string} [options.separator=' '] - Separator between contact parts (e.g. " ", " · ")
|
||||
* @returns {string} HTML string or "" if no valid contact
|
||||
*/
|
||||
export function buildContactLinksHtml(lang, phone, username, options) {
|
||||
const { classPrefix, showLabels = true, separator = " " } = options || {};
|
||||
const parts = [];
|
||||
|
||||
if (phone && String(phone).trim()) {
|
||||
const p = String(phone).trim();
|
||||
const safeHref =
|
||||
"tel:" +
|
||||
p.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
||||
const linkHtml =
|
||||
'<a href="' +
|
||||
safeHref +
|
||||
'" class="' +
|
||||
escapeHtml(classPrefix + "-link " + classPrefix + "-phone") +
|
||||
'">' +
|
||||
escapeHtml(p) +
|
||||
"</a>";
|
||||
if (showLabels) {
|
||||
const label = t(lang, "contact.phone");
|
||||
parts.push(
|
||||
'<span class="' +
|
||||
escapeHtml(classPrefix) +
|
||||
'">' +
|
||||
escapeHtml(label) +
|
||||
": " +
|
||||
linkHtml +
|
||||
"</span>"
|
||||
);
|
||||
} else {
|
||||
parts.push(linkHtml);
|
||||
}
|
||||
}
|
||||
|
||||
if (username && String(username).trim()) {
|
||||
const u = String(username).trim().replace(/^@+/, "");
|
||||
if (u) {
|
||||
const display = "@" + u;
|
||||
const href = "https://t.me/" + encodeURIComponent(u);
|
||||
const linkHtml =
|
||||
'<a href="' +
|
||||
escapeHtml(href) +
|
||||
'" class="' +
|
||||
escapeHtml(classPrefix + "-link " + classPrefix + "-username") +
|
||||
'" target="_blank" rel="noopener noreferrer">' +
|
||||
escapeHtml(display) +
|
||||
"</a>";
|
||||
if (showLabels) {
|
||||
const label = t(lang, "contact.telegram");
|
||||
parts.push(
|
||||
'<span class="' +
|
||||
escapeHtml(classPrefix) +
|
||||
'">' +
|
||||
escapeHtml(label) +
|
||||
": " +
|
||||
linkHtml +
|
||||
"</span>"
|
||||
);
|
||||
} else {
|
||||
parts.push(linkHtml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length === 0) return "";
|
||||
const rowClass = classPrefix + "-row";
|
||||
return '<div class="' + escapeHtml(rowClass) + '">' + parts.join(separator) + "</div>";
|
||||
}
|
||||
Reference in New Issue
Block a user