style: replace old theme with a modernized design system using CSS variables, new layout components, and refined typography.
This commit is contained in:
parent
6be8f41448
commit
200b51494b
3 changed files with 2580 additions and 1133 deletions
397
index.html
397
index.html
|
|
@ -1,186 +1,313 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="no">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Røsstad Familietre</title>
|
<title>Family Tree | Slektstre</title>
|
||||||
<!-- Fonts -->
|
<meta name="description" content="Visual auto-sorting family tree logger with birth and death dates, generational hierarchy, and interactive canvas.">
|
||||||
|
<!-- Google Fonts -->
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
<!-- Styles -->
|
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<!-- Google Maps API -->
|
|
||||||
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBdC0kSFC6auWxO5bttoclLv48i3F44OPY&callback=initMap"
|
|
||||||
async defer></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="dark-mode">
|
<body class="dark-theme">
|
||||||
|
<!-- Main Application Header -->
|
||||||
|
<header class="app-header">
|
||||||
|
<div class="header-left">
|
||||||
|
<div class="brand">
|
||||||
|
<div class="brand-icon">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 class="brand-title">Røsstad Tree</h1>
|
||||||
|
<span class="brand-subtitle" id="statsSubtitle">Loading family records...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Navigation -->
|
<!-- View Mode Switcher -->
|
||||||
<nav class="top-nav glass">
|
<div class="view-toggle-group">
|
||||||
<div class="nav-brand">Røsstad Slekt</div>
|
<button id="viewTreeBtn" class="toggle-btn active" title="Visual Tree View">
|
||||||
<div class="nav-tabs">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<button class="nav-btn active" data-tab="tree">Familietre</button>
|
<rect x="3" y="3" width="7" height="7"></rect>
|
||||||
<button class="nav-btn" data-tab="map">Kart</button>
|
<rect x="14" y="3" width="7" height="7"></rect>
|
||||||
<button class="nav-btn" data-tab="history">Historie</button>
|
<rect x="14" y="14" width="7" height="7"></rect>
|
||||||
|
<rect x="3" y="14" width="7" height="7"></rect>
|
||||||
|
</svg>
|
||||||
|
<span>Tree Canvas</span>
|
||||||
|
</button>
|
||||||
|
<button id="viewListBtn" class="toggle-btn" title="Registry Table View">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<line x1="8" y1="6" x2="21" y2="6"></line>
|
||||||
|
<line x1="8" y1="12" x2="21" y2="12"></line>
|
||||||
|
<line x1="8" y1="18" x2="21" y2="18"></line>
|
||||||
|
<line x1="3" y1="6" x2="3.01" y2="6"></line>
|
||||||
|
<line x1="3" y1="12" x2="3.01" y2="12"></line>
|
||||||
|
<line x1="3" y1="18" x2="3.01" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
<span>Registry List</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-controls">
|
|
||||||
<button id="darkModeToggle" class="icon-btn" title="Bytt lys/mørk modus">🌙</button>
|
|
||||||
<button id="logoutBtn" class="text-btn" style="display: none;">Logg ut</button>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Search and Global Actions -->
|
||||||
<main id="main-content">
|
<div class="header-right">
|
||||||
<!-- Tab 1: Family Tree -->
|
<div class="search-box">
|
||||||
<section id="tree-tab" class="tab-content active">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="search-icon">
|
||||||
<div id="tree-container">
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
<svg id="relationship-lines"></svg>
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||||
<div id="family-cards-container">
|
</svg>
|
||||||
<!-- Cards will be injected here -->
|
<input type="text" id="searchInput" placeholder="Search person by name or year..." autocomplete="off">
|
||||||
|
<button id="clearSearchBtn" class="clear-search-btn" title="Clear search">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="addNewPersonBtn" class="btn btn-primary" title="Add a new family member">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||||
|
</svg>
|
||||||
|
<span>Add Person</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Data & Settings Menu Button -->
|
||||||
|
<div class="dropdown">
|
||||||
|
<button id="dataMenuBtn" class="btn btn-secondary icon-only" title="Data options">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="1"></circle>
|
||||||
|
<circle cx="12" cy="5" r="1"></circle>
|
||||||
|
<circle cx="12" cy="19" r="1"></circle>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div id="dataDropdownMenu" class="dropdown-menu">
|
||||||
|
<button id="exportJsonBtn" class="dropdown-item">
|
||||||
|
<span>📥 Export JSON Backup</span>
|
||||||
|
</button>
|
||||||
|
<button id="importJsonBtn" class="dropdown-item">
|
||||||
|
<span>📤 Import JSON File</span>
|
||||||
|
</button>
|
||||||
|
<input type="file" id="importFileInput" accept=".json" style="display: none;">
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<button id="loadSampleDataBtn" class="dropdown-item">
|
||||||
|
<span>🔄 Load Sample Family</span>
|
||||||
|
</button>
|
||||||
|
<button id="clearAllDataBtn" class="dropdown-item danger">
|
||||||
|
<span>🗑️ Clear All Records</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Theme Toggle -->
|
||||||
|
<button id="themeToggleBtn" class="btn btn-secondary icon-only" title="Toggle Dark/Light Mode">
|
||||||
|
<span id="themeIcon">🌙</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Main Workspace -->
|
||||||
|
<main class="main-viewport">
|
||||||
|
<!-- Visual Tree View -->
|
||||||
|
<section id="treeViewSection" class="view-panel active">
|
||||||
|
<!-- Canvas Controls Overlay -->
|
||||||
|
<div class="canvas-controls">
|
||||||
|
<button id="zoomInBtn" class="control-btn" title="Zoom In">+</button>
|
||||||
|
<button id="zoomOutBtn" class="control-btn" title="Zoom Out">−</button>
|
||||||
|
<button id="zoomResetBtn" class="control-btn" title="Reset Zoom / Fit to Screen">Fit</button>
|
||||||
|
<div class="control-divider"></div>
|
||||||
|
<button id="autoSortBtn" class="control-btn auto-sort-btn" title="Re-sort and organize tree">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67"/>
|
||||||
|
</svg>
|
||||||
|
<span>Auto-Sort</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Canvas Helper Hint -->
|
||||||
|
<div class="canvas-hint">
|
||||||
|
<span>💡 Click & drag canvas to pan • Scroll to zoom • Click cards to inspect</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Zoomable / Pannable Tree Container -->
|
||||||
|
<div id="treeViewport" class="tree-viewport">
|
||||||
|
<div id="treeCanvas" class="tree-canvas">
|
||||||
|
<!-- SVG Connection Lines -->
|
||||||
|
<svg id="treeSvg" class="tree-svg"></svg>
|
||||||
|
|
||||||
|
<!-- Interactive Tree Nodes Layer -->
|
||||||
|
<div id="treeNodesLayer" class="tree-nodes-layer">
|
||||||
|
<!-- Generational tiers and cards rendered by script.js -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button id="addMemberBtn" class="fab" title="Legg til person">+</button>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Tab 2: Map -->
|
<!-- Registry List View -->
|
||||||
<section id="map-tab" class="tab-content">
|
<section id="listViewSection" class="view-panel">
|
||||||
<div id="map"></div>
|
<div class="list-container">
|
||||||
</section>
|
<div class="list-header-row">
|
||||||
|
<div>
|
||||||
|
<h2>Family Members Registry</h2>
|
||||||
|
<p class="text-muted">Chronological list of all logged family members with birth and death dates.</p>
|
||||||
|
</div>
|
||||||
|
<div class="list-actions">
|
||||||
|
<button id="listAddPersonBtn" class="btn btn-primary">
|
||||||
|
<span>+ Add Member</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Tab 3: History -->
|
<div class="table-card">
|
||||||
<section id="history-tab" class="tab-content">
|
<div class="table-responsive">
|
||||||
<div class="history-container">
|
<table class="registry-table" id="registryTable">
|
||||||
<h2>Slektshistorie</h2>
|
<thead>
|
||||||
<ul id="history-list">
|
<tr>
|
||||||
<!-- History items will be injected here -->
|
<th data-sort="name">Name <span class="sort-indicator">↕</span></th>
|
||||||
</ul>
|
<th data-sort="gender">Gender <span class="sort-indicator">↕</span></th>
|
||||||
|
<th data-sort="birthDate">Birth Date <span class="sort-indicator">↕</span></th>
|
||||||
|
<th data-sort="deathDate">Death Date <span class="sort-indicator">↕</span></th>
|
||||||
|
<th data-sort="lifespan">Lifespan / Age <span class="sort-indicator">↕</span></th>
|
||||||
|
<th>Parents</th>
|
||||||
|
<th>Spouse</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="registryTableBody">
|
||||||
|
<!-- Populated dynamically -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Login Modal -->
|
<!-- Modal: Add / Edit Family Member -->
|
||||||
<div id="login-modal" class="modal">
|
<div id="personModal" class="modal-backdrop" style="display: none;">
|
||||||
<div class="modal-content glass-panel login-box">
|
<div class="modal-dialog">
|
||||||
<h2>Velkommen til Røsstad</h2>
|
<div class="modal-content">
|
||||||
<p>Vennligst logg inn for å se slektstreet.</p>
|
<div class="modal-header">
|
||||||
<form id="login-form">
|
<h2 id="personModalTitle">Add Family Member</h2>
|
||||||
<input type="email" id="email" placeholder="E-post" required>
|
<button type="button" class="close-btn" id="closePersonModalBtn" aria-label="Close modal">×</button>
|
||||||
<input type="password" id="password" placeholder="Passord" required>
|
|
||||||
<button type="submit" class="primary-btn">Logg inn</button>
|
|
||||||
</form>
|
|
||||||
<p id="login-error" class="error-msg"></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Add/Edit Member Modal -->
|
<form id="personForm" class="person-form">
|
||||||
<div id="member-modal" class="modal" style="display: none;">
|
<input type="hidden" id="personId">
|
||||||
<div class="modal-content glass-panel">
|
|
||||||
<span class="close-modal">×</span>
|
|
||||||
<h2 id="modal-title">Legg til person</h2>
|
|
||||||
<form id="member-form">
|
|
||||||
<input type="hidden" id="member-id">
|
|
||||||
|
|
||||||
|
<!-- Name fields -->
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group flex-1">
|
||||||
<label>Fornavn</label>
|
<label for="firstNameInput">First Name <span class="req">*</span></label>
|
||||||
<input type="text" id="firstName" required>
|
<input type="text" id="firstNameInput" placeholder="e.g. Henrik" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group flex-1">
|
||||||
<label>Etternavn</label>
|
<label for="lastNameInput">Last Name <span class="req">*</span></label>
|
||||||
<input type="text" id="lastName" required>
|
<input type="text" id="lastNameInput" placeholder="e.g. Røsstad" required>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Kjønn</label>
|
|
||||||
<div class="radio-group">
|
|
||||||
<label><input type="radio" name="gender" value="male"> Mann</label>
|
|
||||||
<label><input type="radio" name="gender" value="female"> Kvinne</label>
|
|
||||||
<label><input type="radio" name="gender" value="other"> Annet</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Gender & Living status -->
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group flex-1">
|
||||||
<label>Fødselsdato</label>
|
<label for="genderSelect">Gender</label>
|
||||||
<input type="date" id="birthDate">
|
<select id="genderSelect">
|
||||||
</div>
|
<option value="male">Male</option>
|
||||||
<div class="form-group">
|
<option value="female">Female</option>
|
||||||
<label>Dødsdato</label>
|
<option value="other">Other / Unspecified</option>
|
||||||
<input type="date" id="deathDate">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Sted</label>
|
|
||||||
<div style="display: flex; gap: 10px;">
|
|
||||||
<input type="text" id="location" placeholder="F.eks. Kristiansand">
|
|
||||||
<button type="button" id="geocodeBtn" class="icon-btn" title="Finn koordinater">📍</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Breddegrad (Lat)</label>
|
|
||||||
<input type="number" id="lat" step="any" placeholder="58.16">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Lengdegrad (Lng)</label>
|
|
||||||
<input type="number" id="lng" step="any" placeholder="8.25">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Relationships -->
|
|
||||||
<div class="form-section">
|
|
||||||
<h3>Relasjoner</h3>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Mor</label>
|
|
||||||
<select id="motherId">
|
|
||||||
<option value="">Ingen valgt</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label>Far</label>
|
<div class="form-group flex-1 checkbox-group-container">
|
||||||
<select id="fatherId">
|
<label>Status</label>
|
||||||
<option value="">Ingen valgt</option>
|
<label class="custom-checkbox">
|
||||||
|
<input type="checkbox" id="isLivingCheckbox" checked>
|
||||||
|
<span class="checkbox-label">Person is still living</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dates Section -->
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group flex-1">
|
||||||
|
<label for="birthDateInput">Birth Date <span class="req">*</span></label>
|
||||||
|
<input type="date" id="birthDateInput" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group flex-1" id="deathDateFieldGroup" style="display: none;">
|
||||||
|
<label for="deathDateInput">Death Date</label>
|
||||||
|
<input type="date" id="deathDateInput">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-divider">Relationships</div>
|
||||||
|
|
||||||
|
<!-- Parents Selection -->
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group flex-1">
|
||||||
|
<label for="parent1Select">Parent 1</label>
|
||||||
|
<select id="parent1Select">
|
||||||
|
<option value="">-- None / Unknown --</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group flex-1">
|
||||||
<label>Ektefelle/Samboer</label>
|
<label for="parent2Select">Parent 2</label>
|
||||||
<select id="spouseId">
|
<select id="parent2Select">
|
||||||
<option value="">Ingen valgt</option>
|
<option value="">-- None / Unknown --</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Spouse Selection -->
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group flex-1">
|
||||||
|
<label for="spouseSelect">Spouse / Partner</label>
|
||||||
|
<select id="spouseSelect">
|
||||||
|
<option value="">-- None / Unmarried --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Notes / Biography -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Notater</label>
|
<label for="notesInput">Notes / Biography (Optional)</label>
|
||||||
<textarea id="notes" rows="3"></textarea>
|
<textarea id="notesInput" rows="2" placeholder="Birthplace, occupation, notable life events..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-section">
|
<div class="modal-footer">
|
||||||
<h3>Barn</h3>
|
<button type="button" class="btn btn-secondary" id="cancelPersonBtn">Cancel</button>
|
||||||
<ul id="children-list" style="list-style: none; padding: 0; margin-bottom: 10px;">
|
<button type="submit" class="btn btn-primary" id="savePersonBtn">Save Person</button>
|
||||||
<!-- Populated by JS -->
|
|
||||||
</ul>
|
|
||||||
<button type="button" id="addChildBtn" class="text-btn"
|
|
||||||
style="color: var(--color-forest); border-color: var(--color-forest); font-size: 0.9rem;">+
|
|
||||||
Legg
|
|
||||||
til barn</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="primary-btn">Lagre</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Firebase SDKs -->
|
<!-- Modal: View Details & Quick Actions -->
|
||||||
<script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-app-compat.js"></script>
|
<div id="detailsModal" class="modal-backdrop" style="display: none;">
|
||||||
<script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-auth-compat.js"></script>
|
<div class="modal-dialog modal-dialog-sm">
|
||||||
<script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore-compat.js"></script>
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 id="detailsModalTitle">Person Details</h3>
|
||||||
|
<button type="button" class="close-btn" id="closeDetailsModalBtn">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="detailsModalBody">
|
||||||
|
<!-- Dynamic Details -->
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer justify-between">
|
||||||
|
<button type="button" class="btn btn-danger-outline" id="detailsDeleteBtn">Delete</button>
|
||||||
|
<div class="footer-actions">
|
||||||
|
<button type="button" class="btn btn-secondary" id="detailsAddChildBtn">+ Add Child</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="detailsEditBtn">Edit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Toast Notification -->
|
||||||
|
<div id="toastNotification" class="toast-notification"></div>
|
||||||
|
|
||||||
<script src="script.js"></script>
|
<script src="script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue