89 lines
2.9 KiB
JavaScript
89 lines
2.9 KiB
JavaScript
const STORAGE_KEY = 'gym_tracker_v6';
|
|
|
|
// Палитра из 16 контрастных цветов для клиентов
|
|
export const CLIENT_COLORS = [
|
|
'#bb86fc', // фиолетовый (дефолтный для личного режима)
|
|
'#03dac6', // бирюзовый
|
|
'#fbbc04', // жёлтый
|
|
'#cf6679', // розовый
|
|
'#64b5f6', // голубой
|
|
'#81c784', // зелёный
|
|
'#ffb74d', // оранжевый
|
|
'#ba68c8', // светло-фиолетовый
|
|
'#4dd0e1', // циан
|
|
'#e57373', // красный
|
|
'#aed581', // салатовый
|
|
'#ffd54f', // янтарный
|
|
'#9575cd', // пурпурный
|
|
'#4db6ac', // тиловый
|
|
'#f06292', // малиновый
|
|
'#7986cb', // индиго
|
|
];
|
|
|
|
export const DEFAULT_COLOR = CLIENT_COLORS[0];
|
|
|
|
export const state = {
|
|
workouts: [],
|
|
exerciseRef: [],
|
|
bodyMetrics: [],
|
|
|
|
workoutTypes: [],
|
|
metricTypes: [],
|
|
clients: [], // каждый клиент: {id, sync_id, full_name, birth_date, goal, color}
|
|
|
|
expandedWorkoutId: null,
|
|
expandedExerciseId: null,
|
|
nextId: 1,
|
|
|
|
mode: 'personal',
|
|
currentClientId: null, // больше не используется глобально, но оставим для совместимости
|
|
};
|
|
|
|
export function loadState() {
|
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
if (saved) {
|
|
try {
|
|
const parsed = JSON.parse(saved);
|
|
// Объединяем, чтобы не потерять новые поля при обновлении версии
|
|
Object.assign(state, parsed);
|
|
if (!state.workoutTypes) state.workoutTypes = [];
|
|
if (!state.metricTypes) state.metricTypes = [];
|
|
if (!state.clients) state.clients = [];
|
|
if (!state.mode) state.mode = 'personal';
|
|
} catch (e) {
|
|
console.error('Failed to load state', e);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function saveState() {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
|
updateSyncStatus();
|
|
}
|
|
|
|
export function genId() {
|
|
return (state.nextId++).toString();
|
|
}
|
|
|
|
// Генерация UUID v4 для sync_id (идемпотентность на бэкенде)
|
|
export function generateUUID() {
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
const r = Math.random() * 16 | 0;
|
|
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
|
|
export function updateSyncStatus() {
|
|
const dirty = state.workouts.filter(w => !w.synced);
|
|
const el = document.getElementById('sync-status');
|
|
if (!el) return;
|
|
if (dirty.length === 0) {
|
|
el.textContent = '● Всё синхронизировано';
|
|
el.className = 'sync-status synced';
|
|
} else {
|
|
el.textContent = `● ${dirty.length} не сохран.`;
|
|
el.className = 'sync-status pending';
|
|
}
|
|
}
|