import { state, saveState, CLIENT_COLORS, DEFAULT_COLOR, genId, generateUUID } from '../state.js';
import { showEditExerciseDialog } from '../modals/edit-exercise-modal.js';
import { showBodyMetricsDialog } from '../modals/metrics-modal.js';
import { showModal } from '../modals/modal-base.js';
export function showSettings() {
const app = document.getElementById('app');
app.innerHTML = '';
const screen = document.createElement('div');
screen.className = 'settings-screen';
screen.innerHTML = `
⚙️ Настройки
👥 Клиенты
Управление клиентами и их замерами. Цвет клиента используется для подсветки тренировок.
📋 Справочник упражнений
Управляйте списком упражнений и привязывайте их к типам тренировок.
📏 Личные замеры
Замеры без привязки к клиенту (для личного режима).
🗑 Данные
`;
app.appendChild(screen);
_renderClientsList();
_renderRefExercises();
_renderPersonalMetricsList();
}
export function _renderPersonalMetricsList() {
const list = document.getElementById('body-metrics-list');
if (!list) return;
const personalMetrics = state.bodyMetrics.filter(m => !m.client_id);
if (personalMetrics.length === 0) {
list.innerHTML = 'Нет личных замеров
';
return;
}
list.innerHTML = personalMetrics.map(m => {
const date = new Date(m.date);
const dateStr = date.toLocaleDateString('ru-RU') + ' ' + date.toLocaleTimeString('ru-RU', {hour:'2-digit', minute:'2-digit'});
const parts = [];
if (m.weight) parts.push(`⚖️ ${m.weight} кг`);
if (m.chest) parts.push(`📏 ${m.chest} см`);
if (m.waist) parts.push(`📏 ${m.waist} см`);
if (m.hips) parts.push(`📏 ${m.hips} см`);
if (m.arms) parts.push(`💪 ${m.arms} см`);
if (m.legs) parts.push(`🦵 ${m.legs} см`);
return `
${dateStr}
${parts.join(' • ') || '—'}
`;
}).join('');
}
export function addRefExercise() {
const input = document.getElementById('new-ref-exercise');
const typeSelect = document.getElementById('new-ref-type');
const name = input.value.trim();
if (!name) return;
const normalizedName = name.trim().toLowerCase();
const exists = state.exerciseRef.find(
e => e.name.trim().toLowerCase() === normalizedName
);
if (!exists) {
state.exerciseRef.push({
id: (state.nextId++).toString(),
name: name.trim(),
workoutType: typeSelect.value,
});
}
saveState();
input.value = '';
typeSelect.value = '';
_renderRefExercises();
}
export function deleteRefExercise(id) {
if (!confirm('Удалить из справочника? (в тренировках останется)')) return;
state.exerciseRef = state.exerciseRef.filter(e => e.id !== id);
saveState();
_renderRefExercises();
}
export function updateRefExerciseType(id, newType) {
const ex = state.exerciseRef.find(e => e.id === id);
if (ex) {
ex.workoutType = newType;
saveState();
}
}
export function editRefExercise(id) {
const ex = state.exerciseRef.find(e => e.id === id);
if (!ex) return;
showEditExerciseDialog(ex, (updatedData) => {
if (!updatedData) return;
Object.assign(ex, updatedData);
saveState();
_renderRefExercises();
});
}
export function clearAllData() {
if (!confirm('Удалить ВСЕ данные? Это действие необратимо!')) return;
if (!confirm('Точно уверены?')) return;
localStorage.clear();
location.reload();
}
export function _renderRefExercises() {
const list = document.getElementById('ref-exercises-list');
if (!list) return;
if (state.exerciseRef.length === 0) {
list.innerHTML = 'Справочник пуст
';
return;
}
const sorted = [...state.exerciseRef].sort((a, b) => {
if (a.workoutType !== b.workoutType) return (a.workoutType || '').localeCompare(b.workoutType || '');
return a.name.localeCompare(b.name);
});
// Генерируем опции для select динамически из state.workoutTypes
const typeOptions = state.workoutTypes.map(wt =>
``
).join('');
list.innerHTML = sorted.map(ex => {
let count = 0;
state.workouts.forEach(w => {
w.exercises.forEach(e => {
if (e.name.trim().toLowerCase() === ex.name.trim().toLowerCase()) count++;
});
});
return `
${ex.name}
(${count}×)
${ex.workoutType ? `${ex.workoutType}` : ''}
`;
}).join('');
}
export function _renderMetricTypes() {
const container = document.getElementById('metric-types-list');
if (!container) return;
if (state.metricTypes.length === 0) {
container.innerHTML = 'Нет типов замеров
';
return;
}
container.innerHTML = state.metricTypes.map(mt => `
`).join('');
}
export function _renderBodyMetricsList() {
const list = document.getElementById('body-metrics-list');
if (!list) return;
if (state.bodyMetrics.length === 0) {
list.innerHTML = 'Нет замеров
';
return;
}
list.innerHTML = state.bodyMetrics.map(m => {
const date = new Date(m.date);
const dateStr = date.toLocaleDateString('ru-RU') + ' ' + date.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' });
const parts = [];
if (m.weight) parts.push(`⚖️ ${m.weight} кг`);
if (m.chest) parts.push(`📏 ${m.chest} см`);
if (m.waist) parts.push(`📏 ${m.waist} см`);
if (m.hips) parts.push(`📏 ${m.hips} см`);
if (m.arms) parts.push(`💪 ${m.arms} см`);
if (m.legs) parts.push(`🦵 ${m.legs} см`);
return `
${dateStr}
${parts.join(' • ') || '—'}
`;
}).join('');
}
export function showAddClientModal(editingClient = null) {
const isEdit = !!editingClient;
const client = editingClient || { full_name: '', birth_date: '', goal: '', color: DEFAULT_COLOR };
const paletteHtml = CLIENT_COLORS.map(color => `
`).join('');
const html = `
`;
showModal(html);
window._tempClientColor = client.color;
}
export function selectClientColor(color) {
window._tempClientColor = color;
document.querySelectorAll('#m-client-palette .color-swatch').forEach(el => {
el.classList.toggle('selected', el.style.background === color ||
rgbToHex(el.style.backgroundColor) === color.toLowerCase());
});
}
function rgbToHex(rgb) {
if (rgb.startsWith('#')) return rgb.toLowerCase();
const match = rgb.match(/\d+/g);
if (!match) return rgb;
return '#' + match.slice(0, 3).map(x => parseInt(x).toString(16).padStart(2, '0')).join('');
}
export function submitClient(editingId) {
const name = document.getElementById('m-client-name').value.trim();
if (!name) { alert('Введите ФИО'); return; }
const color = window._tempClientColor || DEFAULT_COLOR;
if (editingId) {
// Редактирование
const client = state.clients.find(c => c.id == editingId);
if (client) {
client.full_name = name;
client.birth_date = document.getElementById('m-client-dob').value || null;
client.goal = document.getElementById('m-client-goal').value.trim();
client.color = color;
}
} else {
// Создание
const newClient = {
id: genId(),
sync_id: generateUUID(),
full_name: name,
birth_date: document.getElementById('m-client-dob').value || null,
goal: document.getElementById('m-client-goal').value.trim(),
color: color,
is_active: 1,
};
state.clients.push(newClient);
}
saveState();
window.ModalBase.closeModal();
_renderClientsList();
window.renderMainScreen(); // Обновляем цвета тренировок
}
export function deleteClient(id) {
if (!confirm('Удалить клиента? Тренировки останутся, но без привязки.')) return;
state.clients = state.clients.filter(c => c.id != id);
// Отвязываем тренировки
state.workouts.forEach(w => {
if (w.client_id == id) w.client_id = null;
});
// Отвязываем замеры
state.bodyMetrics.forEach(m => {
if (m.client_id == id) m.client_id = null;
});
saveState();
_renderClientsList();
window.renderMainScreen();
}
export function _renderClientsList() {
const list = document.getElementById('clients-list');
if (!list) return;
if (state.clients.length === 0) {
list.innerHTML = 'Нет клиентов. Добавьте первого!
';
return;
}
list.innerHTML = state.clients.map(c => {
// Считаем замеры этого клиента
const clientMetrics = state.bodyMetrics.filter(m => m.client_id == c.id);
const lastMetric = clientMetrics[0]; // уже отсортированы по дате
let metricsPreview = 'Нет замеров';
if (lastMetric) {
const parts = [];
if (lastMetric.weight) parts.push(`⚖️ ${lastMetric.weight} кг`);
if (lastMetric.chest) parts.push(`📏 ${lastMetric.chest} см`);
metricsPreview = parts.join(' • ') || 'Замеры без значений';
}
// Считаем тренировки
const clientWorkouts = state.workouts.filter(w => w.client_id == c.id);
return `
${c.full_name}
${c.goal || 'Цель не указана'}
🏋️ ${clientWorkouts.length} трен. • 📏 ${clientMetrics.length} замеров
${lastMetric ? `
Последний: ${metricsPreview}
` : ''}
`;
}).join('');
}
export function showClientMetrics(clientId) {
const client = state.clients.find(c => c.id == clientId);
if (!client) return;
const clientMetrics = state.bodyMetrics
.filter(m => m.client_id == clientId)
.sort((a, b) => new Date(b.date) - new Date(a.date));
const metricsHtml = clientMetrics.length === 0
? 'Нет замеров
'
: clientMetrics.map(m => {
const date = new Date(m.date);
const dateStr = date.toLocaleDateString('ru-RU') + ' ' + date.toLocaleTimeString('ru-RU', {hour:'2-digit', minute:'2-digit'});
const parts = [];
if (m.weight) parts.push(`⚖️ ${m.weight} кг`);
if (m.chest) parts.push(`📏 ${m.chest} см`);
if (m.waist) parts.push(`📏 ${m.waist} см`);
if (m.hips) parts.push(`📏 ${m.hips} см`);
if (m.arms) parts.push(`💪 ${m.arms} см`);
if (m.legs) parts.push(`🦵 ${m.legs} см`);
return `
${dateStr}
${parts.join(' • ') || '—'}
`;
}).join('');
const html = `
📏 Замеры: ${client.full_name}
${metricsHtml}
`;
showModal(html);
}
export function addClientMetric(clientId) {
showBodyMetricsDialog(
state.bodyMetrics.filter(m => m.client_id == clientId),
(newMetric) => {
if (!newMetric) return;
newMetric.id = genId();
newMetric.client_id = parseInt(clientId);
state.bodyMetrics.unshift(newMetric);
state.bodyMetrics.sort((a, b) => new Date(b.date) - new Date(a.date));
saveState();
showClientMetrics(clientId); // Перерисовываем модалку
}
);
}
export function editClientMetric(metricId, clientId) {
const metric = state.bodyMetrics.find(m => m.id === metricId);
if (!metric) return;
showBodyMetricsDialog(
state.bodyMetrics,
(updatedData) => {
if (!updatedData) return;
Object.assign(metric, updatedData);
state.bodyMetrics.sort((a, b) => new Date(b.date) - new Date(a.date));
saveState();
showClientMetrics(clientId);
},
metric
);
}
export function deleteClientMetric(metricId, clientId) {
if (!confirm('Удалить замер?')) return;
state.bodyMetrics = state.bodyMetrics.filter(m => m.id !== metricId);
saveState();
showClientMetrics(clientId);
}
// Обновляем экспорт
window.SettingsLogic = {
show: showSettings,
addRefExercise, deleteRefExercise, updateRefExerciseType,
editRefExercise, clearAllData,
_renderRefExercises, _renderBodyMetricsList,
// Клиенты:
showAddClientModal, submitClient, deleteClient, _renderClientsList,
selectClientColor,
// Замеры клиента:
showClientMetrics, addClientMetric, editClientMetric, deleteClientMetric,
};
// Делаем state доступным для inline onclick
window.state = state;