import { state, saveState, generateUUID } from './state.js'; import { fetchWorkoutTypes, fetchMetricTypes, fetchClients, syncData } from './api.js'; import { renderMainScreen } from './render/main-screen.js'; export async function initAppData() { try { // Загружаем справочники с бэкенда state.workoutTypes = await fetchWorkoutTypes(); state.metricTypes = await fetchMetricTypes(); state.clients = await fetchClients(); saveState(); console.log('Справочники загружены с бэкенда'); } catch (e) { console.warn('Не удалось загрузить справочники (возможно, бэк недоступен):', e); // Не ломаем приложение, работаем офлайн с тем, что есть } } export function getDirtyWorkouts() { return state.workouts.filter(w => !w.synced); } export async function sync() { const dirty = getDirtyWorkouts(); if (dirty.length === 0) { alert('Нет данных для синхронизации'); return; } const btn = document.querySelector('.header-actions .btn-secondary'); if (btn) { btn.disabled = true; btn.textContent = '⏳ Отправка...'; } try { // Маппинг данных из формата фронтенда в формат бэкенда const payload = { workouts: dirty.map(w => { // Находим ID типа тренировки по его имени const wt = state.workoutTypes.find(t => t.name === w.type); return { sync_id: w.sync_id || generateUUID(), client_id: state.currentClientId || null, workout_type_id: wt ? wt.id : null, start_time: w.startTime, end_time: w.endTime, notes: w.notes || "", workout_exercises: (w.exercises || []).map((ex, exIdx) => ({ sync_id: ex.sync_id || generateUUID(), exercise_id: ex.exercise_id || null, exercise_name: ex.name, sort_order: exIdx, sets: (ex.sets || []).map((s, sIdx) => ({ sync_id: s.sync_id || generateUUID(), is_dropset: s.isDropset ? 1 : 0, weight: s.isDropset ? null : (parseFloat(s.weight) || null), reps: s.isDropset ? null : (parseInt(s.reps) || null), rating: s.rating || 3, notes: s.notes || "", sort_order: sIdx, timestamp: s.timestamp || new Date().toISOString(), dropset_items: s.isDropset ? (s.subSets || []).map((item, idx) => ({ weight: parseFloat(item.weight) || 0, reps: parseInt(item.reps) || 0, sort_order: idx })) : [] })) })), cardio_sessions: (w.cardio || []).map((c, cIdx) => ({ sync_id: c.sync_id || generateUUID(), cardio_type: c.type, purpose: c.purpose, duration: c.duration || null, distance: c.distance || null, speed: c.speed || null, incline: c.incline || null, resistance: c.resistance || null, notes: c.notes || "", sort_order: cIdx })) }; }) }; console.log('📤 Отправляем на синхронизацию:', payload); const response = await syncData(payload); console.log('✅ Ответ синхронизации:', response); // Помечаем локально как синхронизированные dirty.forEach(w => w.synced = true); saveState(); // Перерисовываем интерфейс (чтобы обновить бейджи и read-only режим) if (typeof window.renderMainScreen === 'function') { window.renderMainScreen(); } else { // Fallback если функция не экспортирована в window location.reload(); } alert(`Успешно синхронизировано: ${response.created.workouts || 0} тренировок`); } catch (e) { console.error('❌ Sync error:', e); alert('Ошибка синхронизации: ' + e.message); } finally { if (btn) { btn.disabled = false; btn.textContent = '🔄'; } } }