From 6e2ed796c0739bafe1b0fc39879cd7cd3c62cfdf Mon Sep 17 00:00:00 2001 From: Anaru Date: Tue, 25 Nov 2025 12:06:20 +0100 Subject: [PATCH] feat: Introduce main application UI with family tree, map, and timeline views, alongside updated authentication and documentation. --- README.md | 27 ++++++++++- Script.js | 101 +++++++++++++++++++++++++++++---------- Røsstad.html => app.html | 33 ++++++++++++- login.js | 2 +- style.css | 16 ++++++- 5 files changed, 149 insertions(+), 30 deletions(-) rename Røsstad.html => app.html (77%) diff --git a/README.md b/README.md index 8c0ddb3..2ce28f5 100644 --- a/README.md +++ b/README.md @@ -1 +1,26 @@ -https://funnyeivske.github.io/Roesstad/ +# Røsstad Familietre + +**Nettsiden finner du her:** [https://funnyeivske.github.io/Roesstad/](https://funnyeivske.github.io/Roesstad/) + +## Om Nettsiden +Dette er et digitalt samlingspunkt for Røsstad-familietreet. Nettsiden er designet for å bevare og visualisere familiens historie på en moderne og interaktiv måte. + +## Hvordan den fungerer + +### 1. Familietre (Hovedvisning) +Her ser du selve slektstreet. +- **Navigering:** Du kan dra i bakgrunnen for å flytte deg rundt, og zoome ut/inn. +- **Personkort:** Klikk på en person for å sette dem i fokus. +- **Legg til:** Bruk den store grønne knappen nede til høyre for å legge til nye familiemedlemmer. + +### 2. Kart +Se hvor familien har sine røtter. +- Når du legger til en person, kan du skrive inn et sted (f.eks. "Grimstad"). +- Personen vil da dukke opp som en markør på kartet. + +### 3. Tidslinje +En kronologisk oversikt over familiens historie. +- Her listes alle fødsler og dødsfall opp automatisk basert på informasjonen i treet. + +--- +*Laget med ❤️ for Røsstad-familien.* diff --git a/Script.js b/Script.js index 3fee2ed..59dca8b 100644 --- a/Script.js +++ b/Script.js @@ -21,13 +21,26 @@ const state = { map: null }; +// --- IMMEDIATE SETUP (UI Handlers) --- +// This ensures buttons work even if Auth is slow +setupTabs(); +setupZoom(); +setupModal(); +setupHelp(); +setupFAB(); + // --- AUTHENTICATION --- onAuthStateChanged(auth, (user) => { if (user) { state.user = user; - initApp(); + startDataListener(); } else { - window.location.href = "index.html"; + // Redirect to login if not authenticated + // Note: In local development with file://, this might trigger unexpectedly. + // For now, we assume index.html is the login page. + if (!window.location.pathname.endsWith('index.html') && !window.location.pathname.endsWith('/')) { + window.location.href = "index.html"; + } } }); @@ -35,9 +48,8 @@ document.getElementById('btn-logout')?.addEventListener('click', () => { signOut(auth).then(() => window.location.href = "index.html"); }); -// --- APP LOGIC --- -function initApp() { - // Start Data Listener +// --- DATA LISTENER --- +function startDataListener() { const q = query(collection(db, 'familyMembers')); onSnapshot(q, (snapshot) => { state.people = snapshot.docs.map(d => ({ id: d.id, ...d.data() })); @@ -50,11 +62,6 @@ function initApp() { renderAll(); }); - - // Setup Navigation - setupTabs(); - setupZoom(); - setupModal(); } function renderAll() { @@ -74,7 +81,8 @@ function setupTabs() { // Show View document.querySelectorAll('.view-section').forEach(v => v.classList.add('hidden')); const viewId = `view-${btn.dataset.view}`; - document.getElementById(viewId).classList.remove('hidden'); + const viewEl = document.getElementById(viewId); + if (viewEl) viewEl.classList.remove('hidden'); // Special handling for Map resize if (btn.dataset.view === 'map' && state.map) { @@ -97,7 +105,10 @@ function renderTree() { childrenRow.innerHTML = ''; if (state.people.length === 0) { - focusRow.innerHTML = ``; + focusRow.innerHTML = `
+

Ingen personer i treet ennå.

+

Klikk på nede til høyre for å starte!

