Panel raportów

Zaloguj się

Użyj adresu e-mail i hasła przypisanego do Twojego konta.

Nie masz jeszcze dostępu do panelu?

Jeśli logujesz się tutaj pierwszy raz i nie masz hasła, napisz do mnie. Wyślę Ci zaproszenie do aktywacji konta.

Panel trenera

Podopieczne

0 podopiecznych
Aktywne 0
Archiwalne 0
Bez raportu 0
Raport ≤ 7 dni 0
Strefa Formy

Twoja współpraca w jednym miejscu.

Tutaj będziesz mieć raporty, plan treningowy, historię treningów, progres i wiadomości.

Ostatnia aktualizacja Brak raportu Po zapisaniu pierwszego raportu zobaczysz tutaj najważniejsze informacje.
Raporty 0 zapisane w historii
Aktualna waga brak danych
Brzuch brak danych
Pośladki brak danych
Ostatni raport

Nie ma jeszcze raportu

Po zapisaniu raportu najważniejsze informacje pojawią się tutaj automatycznie.

Trening
Dieta
Sen
Stres
Twoja Strefa Formy

Wszystko w jednym miejscu

Kolejne moduły będziemy podpinać bez przebudowywania całego systemu.

R
Raporty Pomiary, historia i analiza zmian.
Działa
P
Plan treningowy Aktualny plan przypisany przez trenera.
Wkrótce
T
Treningi i progres Historia treningów, ciężary i rekordy.
Wkrótce
W
Wiadomości Komunikacja z trenerem bez szukania maili.
Wkrótce
Panel monitorowania postępów

Raport Postępów

Twoje postępy w jednym miejscu - raporty, pomiary i zmiany z tygodnia na tydzień.

Liczba raportów 0 Brak danych
Aktualna waga Dodaj pierwszy raport
Aktualny brzuch Dodaj pierwszy raport
Aktualne pośladki Dodaj pierwszy raport

Historia pomiarów

Kliknij „Edytuj”, aby poprawić wcześniejszy wpis.

Zmiany w czasie

Waga

Zmiana masy ciała w kilogramach

Obwód brzucha

Pomiar na wysokości pępka

Obwód pośladków

Pomiar w najszerszym miejscu

Progres treningowy

Łączna liczba raportów, w których pojawił się jakikolwiek progres

