f1tness_parser/frontend/js/common.js

75 lines
2.5 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';
/** What a training without a trainer is called */
export const SOLO = 'соло';
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}`;
}
/** "First Last" of a user, the email when the name is empty */
export function userName(user) {
return `${user.first_name} ${user.last_name}`.trim() || user.email;
}
/** "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);
}