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 => `
${mt.name} ${mt.unit}
`).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 = ` `; 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;