`); preview.document.close(); }catch(error){} return preview; } async function generateIphonePdf(previewWindow){ const entries = getSortedEntries(); if(!entries.length){ if(previewWindow && !previewWindow.closed) previewWindow.close(); showToast("Najpierw zapisz przynajmniej jeden raport"); return; } setPdfGenerationStatus("Przygotowywanie podsumowania…"); const pages = []; let pageNumber = 1; await pdfBuildSummaryPage(pages,entries,pageNumber++); pageNumber += await pdfBuildHistoryPages(pages,entries,pageNumber); pageNumber += await pdfBuildChartPages(pages,entries,pageNumber); pageNumber += await pdfBuildReportDetailPages(pages,entries,pageNumber); setPdfGenerationStatus("Łączenie stron w jeden plik PDF…"); const blob = pdfBuildBlob(pages); const url = URL.createObjectURL(blob); const filename = pdfSafeFilename(); let opened = false; if(previewWindow && !previewWindow.closed){ try{ previewWindow.document.title = filename; previewWindow.location.replace(url); opened = true; }catch(error){} } if(!opened){ const link = document.createElement("a"); link.href = url; link.target = "_blank"; link.rel = "noopener"; link.download = filename; document.body.appendChild(link); link.click(); link.remove(); } hidePdfGenerationStatus(); showToast("PDF został przygotowany"); setTimeout(() => URL.revokeObjectURL(url),10*60*1000); } async function printFullHistory(event){ if(event && typeof event.preventDefault === "function") event.preventDefault(); if(isAppleMobile()){ const previewWindow = pdfOpenPreparingWindow(); try{ await generateIphonePdf(previewWindow); }catch(error){ console.error(error); hidePdfGenerationStatus(); if(previewWindow && !previewWindow.closed) previewWindow.close(); showToast("Nie udało się utworzyć PDF. Otwieram zapis awaryjny."); enterWebPdfMode(); } return false; } document.body.classList.remove("print-latest-only"); renderPrintHistoryPages(); preparePrintTitle(); try{ window.focus(); window.print(); }catch(error){ restorePrintMode(); alert("Nie udało się otworzyć okna drukowania. Użyj skrótu Ctrl+P na Windowsie lub Command+P na Macu."); } return false; } window.__raportPrint = printFullHistory; function loadDemoData(){ if(state.entries.length && !confirm("Dodać przykładowe dane do istniejącej historii?")) return; const now = new Date(); const samples = [ {days:-70,weight:68.8,abdomen:83.0,waist:73.4,glutes:97.2,bicepsLeft:28.1,bicepsRight:28.4,thighLeft:57.2,thighRight:57.5,calfLeft:36.1,calfRight:36.3,training:75,diet:75,hunger:3,notes:"Start współpracy. Treningi poszły dobrze, ale pod koniec tygodnia miałam mniej energii. Chciałabym też wiedzieć, czy na razie zostawiamy cardio bez zmian."}, {days:-56,weight:68.1,abdomen:81.8,waist:72.5,glutes:97.5,bicepsLeft:28.0,bicepsRight:28.3,thighLeft:57.0,thighRight:57.3,calfLeft:36.0,calfRight:36.2,training:100,diet:75,hunger:3,notes:"Dobra regularność. Dietę trzymałam przez większość dni, tylko w weekend wypadło jedno wyjście."}, {days:-42,weight:67.4,abdomen:80.5,waist:71.7,glutes:98.0,bicepsLeft:28.0,bicepsRight:28.2,thighLeft:56.8,thighRight:57.1,calfLeft:35.9,calfRight:36.1,training:100,diet:100,hunger:3,notes:"Siła rośnie, szczególnie w hip thrustach. Czuję też lepiej pośladki podczas treningu."}, {days:-28,weight:66.9,abdomen:79.2,waist:70.9,glutes:98.6,bicepsLeft:27.9,bicepsRight:28.1,thighLeft:56.7,thighRight:57.0,calfLeft:35.8,calfRight:36.0,training:75,diet:75,hunger:4,notes:"Gorszy tydzień przez cykl. Miałam większy głód i opuściłam jeden trening, ale poza tym plan był zrealizowany."}, {days:-14,weight:66.3,abdomen:78.1,waist:70.1,glutes:99.1,bicepsLeft:27.9,bicepsRight:28.0,thighLeft:56.5,thighRight:56.8,calfLeft:35.8,calfRight:35.9,training:100,diet:100,hunger:3,notes:"Sylwetka wyraźnie się zmienia. Spodnie są luźniejsze w pasie, a pośladki wyglądają pełniej."}, {days:0,weight:65.9,abdomen:77.3,waist:69.6,glutes:99.8,bicepsLeft:27.8,bicepsRight:28.0,thighLeft:56.4,thighRight:56.7,calfLeft:35.7,calfRight:35.9,training:100,diet:100,hunger:3,notes:"Bardzo dobry raport. Mam więcej energii i nie czuję już takiego głodu wieczorami. Czy w kolejnym tygodniu dokładamy ciężar w ćwiczeniach na pośladki?"} ]; const pad = value => String(value).padStart(2,"0"); const dateFromDays = days => { const date = new Date(now); date.setDate(date.getDate()+days); return `${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())}`; }; state.entries.push(...samples.map((sample,index) => normalizeEntry({ ...sample, id:uid(), date:dateFromDays(sample.days), trainingReasons:sample.training < 100 ? (index === 3 ? ["energy"] : ["time"]) : [], dietReasons:sample.diet < 100 ? (index === 3 ? ["hunger","stress_snacking"] : ["weekend_event"]) : [], progressOptions:index === 3 ? ["none"] : (index % 2 ? ["weight","reps"] : ["technique","range"]), trainingIntensity:index === 3 ? "too_hard" : "right", recovery:index === 3 ? "often_tired" : "mostly", contextChanges:index === 3 ? ["stressful_period"] : ["none"], steps:7800 + index * 250, cardioSessions:index === 3 ? 1 : 2, cycleImpact:index === 3 ? "yes" : "no", sleep:index === 3 ? 2 : 4, stress:index === 3 ? 5 : 3, updatedAt:new Date().toISOString() }))); if(!state.profile.clientName) state.profile.clientName = "Anna – dane testowe"; syncProfileToUI(); saveState(); resetForm(); renderAll(); showToast("Przykładowe dane zostały dodane"); } function clearAll(){ if(!confirm("Usunąć całą historię pomiarów i dane profilu? Tej operacji nie można cofnąć bez kopii zapasowej.")) return; state = {profile:{clientName:"",autoBackup:false,lastBackupAt:""},entries:[]}; try{ localStorage.removeItem(STORAGE_KEY); localStorage.removeItem(LEGACY_KEY); localStorage.removeItem(NAME_KEY); localStorage.removeItem(PREFERENCES_KEY); }catch(error){} syncProfileToUI(); saveState(); resetForm(); renderAll(); showToast("Wszystkie dane zostały usunięte"); } function adjustNumber(targetId,direction){ const input = $(targetId); if(!input) return; const step = Number(input.step) || .1; const current = parseNumber(input.value) ?? 0; const min = input.min === "" ? -Infinity : Number(input.min); const max = input.max === "" ? Infinity : Number(input.max); const next = Math.min(max,Math.max(min,Math.round((current+step*direction)*10)/10)); input.value = next; input.dispatchEvent(new Event("input",{bubbles:true})); } function focusNextFormControl(current){ const controls = [...els.form.querySelectorAll( 'input:not([type="hidden"]), select, textarea, button[type="button"]' )].filter(control => { if(control.disabled) return false; if(control.classList.contains("hidden")) return false; if(control.closest(".hidden")) return false; const style = window.getComputedStyle(control); return style.display !== "none" && style.visibility !== "hidden"; }); const index = controls.indexOf(current); const next = controls.slice(index + 1).find(control => { if(control.type === "checkbox" && !control.checked){ return true; } return true; }); if(next){ next.focus({preventScroll:false}); next.scrollIntoView({behavior:"smooth",block:"center"}); }else{ current.blur(); } } function bindEvents(){ els.form.addEventListener("keydown",event => { const target = event.target; if(event.key !== "Enter") return; if(target.tagName === "TEXTAREA") return; if(target.type === "submit" || target.tagName === "BUTTON") return; event.preventDefault(); event.stopPropagation(); if(target.matches('input[type="text"], input[type="number"], input[type="date"]')){ focusNextFormControl(target); } }); els.form.addEventListener("submit",submitReport); els.cancelEdit.addEventListener("click",() => { resetForm(); showLatestSavedChangeSummary(); }); const updateSleepValue = () => { els.sleepValue.textContent = `Wybrano: ${els.sleep.value}/5`; }; const updateStressValue = () => { els.stressValue.textContent = `Wybrano: ${els.stress.value}/5`; }; els.sleep.addEventListener("input",updateSleepValue); els.sleep.addEventListener("change",updateSleepValue); els.stress.addEventListener("input",updateStressValue); els.stress.addEventListener("change",updateStressValue); document.querySelectorAll('input[name="progressOption"]').forEach(input => { input.addEventListener("change",() => { const all = [...document.querySelectorAll('input[name="progressOption"]')]; if(input.value === "none" && input.checked){ all.filter(item => item.value !== "none").forEach(item => item.checked = false); }else if(input.value !== "none" && input.checked){ const none = all.find(item => item.value === "none"); if(none) none.checked = false; } }); }); els.training.addEventListener("change",() => updateConditionalFields(true)); els.diet.addEventListener("change",() => updateConditionalFields(true)); [els.trainingReasonOther,els.dietReasonOther,els.contextOther].forEach(input => { input.addEventListener("keydown",event => { if(event.key !== "Enter") return; event.preventDefault(); event.stopPropagation(); focusNextFormControl(input); }); }); document.querySelectorAll('input[name="trainingReason"]').forEach(input => { input.addEventListener("change",() => updateReasonOtherField("trainingReason",els.trainingReasonOther)); }); document.querySelectorAll('input[name="dietReason"]').forEach(input => { input.addEventListener("change",() => updateReasonOtherField("dietReason",els.dietReasonOther)); }); document.querySelectorAll('input[name="contextChange"]').forEach(input => { input.addEventListener("change",() => { const all = [...document.querySelectorAll('input[name="contextChange"]')]; if(input.value === "none" && input.checked){ all.filter(item => item.value !== "none").forEach(item => item.checked = false); }else if(input.value !== "none" && input.checked){ const none = all.find(item => item.value === "none"); if(none) none.checked = false; } if(!all.some(item => item.checked)){ const none = all.find(item => item.value === "none"); if(none) none.checked = true; } updateReasonOtherField("contextChange",els.contextOther); }); }); els.clientName.addEventListener("input",saveState); els.clientName.addEventListener("change",saveState); els.clientName.addEventListener("blur",saveState); els.autoBackup.addEventListener("change",() => { state.profile.autoBackup = els.autoBackup.checked; saveState(); showToast( els.autoBackup.checked ? "Automatyczne pobieranie kopii zostało włączone" : "Automatyczne pobieranie kopii zostało wyłączone" ); }); [ els.reportDate, els.weight, els.abdomen, els.waist, els.glutes, els.bicepsLeft, els.bicepsRight, els.thighLeft, els.thighRight, els.calfLeft, els.calfRight ].forEach(input => { input.addEventListener("input",updateLiveChangeSummary); input.addEventListener("change",updateLiveChangeSummary); }); document.addEventListener("click",event => { const button = event.target.closest(".step-btn"); if(button) adjustNumber(button.dataset.target,Number(button.dataset.direction)); }); document.querySelectorAll(".number-control").forEach(control => { control.addEventListener("wheel",event => { const input = control.querySelector('input[type="number"]'); if(!input) return; event.preventDefault(); adjustNumber(input.id,event.deltaY < 0 ? 1 : -1); input.focus({preventScroll:true}); },{passive:false}); }); $("backupBtn").addEventListener("click",downloadBackup); $("importBtn").addEventListener("click",() => $("importFile").click()); $("importFile").addEventListener("change",event => { const file = event.target.files[0]; if(file) importBackup(file); event.target.value = ""; }); $("clearBtn").addEventListener("click",clearAll); const showCleanPdfView = document.getElementById("showCleanPdfView"); const cancelIosPdfView = document.getElementById("cancelIosPdfView"); const hideIosPdfInstructions = document.getElementById("hideIosPdfInstructions"); const returnFromIosPdf = document.getElementById("returnFromIosPdf"); if(showCleanPdfView){ showCleanPdfView.addEventListener("click",() => { document.body.classList.add("clean-pdf-view"); document.body.classList.remove("ios-pdf-instructions-hidden"); const guide = document.getElementById("iosPdfGuide"); const readyBar = document.getElementById("iosPdfReadyBar"); if(guide) guide.hidden = true; if(readyBar) readyBar.hidden = false; window.scrollTo({top:0,behavior:"smooth"}); }); } if(cancelIosPdfView){ cancelIosPdfView.addEventListener("click",exitWebPdfMode); } if(hideIosPdfInstructions){ hideIosPdfInstructions.addEventListener("click",() => { document.body.classList.add("ios-pdf-instructions-hidden"); const readyBar = document.getElementById("iosPdfReadyBar"); if(readyBar) readyBar.hidden = true; window.scrollTo({top:0,behavior:"smooth"}); showToast("Raport gotowy. W Safari wybierz Udostępnij → Oznaczenia"); }); } if(returnFromIosPdf){ returnFromIosPdf.addEventListener("click",exitWebPdfMode); } // Drukowanie jest wywoływane bezpośrednio z przycisku, // aby Chrome, Safari i przeglądarki mobilne nie blokowały okna drukowania. window.addEventListener("beforeprint",renderPrintHistoryPages); window.addEventListener("afterprint",restorePrintMode); els.rangeFilter.addEventListener("change",renderCharts); window.addEventListener("beforeunload",saveState); window.addEventListener("pagehide",saveState); document.addEventListener("visibilitychange",() => { if(document.visibilityState === "hidden") saveState(); }); } function requestPersistentStorage(){ try{ if(navigator.storage && typeof navigator.storage.persist === "function"){ navigator.storage.persist().catch(() => {}); } }catch(error){} } // Most dla warstwy Supabase. Dzięki temu obecny interfejs, // wykresy i PDF nadal pracują na state.entries, ale dane źródłowe // są pobierane z chmury po zalogowaniu. window.reportAppBridge = { applyCloudState(profile,entries){ const normalizedEntries = Array.isArray(entries) ? entries.map(normalizeEntry) : []; const nameFromReports = [...normalizedEntries].reverse().find(item => item.clientName)?.clientName || ""; state = { profile:{ clientName:String(profile?.full_name || nameFromReports || ""), autoBackup:false, lastBackupAt:"" }, entries:normalizedEntries }; editingId = null; syncProfileToUI(); resetForm(); renderAll(); updatePreviousValueHints(); }, clearCloudState(){ state = { profile:{clientName:"",autoBackup:false,lastBackupAt:""}, entries:[] }; editingId = null; syncProfileToUI(); resetForm(); renderAll(); }, getClientName(){ return els.clientName.value.trim() || state.profile.clientName || ""; }, getDashboardData(){ const sorted = getSortedEntries(); const first = sorted[0] || null; const latest = sorted[sorted.length - 1] || null; return { clientName:els.clientName.value.trim() || state.profile.clientName || "", reportCount:sorted.length, first:first ? {...first} : null, latest:latest ? {...latest} : null }; } }; function init(){ requestPersistentStorage(); loadState(); syncProfileToUI(); initCharts(); bindEvents(); resetForm(); renderAll(); } init(); })();