Init project

This commit is contained in:
2025-06-18 15:26:47 +02:00
commit 1bdc9dacb6
11 changed files with 2428 additions and 0 deletions

28
web/api.js Normal file
View File

@@ -0,0 +1,28 @@
export function fetchApi(pathcomponents, query) {
query.pretty = true;
let url = '/api/' + pathcomponents.join("/") + "?" + new URLSearchParams(query).toString();
return fetch(url).then(response => {
if (!response.ok) {
throw new Error("Fetching api failed");
}
return response;
}).then(response => response.json());
}
export function fetchLocations(query) {
return fetchApi(["locations"], {
query: query,
addresses: false,
poi: false,
subStop: false,
entrances: false,
linesOfStops: false,
});
}
export function fetchJourneys(from_, to) {
return fetchApi(["journeys"], {
from: from_,
to: to,
});
}

26
web/index.html Normal file
View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" href="style.css" />
<script type="module" src="traveldrafter.js"></script>
</head>
<body>
<div id="journey-search-from">Select…</div>
<div id="journey-search-to">Select…</div>
<div id="journey-search-submit">Search</div>
<div id="journey-search-result"></div>
<div id="locations-search" class="popup">
<div class="popup-close">&times;</div>
<div id="locations-search-content" class="popup-content">
<div class="container">
<input id="locations-search-query" class="form-control" type="text" />
<div id="locations-search-response"></div>
</div>
</div>
</div>
</body>
</html>

39
web/journeys-search.js Normal file
View File

@@ -0,0 +1,39 @@
import { fetchJourneys } from './api.js';
import { attachLocationsSearch } from './locations-search.js';
let element_from = document.querySelector("#journey-search-from");
let element_to = document.querySelector("#journey-search-to");
let element_submit = document.querySelector("#journey-search-submit");
let element_result = document.querySelector("#journey-search-result");
export function setupJourneysSearch() {
attachLocationsSearch(element_from);
attachLocationsSearch(element_to);
element_submit.addEventListener("click", event => {
element_result.innerText = "Loading…";
fetchJourneys(element_from.dataset.locationId, element_to.dataset.locationId).then(result => {
for (let journey of result.journeys) {
element_result.appendChild(createJourneyElement(journey));
}
});
});
}
function createJourneyElement(journey) {
let el = document.createElement("div");
for (let leg of journey.legs) {
el.appendChild(createJourneyLegElement(leg));
}
return el;
}
function createJourneyLegElement(leg) {
let el = document.createElement("div");
el.innerText = JSON.stringify(leg);
el.innerText = leg?.line?.name + ": " + leg.origin.name + " > " + leg.destination.name;
return el;
}

36
web/locations-search.js Normal file
View File

@@ -0,0 +1,36 @@
import { fetchLocations } from './api.js';
let element_locations_search = document.querySelector("#locations-search");
let element_query = document.querySelector("#locations-search-query");
let element_response = document.querySelector("#locations-search-response");
export function setupLocationsSearch() {
element_query.addEventListener("change", (event) => {
element_response.innerText = "Loading…";
fetchLocations(event.target.value).then(result => {
element_response.innerText = "";
result.forEach(lr => {
let location_element = document.createElement("div");
location_element.innerText = lr.name;
location_element.dataset.locationId = lr.id;
location_element.addEventListener("click", event => {
console.log(event.target.dataset.locationId);
element_locations_search.locationSelectedCallback(event.target.innerText, event.target.dataset.locationId);
element_locations_search.style.display = "none";
});
element_response.appendChild(location_element);
});
});
});
}
export function attachLocationsSearch(search_element) {
search_element.addEventListener("click", event => {
element_locations_search.locationSelectedCallback = (location_name, location_id) => {
search_element.innerText = location_name;
search_element.dataset.locationId = location_id;
};
element_locations_search.style.display = "block";
});
}

7
web/popup.js Normal file
View File

@@ -0,0 +1,7 @@
export function setupPopups() {
document.querySelectorAll(".popup-close").forEach(element => {
element.addEventListener("click", event => {
event.target.parentElement.style.display="none";
});
});
}

54
web/style.css Normal file
View File

@@ -0,0 +1,54 @@
* {
font-family: sans-serif;
}
.container {
margin-right: auto;
margin-left: auto;
max-width: 768px;
}
.popup {
position: fixed;
height: 100%;
width: 100%;
top: 0;
left: 0;
z-index: 10000;
background-color: rgba(0, 0, 0, 0.9);
color: white;
display: none;
}
.popup .popup-close {
margin: 60px;
margin-left: auto;
font-size: 60px;
width: 70px;
}
.popup .popup-content {
position: relative;
margin-top: 20px;
margin-bottom: 20px;
}
.popup input {
color: white;
}
input.form-control {
width: 100%;
background-color: transparent;
border: none;
border-bottom-style: solid;
border-width: 1px;
font-size: 2em;
}

10
web/traveldrafter.js Normal file
View File

@@ -0,0 +1,10 @@
import * as Api from './api.js';
import { setupPopups } from "./popup.js";
import { setupLocationsSearch } from './locations-search.js';
import { setupJourneysSearch } from './journeys-search.js';
window.Api = Api;
setupPopups();
setupLocationsSearch();
setupJourneysSearch();