f1tness_parser/frontend/js/common.js

67 lines
2.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Shared helpers: API access, DOM building, formatting
const API = '/api/v1';
export async function api(path, options) {
const response = await fetch(API + path, options);
if (!response.ok) {
const error = new Error(`HTTP ${response.status}`);
error.status = response.status;
throw error;
}
return response.json();
}
/** h('div', {class: 'x'}, 'text', childNode) -> element; strings become text nodes */
export function h(tag, attrs = {}, ...children) {
const element = document.createElement(tag);
for (const [name, value] of Object.entries(attrs)) {
if (value !== null && value !== undefined && value !== false) {
element.setAttribute(name, value === true ? '' : value);
}
}
element.append(...children.filter((child) => child !== null && child !== undefined));
return element;
}
export function alert(kind, ...children) {
return h('div', { class: `alert alert-${kind}` }, ...children);
}
/** "2026-10-21" -> "21.10.2026" (no Date: a timezone must not shift the day) */
export function formatDate(iso) {
const [year, month, day] = iso.split('-');
return `${day}.${month}.${year}`;
}
/** "2026-10-23T17:27:00+00:00" -> "23.10.2026 17:27" in the viewer's timezone */
export function formatDateTime(iso) {
return new Date(iso).toLocaleString('ru-RU', { dateStyle: 'short', timeStyle: 'short' }).replace(',', '');
}
/** Rounded to one decimal without trailing zeros: 22.5 -> "22.5", 20 -> "20" */
export function num(value) {
return String(Math.round(value * 10) / 10);
}
/** Same with an explicit sign: "+12", "-3.5", "+0" */
export function signed(value) {
const rounded = Math.round(value * 10) / 10;
return (rounded > 0 ? '+' : '') + rounded;
}
/** Approaches as "weight × reps" chips */
export function approachChips(approaches) {
return approaches.map((approach) =>
h('span', { class: 'approach' },
`${approach.weight ? num(approach.weight) + ' кг' : 'без веса'} × ${approach.reps}`)
);
}
export function perArmBadge() {
return h('span', { class: 'badge text-bg-light border', title: 'Вес указан на каждую руку (гантель)' }, 'на руку');
}
export function tip(text, hint) {
return h('span', { class: 'has-tip', title: hint }, text);
}