+
`; return; } @@ -203,6 +214,9 @@ function createSlot(person, label, action) { // --- MAP RENDERER --- function renderMap() { if (!state.map) { + const mapContainer = document.getElementById('map-container'); + if (!mapContainer) return; + state.map = L.map('map-container').setView([65.0, 13.0], 5); L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { attribution: '© OpenStreetMap © CARTO', @@ -274,18 +288,21 @@ function renderTimeline() { function setupModal() { window.openModal = (action) => { state.modal.action = action; - document.getElementById('modal-overlay').classList.remove('hidden'); + const overlay = document.getElementById('modal-overlay'); + if (overlay) overlay.classList.remove('hidden'); // Reset Form document.querySelectorAll('.input-field').forEach(i => i.value = ''); // Set Title const title = document.getElementById('modal-title'); - if (action === 'add-parent-m') title.textContent = "Legg til Far"; - else if (action === 'add-parent-f') title.textContent = "Legg til Mor"; - else if (action === 'add-child') title.textContent = "Legg til Barn"; - else if (action === 'add-partner') title.textContent = "Legg til Partner"; - else title.textContent = "Start Tre"; + if (title) { + if (action === 'add-parent-m') title.textContent = "Legg til Far"; + else if (action === 'add-parent-f') title.textContent = "Legg til Mor"; + else if (action === 'add-child') title.textContent = "Legg til Barn"; + else if (action === 'add-partner') title.textContent = "Legg til Partner"; + else title.textContent = "Legg til Person"; + } // Default Mode window.setModalMode('new'); @@ -300,11 +317,13 @@ function setupModal() { document.getElementById('form-existing').classList.toggle('hidden', mode !== 'existing'); }; - document.getElementById('btn-close-modal').onclick = () => { + const closeBtn = document.getElementById('btn-close-modal'); + if (closeBtn) closeBtn.onclick = () => { document.getElementById('modal-overlay').classList.add('hidden'); }; - document.getElementById('btn-save').onclick = async () => { + const saveBtn = document.getElementById('btn-save'); + if (saveBtn) saveBtn.onclick = async () => { const focusP = state.people.find(p => p.id === state.focusId); let targetId = null; @@ -335,14 +354,18 @@ function setupModal() { const docRef = await addDoc(collection(db, 'familyMembers'), newPerson); targetId = docRef.id; - if (state.modal.action === 'create-first') state.focusId = targetId; + + // If creating first person or using generic add + if (state.modal.action === 'create-first' || !state.focusId) { + state.focusId = targetId; + } } else { targetId = document.getElementById('selected-existing-id').value; } // Link Relations - if (focusP && targetId) { + if (focusP && targetId && state.modal.action !== 'create-first' && state.modal.action !== 'add-generic') { const col = collection(db, 'familyMembers'); const updates = []; @@ -365,6 +388,7 @@ function setupModal() { function renderExistingList() { const list = document.getElementById('existing-list'); + if (!list) return; list.innerHTML = ''; state.people.filter(p => p.id !== state.focusId).forEach(p => { const div = document.createElement('div'); @@ -385,17 +409,42 @@ function setupZoom() { const grid = document.getElementById('tree-grid'); const display = document.getElementById('zoom-display'); - document.getElementById('btn-zoom-in').onclick = () => { + const btnIn = document.getElementById('btn-zoom-in'); + if (btnIn) btnIn.onclick = () => { state.scale += 0.1; updateZoom(); }; - document.getElementById('btn-zoom-out').onclick = () => { + + const btnOut = document.getElementById('btn-zoom-out'); + if (btnOut) btnOut.onclick = () => { if (state.scale > 0.4) state.scale -= 0.1; updateZoom(); }; function updateZoom() { - grid.style.transform = `scale(${state.scale})`; - display.textContent = Math.round(state.scale * 100) + '%'; + if (grid) grid.style.transform = `scale(${state.scale})`; + if (display) display.textContent = Math.round(state.scale * 100) + '%'; + } +} + +// --- HELP --- +function setupHelp() { + const btn = document.getElementById('btn-help'); + const modal = document.getElementById('modal-help'); + const close = document.getElementById('btn-close-help'); + + if (btn && modal) { + btn.onclick = () => modal.classList.remove('hidden'); + } + if (close && modal) { + close.onclick = () => modal.classList.add('hidden'); + } +} + +// --- FAB --- +function setupFAB() { + const fab = document.getElementById('fab-add'); + if (fab) { + fab.onclick = () => window.openModal('add-generic'); } } diff --git a/Røsstad.html b/app.html similarity index 77% rename from Røsstad.html rename to app.html index 512e71b..6808192 100644 --- a/Røsstad.html +++ b/app.html @@ -36,6 +36,9 @@
+ +
+ 100% @@ -73,10 +76,15 @@
+ + + - +