Neues Modul js/import/ mit reiner Validierungslogik (importValidator.js: parseAndValidate(), findDuplicate()). Jeder Ausflugsziel-Eintrag im importierten KI-JSON wird einzeln bewertet statt alles-oder-nichts: fehlender Name/Kategorie oder ungueltige Koordinaten fuehren zur Ablehnung, weiche Probleme (unbekannte Kategorie, unsichere website/image-URL, ungueltige Bewertung, falscher Feldtyp) erzeugen nur eine Warnung. sanitizeEntry() setzt sicherheitskritische Felder vor der Uebernahme zurueck (z.B. javascript:-URLs), da normalizePlace() selbst keine Sicherheitspruefung vornimmt. findDuplicate() erkennt wahrscheinliche Duplikate gegen den bestehenden Datenbestand. UI (js/ui/import.js): Modal mit zwei Tabs - KI-Ausflugsziele importieren (Text einfuegen oder Datei hochladen, Vorschau mit gueltig/Warnung/abgelehnt, Duplikat-Toggle, Bestaetigen) und Backup sichern/wiederherstellen (js/import/backup.js: Export als JSON-Datei, Wiederherstellen mit Ersetzen/Ergaenzen-Wahl). Vorschau-Rendering ausschliesslich ueber el()/textContent, da es sich um echte, ungepruefte Fremddaten handelt. Damit sind alle Kernfunktionen aus anforderung.md umgesetzt. docs/plan.md entsprechend fortgeschrieben.
130 lines
3.7 KiB
JavaScript
130 lines
3.7 KiB
JavaScript
// Einstiegspunkt der Anwendung: initialisiert Store, Karte und Liste.
|
|
|
|
(function () {
|
|
const { initStore } = EventMap.store;
|
|
const { initMap, centerOnCoords, requestUserLocation } = EventMap.map;
|
|
const {
|
|
initList,
|
|
initDetail,
|
|
initForm,
|
|
initManualLocationForm,
|
|
initFilterPanel,
|
|
initPromptGenerator,
|
|
initImport,
|
|
} = EventMap.ui;
|
|
|
|
function showFatalError(message) {
|
|
const status = document.getElementById('app-status');
|
|
const statusText = document.getElementById('app-status-text');
|
|
if (!status || !statusText) return;
|
|
statusText.textContent = message;
|
|
status.hidden = false;
|
|
}
|
|
|
|
function hideStatus() {
|
|
const status = document.getElementById('app-status');
|
|
const statusText = document.getElementById('app-status-text');
|
|
if (!status || !statusText) return;
|
|
status.hidden = true;
|
|
statusText.textContent = '';
|
|
}
|
|
|
|
// Verdrahtet den Schließen-Button der Statusmeldung unabhängig vom
|
|
// restlichen Bootstrap-Ablauf, damit er auch dann funktioniert, wenn
|
|
// bootstrap() selbst fehlschlägt und showFatalError() aus dem
|
|
// catch-Handler heraus aufgerufen wird (siehe Gitea Issue #1: die
|
|
// Fehlermeldung war bisher dauerhaft nicht schließbar).
|
|
function setupStatusClose() {
|
|
const closeBtn = document.getElementById('app-status-close');
|
|
if (!closeBtn) return;
|
|
closeBtn.addEventListener('click', hideStatus);
|
|
}
|
|
|
|
function setupGeolocationButton() {
|
|
const button = document.getElementById('locate-btn');
|
|
if (!button) return;
|
|
|
|
const defaultLabel = button.textContent;
|
|
|
|
button.addEventListener('click', () => {
|
|
button.disabled = true;
|
|
button.textContent = 'Standort wird ermittelt…';
|
|
|
|
requestUserLocation({
|
|
onSuccess: ({ lat, lng }) => {
|
|
centerOnCoords(lat, lng, 13);
|
|
button.disabled = false;
|
|
button.textContent = defaultLabel;
|
|
},
|
|
onError: () => {
|
|
button.disabled = false;
|
|
button.textContent = defaultLabel;
|
|
showFatalError(
|
|
'Standort konnte nicht ermittelt werden. Bitte Standortfreigabe im Browser prüfen.'
|
|
);
|
|
},
|
|
});
|
|
});
|
|
}
|
|
|
|
function setupManualLocationButton() {
|
|
const button = document.getElementById('manual-location-btn');
|
|
if (!button) return;
|
|
|
|
button.addEventListener('click', () => {
|
|
EventMap.ui.openManualLocationForm();
|
|
});
|
|
}
|
|
|
|
function setupAddPlaceButton() {
|
|
const button = document.getElementById('add-place-btn');
|
|
if (!button) return;
|
|
|
|
button.addEventListener('click', () => {
|
|
EventMap.ui.openPlaceForm();
|
|
});
|
|
}
|
|
|
|
function setupPromptGeneratorButton() {
|
|
const button = document.getElementById('open-prompt-generator-btn');
|
|
if (!button) return;
|
|
|
|
button.addEventListener('click', () => {
|
|
EventMap.ui.openPromptGenerator();
|
|
});
|
|
}
|
|
|
|
function setupImportButton() {
|
|
const button = document.getElementById('open-import-btn');
|
|
if (!button) return;
|
|
|
|
button.addEventListener('click', () => {
|
|
EventMap.ui.openImport();
|
|
});
|
|
}
|
|
|
|
async function bootstrap() {
|
|
await initStore();
|
|
initMap('map');
|
|
initFilterPanel('filter-panel');
|
|
initList('place-list');
|
|
initDetail('detail-modal');
|
|
initForm('place-form-modal');
|
|
initManualLocationForm('manual-location-modal');
|
|
initPromptGenerator('prompt-generator-modal');
|
|
initImport('import-modal');
|
|
setupGeolocationButton();
|
|
setupManualLocationButton();
|
|
setupAddPlaceButton();
|
|
setupPromptGeneratorButton();
|
|
setupImportButton();
|
|
}
|
|
|
|
setupStatusClose();
|
|
|
|
bootstrap().catch((err) => {
|
|
console.error('EventMap: Initialisierung fehlgeschlagen.', err);
|
|
showFatalError('Die Anwendung konnte nicht geladen werden. Bitte Seite neu laden.');
|
|
});
|
|
})();
|