Phase 0+1: Scaffold, Datenmodell, Karte und Liste
Reines HTML5/CSS3/JavaScript ohne Build-Tooling, lauffaehig direkt per Doppelklick auf index.html (file://, kein Server noetig): - Datenmodell fuer Ausflugsziele mit allen Feldern aus der Anforderung, 15 feste Kategorien inkl. Farbe/Icon - Zentraler Pub/Sub-Store mit LocalStorage-Persistenz und Migrations-Grundgeruest - Interaktive Karte (Leaflet + Leaflet.markercluster, lokal vendored) mit kategoriefarbenen Markern, Clustering, Popups, Geolocation-Button - Kartengrundlage ueber CARTO-Basemaps statt tile.openstreetmap.org, da dessen Referer-Pflicht file://-Aufrufe blockiert - Seed-Daten als eingebettetes Script (data/seed.js) statt JSON-Datei, da fetch() auf lokale Dateien unter file:// nicht zuverlaessig funktioniert - Ergebnisliste mit 10 Beispiel-Ausflugszielen, sicher gerendert ueber textContent/DOM-APIs statt innerHTML (Vorbereitung fuer spaeteren KI-JSON-Import mit Fremddaten) - Responsives Layout (Desktop nebeneinander, Mobile gestapelt)
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
})();
|
||||
Reference in New Issue
Block a user