Files
eventmap/js/store/store.js
T
dapelza dac952fbf9 Phase 4: Filter- und Suchfunktion
Neues Modul js/filters/ mit reiner Filterlogik (filterEngine.js:
applyFilters(), getDistanceKm()) und UI (filterPanel.js) fuer alle Filter
aus anforderung.md Abschnitt 3: Kategorie, Entfernung (mit "Standort
verwenden"-Button), Alter der Kinder, Kosten, Indoor/Outdoor, 5
Ausstattungsmerkmale, Aufenthaltsdauer, Mindestbewertung sowie
Freitextsuche ueber Name/Ort/Region/Adresse. Trefferzaehler, "Filter
zuruecksetzen" und "Auf Ergebnisse zoomen" ergaenzt.

Store erweitert um updateFilters()/getFilteredPlaces() (Selector).
Karte und Liste rendern jetzt die gefilterte statt die volle
Ausflugsziel-Liste; die Karten-Rerender-Optimierung vergleicht dafuer
die Trefferliste per Id-Schluessel statt nur state.places, damit reine
Filter-/Standortaenderungen ohne Places-Aenderung erkannt werden.

Layout: Filter-Panel als aufklappbares <details> auf Mobile, eigener
Grid-Bereich neben der Karte auf Desktop. docs/plan.md entsprechend
fortgeschrieben (Phase 2-5 als umgesetzt markiert, Phase 5
Navigation war bereits Teil von Phase 2).
2026-07-24 12:21:07 +02:00

111 lines
3.3 KiB
JavaScript

// Zentraler Pub/Sub-Store der Anwendung. Hält den Laufzeitzustand
// (places, filters, userLocation, selectedPlaceId) und benachrichtigt
// Subscriber bei Änderungen. Lädt beim Start aus LocalStorage bzw. beim
// allerersten Start aus data/seed.js und persistiert Änderungen zurück.
(function () {
const { loadData, saveData, loadSettings, saveSettings } = EventMap.store;
const { normalizePlace } = EventMap.models;
let state = {
places: [],
filters: null,
userLocation: null,
selectedPlaceId: null,
};
let settings = loadSettings();
const subscribers = new Set();
function notify() {
for (const fn of subscribers) {
fn(state);
}
}
/** Registriert einen Listener, der bei jeder Zustandsänderung aufgerufen wird. */
function subscribe(fn) {
subscribers.add(fn);
return () => subscribers.delete(fn);
}
function getState() {
return state;
}
function setState(patch) {
state = { ...state, ...patch };
notify();
}
function getSettings() {
return settings;
}
function updateSettings(patch) {
settings = { ...settings, ...patch };
saveSettings(settings);
}
function loadSeedPlaces() {
const raw = (EventMap.data && EventMap.data.SEED_PLACES) || [];
return raw.map((item) => normalizePlace(item));
}
/**
* Initialisiert den Store: lädt persistierte Places aus LocalStorage oder,
* falls noch keine vorhanden sind, die eingebetteten Beispieldaten
* (data/seed.js). Gibt den finalen State zurück.
*/
function initStore() {
const persisted = loadData();
let places = [];
if (persisted && Array.isArray(persisted.places) && persisted.places.length > 0) {
places = persisted.places.map((p) => normalizePlace(p));
} else {
places = loadSeedPlaces();
saveData(places);
}
setState({ places });
return state;
}
/** Persistiert state.places nach Änderungen (Erstellen/Bearbeiten/Löschen). */
function persistPlaces() {
saveData(state.places);
}
/**
* Merged patch in state.filters (initialisiert mit EventMap.filters.DEFAULT_FILTERS
* falls state.filters noch null ist) und setzt eine neue Objekt-Referenz.
*
* WICHTIG: EventMap.filters wird hier bewusst erst zur Aufrufzeit
* referenziert (nicht beim IIFE-Start destrukturiert), analog zum Muster
* zwischen js/ui/detail.js und js/ui/form.js - so ist store.js unabhängig
* von der genauen Ladereihenfolge relativ zu js/filters/filterEngine.js.
*/
function updateFilters(patch) {
const current = state.filters || EventMap.filters.DEFAULT_FILTERS;
setState({ filters: { ...current, ...patch } });
}
/** Selector: liefert state.places gefiltert nach state.filters/state.userLocation. */
function getFilteredPlaces() {
const filters = state.filters || EventMap.filters.DEFAULT_FILTERS;
return EventMap.filters.applyFilters(state.places, filters, state.userLocation);
}
EventMap.store.subscribe = subscribe;
EventMap.store.getState = getState;
EventMap.store.setState = setState;
EventMap.store.getSettings = getSettings;
EventMap.store.updateSettings = updateSettings;
EventMap.store.initStore = initStore;
EventMap.store.persistPlaces = persistPlaces;
EventMap.store.updateFilters = updateFilters;
EventMap.store.getFilteredPlaces = getFilteredPlaces;
})();