From a20047ff59b7c7ffe56a1104b63fa4c4f62fd442 Mon Sep 17 00:00:00 2001 From: migatu Date: Fri, 24 Jul 2026 17:10:09 +0200 Subject: [PATCH] feat(prezentacja): wspolne dane formularza miedzy zakladkami + imie i nazwisko (PRE-21) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trzecia wskazowka partnerow. Kazda zakladke (Horoskop / Interpretacje / Kalendarz) wypelnialo sie od nowa — te same imie, data, godzina, strefa i miejsce. Teraz raz wpisane dane wedruja za uzytkownikiem, a zmiana w jednej zakladce przenosi sie na pozostale. Nowy formsync.js trzyma stan w localStorage. Dlaczego nie sesja na serwerze: serwer zostaje BEZSTANOWY — zadnego magazynu sesji, zadnych danych urodzeniowych trzymanych po stronie uslugi (spojne z postawa z LOG-32/PRE-16). Przy okazji dane synchronizuja sie tez miedzy osobnymi kartami przegladarki, bo zdarzenie `storage` daje to za darmo. Dolozone pole „imie i nazwisko" (wszystkie trzy zakladki) — potrzebne do naglowka raportu PDF (PRE-24). Handlery przyjmuja je i oddaja, wiec nie znika po przeliczeniu. Dwie rzeczy, ktore trzeba bylo domknac, zeby to dzialalo naprawde: 1. KOLEJNOSC SKRYPTOW. formsync.js ladowany w z `defer` — skrypty defer wykonuja sie w kolejnosci dokumentu, wiec ten zdazy odtworzyc wspolrzedne, ZANIM geo.js zbuduje mape. Mapa startuje od razu we wlasciwym miejscu, zamiast przeskakiwac po chwili. 2. geo.js ustawia pola z KODU (`.value = ...`), co samo z siebie NIE wywoluje zdarzen — bez tego synchronizacja przegapilaby kazdy wybor z mapy, z wyszukiwarki i z „Tu i teraz". Dolozony setVal(), ktory jawnie zglasza `change`. Zakladka bez danego pola (np. Sygnifikatory) nie kasuje wartosci zapamietanej gdzie indziej; uszkodzony wpis w localStorage nie blokuje formularza. Q-14 rozstrzygniete: lancuch LaTeX->PDF stanie jako OSOBNA USLUGA render — spojne z izolacja swisseph (LOG-27), obraz produktu zostaje maly. Weryfikacja na zywym stacku (data+logika+prezentacja) w przegladarce: dane wpisane w Horoskopie pojawily sie w Interpretacjach; zmiana godziny w Interpretacjach dotarla do Kalendarza; pola nieobecne na zakladce (zodiak, system domow) przetrwaly; po POST imie zostalo, a horoskop policzyl sie normalnie. Testy: 22 nowe strukturalne. Calosc: prezentacja 67 passed. Co-Authored-By: Claude Opus 4.8 --- docs/astrololo_wymagania.xlsx | Bin 36953 -> 36969 bytes services/presentation/app/main.py | 9 +- services/presentation/app/static/formsync.js | 98 ++++++++++++++++++ services/presentation/app/static/geo.js | 19 +++- services/presentation/app/templates/base.html | 5 + .../presentation/app/templates/chart.html | 6 ++ .../presentation/app/templates/interpret.html | 6 ++ .../presentation/app/templates/timeline.html | 6 ++ services/presentation/tests/test_formsync.py | 93 +++++++++++++++++ services/presentation/tests/test_static_js.py | 4 +- 10 files changed, 237 insertions(+), 9 deletions(-) create mode 100644 services/presentation/app/static/formsync.js create mode 100644 services/presentation/tests/test_formsync.py diff --git a/docs/astrololo_wymagania.xlsx b/docs/astrololo_wymagania.xlsx index 7d529d9b9fe6d5e488c3fb76d2bed5e3ad78bd4a..c7bb6d5977d1843145a729627904be61911e958f 100644 GIT binary patch delta 6001 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 HJev6*- z `defer`, więc wykonuje się +// PO sparsowaniu dokumentu, ale PRZED geo.js (skrypty defer idą w kolejności +// dokumentu). Dzięki temu mapa startuje już z odtworzonymi współrzędnymi i nie +// trzeba jej potem przestawiać. +(function () { + 'use strict'; + + var KEY = 'astrololo.form.v1'; + + // Pola wspólne dla zakładek. Nazwa pola formularza -> jak je znaleźć. + // `geoSearch` (nazwa miejsca) nie ma atrybutu name — szukamy po id. + var FIELDS = [ + { key: 'person', sel: 'input[name=person]' }, + { key: 'date', sel: 'input[name=date]' }, + { key: 'time', sel: 'input[name=time]' }, + { key: 'tz_offset', sel: 'input[name=tz_offset]' }, + { key: 'lat', sel: 'input[name=lat]' }, + { key: 'lon', sel: 'input[name=lon]' }, + { key: 'house_system', sel: 'select[name=house_system]' }, + { key: 'zodiac', sel: 'select[name=zodiac]' }, + { key: 'place', sel: '#geoSearch' } + ]; + + function load() { + try { + return JSON.parse(localStorage.getItem(KEY) || '{}') || {}; + } catch (e) { + return {}; // uszkodzony wpis nie może zablokować strony + } + } + + function save(state) { + try { + localStorage.setItem(KEY, JSON.stringify(state)); + } catch (e) { + /* tryb prywatny albo brak miejsca — trudno, formularz ma działać dalej */ + } + } + + function apply(state) { + FIELDS.forEach(function (f) { + var el = document.querySelector(f.sel); + if (!el) return; + var v = state[f.key]; + if (v === undefined || v === null || v === '') return; + if (el.value !== v) el.value = v; + }); + } + + function collect() { + var state = load(); + FIELDS.forEach(function (f) { + var el = document.querySelector(f.sel); + if (!el) return; // pola nieobecnego na tej zakładce nie kasujemy + if (el.value === '' || el.value === null) return; + state[f.key] = el.value; + }); + return state; + } + + // 1) odtwórz to, co użytkownik wpisał gdzie indziej + apply(load()); + + // 2) zapisuj każdą zmianę (input dla wpisywania, change dla list i kalendarzy) + FIELDS.forEach(function (f) { + var el = document.querySelector(f.sel); + if (!el) return; + ['input', 'change'].forEach(function (ev) { + el.addEventListener(ev, function () { save(collect()); }); + }); + }); + + // 3) zapisz stan po wysłaniu formularza — po przeładowaniu strony pola mają + // pokazywać to, co faktycznie policzyliśmy + document.addEventListener('submit', function () { save(collect()); }, true); + + // 4) druga otwarta karta zmieniła dane -> dociągnij je tutaj na żywo + window.addEventListener('storage', function (e) { + if (e.key === KEY) apply(load()); + }); + + // 5) geo.js ustawia lat/lon z mapy poza polami formularza — po takiej zmianie + // też chcemy zapamiętać (zdarzenie leci z „Tu i teraz" oraz z mapy) + document.addEventListener('astrololo:coords', function () { + setTimeout(function () { save(collect()); }, 0); + }); +})(); diff --git a/services/presentation/app/static/geo.js b/services/presentation/app/static/geo.js index 9a0b3e6..3dbb4de 100644 --- a/services/presentation/app/static/geo.js +++ b/services/presentation/app/static/geo.js @@ -38,9 +38,18 @@ // Kontener mógł być renderowany zanim ustaliła się jego szerokość — poprawka. setTimeout(function () { map.invalidateSize(); }, 60); + // Ustawienie `.value` z kodu NIE wywołuje zdarzeń, więc synchronizacja + // między zakładkami (formsync.js) przegapiłaby zmiany z mapy. Zgłaszamy je + // jawnie przez `change`. + function setVal(el, value) { + if (!el) return; + el.value = value; + el.dispatchEvent(new Event('change', { bubbles: true })); + } + function setCoords(lat, lon, fromMap) { - latEl.value = lat.toFixed(4); - lonEl.value = lon.toFixed(4); + setVal(latEl, lat.toFixed(4)); + setVal(lonEl, lon.toFixed(4)); marker.setLatLng([lat, lon]); if (!fromMap) map.setView([lat, lon], Math.max(map.getZoom(), 12)); if (fromMap) reverseName(lat, lon); @@ -51,10 +60,10 @@ // po prostu nieprawdziwa — pokazywałaby inne miejsce niż współrzędne obok. // Dotyczy też sytuacji, gdy /reverse nie odpowie (brak sieci): lepiej puste // pole i poprawne współrzędne niż nazwa, która kłamie. - if (searchEl) searchEl.value = ''; + if (searchEl) setVal(searchEl, ''); fetch('/reverse?lat=' + lat + '&lon=' + lon) .then(function (r) { return r.ok ? r.json() : null; }) - .then(function (d) { if (d && d.name && searchEl) searchEl.value = d.name; }) + .then(function (d) { if (d && d.name && searchEl) setVal(searchEl, d.name); }) .catch(function () {}); } @@ -73,7 +82,7 @@ li.textContent = it.name; li.addEventListener('click', function () { setCoords(it.lat, it.lon, false); - searchEl.value = it.name; + setVal(searchEl, it.name); resultsEl.hidden = true; }); resultsEl.appendChild(li); diff --git a/services/presentation/app/templates/base.html b/services/presentation/app/templates/base.html index 8c4dde7..fce2746 100644 --- a/services/presentation/app/templates/base.html +++ b/services/presentation/app/templates/base.html @@ -5,6 +5,11 @@ astrololo · {% block title %}{% endblock %} + {# Wspólne dane formularza między zakładkami (PRE-21). W z `defer` + CELOWO: skrypty defer wykonują się w kolejności dokumentu, więc ten zdąży + odtworzyć współrzędne, ZANIM geo.js zbuduje mapę — mapa startuje od razu + we właściwym miejscu, bez przestawiania. #} +
diff --git a/services/presentation/app/templates/chart.html b/services/presentation/app/templates/chart.html index 5628b01..3e522e6 100644 --- a/services/presentation/app/templates/chart.html +++ b/services/presentation/app/templates/chart.html @@ -6,6 +6,12 @@

Wpisz dane momentu i miejsca — program policzy pozycje obiektów, osie i domy (silnik efemeryd warstwy logicznej).

+
+ +