Implementing API - WIP frontend

This commit is contained in:
2026-07-26 11:37:03 +02:00
parent e989594b96
commit 8b1d3288c0
12 changed files with 2222 additions and 519 deletions
+230 -5
View File
@@ -8,11 +8,102 @@
body {
margin: 0;
border: 0;
display: flex;
flex-direction: column;
height: 100vh;
font-family:
system-ui,
-apple-system,
sans-serif;
}
.container {
display: grid;
grid-template-columns: 2fr 1fr;
grid-template-rows: 1fr;
height: 100%;
gap: 8px;
padding: 8px;
box-sizing: border-box;
}
#map-container {
grid-column: 1 / 2;
grid-row: 1 / 2;
display: flex;
flex-direction: column;
gap: 8px;
}
#map {
width: 600px;
height: 600px;
margin: auto;
flex: 1;
min-height: 0;
border: 1px solid #ccc;
border-radius: 4px;
}
#file-content {
flex: 1;
min-height: 0;
border: 1px solid #ccc;
border-radius: 4px;
padding: 16px;
overflow-y: auto;
background: #fafafa;
}
#plan-container {
grid-column: 2 / 3;
grid-row: 1 / 2;
border: 1px solid #ccc;
border-radius: 4px;
padding: 16px;
overflow-y: auto;
background: #f5f5f5;
}
#file-content h1,
#file-content h2,
#file-content h3 {
margin-top: 0;
}
#file-content p {
line-height: 1.6;
}
.plan-item {
background: white;
padding: 12px;
margin-bottom: 8px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.plan-item h4 {
margin: 0 0 8px 0;
color: #333;
}
.add-to-plan-btn {
background: #6d4aff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin-top: 8px;
}
.add-to-plan-btn:hover {
background: #5a3fd6;
}
.empty-state {
color: #999;
text-align: center;
padding: 32px;
}
</style>
<link rel="stylesheet" href="/node_modules/leaflet/dist/leaflet.css" />
@@ -29,10 +120,144 @@
<script src="graph.mjs" type="module"></script>
</head>
<body>
<script type="module">
import {Graph} from "./graph.mjs"
<div class="container">
<div id="map-container">
<div id="map"></div>
<div id="file-content">
<div class="empty-state">Click on a destination marker to view details</div>
</div>
</div>
<div id="plan-container">
<h3>My Plan</h3>
<div id="plan-items">
<div class="empty-state">No destinations added yet</div>
</div>
</div>
</div>
<script defer type="module">
import {Graph} from "./graph.mjs";
const planItems = [];
function displayContent(data) {
const contentDiv = document.getElementById("file-content");
if (!data || !data.Content) {
contentDiv.innerHTML = '<div class="empty-state">No content available</div>';
return;
}
let html = "";
// Show global content first if available
if (data.Content.global) {
html += `<h1>${data.Content.global.Title || "Untitled"}</h1>`;
for (const [section, subsections] of Object.entries(data.Content.global)) {
if (section === "Title") continue;
html += `<h2>${section}</h2>`;
if (typeof subsections === "object") {
for (const [sub, content] of Object.entries(subsections)) {
html += `<h3>${sub}</h3><p>${content}</p>`;
}
} else {
html += `<p>${subsections}</p>`;
}
}
}
// Show language-specific content
for (const [lang, content] of Object.entries(data.Content)) {
if (lang === "global") continue;
html += `<h2>${lang}</h2>`;
if (content.Title) {
html += `<h1>${content.Title}</h1>`;
}
for (const [section, subsections] of Object.entries(content)) {
if (section === "Title") continue;
html += `<h3>${section}</h3>`;
if (typeof subsections === "object") {
for (const [sub, text] of Object.entries(subsections)) {
html += `<h4>${sub}</h4><p>${text}</p>`;
}
} else {
html += `<p>${subsections}</p>`;
}
}
}
// Add "Add to Plan" button
const ref = data.Reference?.reference || "unknown";
const name =
data.Content?.global?.Title ||
Object.values(data.Content)?.[0]?.Title ||
"Destination";
html += `
<button class="add-to-plan-btn" onclick="addToPlan('${ref}', '${name}')">
Add to Plan
</button>
`;
contentDiv.innerHTML = html;
}
window.addToPlan = function (ref, name) {
if (planItems.some(item => item.ref === ref)) {
alert("Already in plan!");
return;
}
planItems.push({ref, name});
updatePlanDisplay();
};
function updatePlanDisplay() {
const planDiv = document.getElementById("plan-items");
if (planItems.length === 0) {
planDiv.innerHTML = '<div class="empty-state">No destinations added yet</div>';
return;
}
let html = "";
for (const item of planItems) {
html += `
<div class="plan-item">
<h4>${item.name}</h4>
<small>${item.ref}</small>
</div>
`;
}
planDiv.innerHTML = html;
}
(async () => {
const network = new Graph();
// Override the click handler to display content instead of loading connected pins
const originalGet = network.get.bind(network);
network.get = async function (ref) {
const element = await originalGet(ref);
// If this is a node (destination), display its content
if (element && element.data && element.data.Content) {
displayContent(element.data);
}
return element;
};
// Also override the Node.draw method to use our custom click handler
const originalNodeDraw = network.nodesConstructor
? network.nodesConstructor.prototype.draw
: null;
// Reinitialize with our custom behavior
const t = await network.get("<NO/Lunde>");
network.draw();
})();