ctucx.git: trainsearch

web based trip-planner, fork of https://cyberchaos.dev/yuka/trainsearch

commit 755b0014c96df6414bd77ffde9137773f31036fb
parent 7a0cd5f02045e5fc00083c88ae7a15afb4f61e8f
Author: Yureka <yuka@yuka.dev>
Date: Tue, 6 Sep 2022 11:08:31 +0200

wip tripview
4 files changed, 215 insertions(+), 26 deletions(-)
M
src/app_functions.js
|
31
+++++++++++++++++--------------
M
src/journeyView.js
|
15
+++------------
M
src/main.js
|
2
++
A
src/tripView.js
|
193
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/src/app_functions.js b/src/app_functions.js
@@ -68,23 +68,24 @@ const mkSettings = () => {
 
 const processJourneys = data => {
 	for (const journey of data.journeys) processJourney(journey);
-	return data;
 }
 
+export const processLeg = leg => {
+	if (leg.plannedDeparture) leg.plannedDeparture = new Date(leg.plannedDeparture);
+	if (leg.plannedArrival) leg.plannedArrival = new Date(leg.plannedArrival);
+	if (leg.departure) leg.departure = new Date(leg.departure);
+	if (leg.arrival) leg.arrival = new Date(leg.arrival);
+	for (const stopover of (leg.stopovers || [])) {
+		if (stopover.plannedDeparture) stopover.plannedDeparture = new Date(stopover.plannedDeparture);
+		if (stopover.plannedArrival) stopover.plannedArrival = new Date(stopover.plannedArrival);
+		if (stopover.departure) stopover.departure = new Date(stopover.departure);
+		if (stopover.arrival) stopover.arrival = new Date(stopover.arrival);
+	}
+};
 const processJourney = journey => {
 	for (const leg of journey.legs) {
-		if (leg.plannedDeparture) leg.plannedDeparture = new Date(leg.plannedDeparture);
-		if (leg.plannedArrival) leg.plannedArrival = new Date(leg.plannedArrival);
-		if (leg.departure) leg.departure = new Date(leg.departure);
-		if (leg.arrival) leg.arrival = new Date(leg.arrival);
-		for (const stopover of (leg.stopovers || [])) {
-			if (stopover.plannedDeparture) stopover.plannedDeparture = new Date(stopover.plannedDeparture);
-			if (stopover.plannedArrival) stopover.plannedArrival = new Date(stopover.plannedArrival);
-			if (stopover.departure) stopover.departure = new Date(stopover.departure);
-			if (stopover.arrival) stopover.arrival = new Date(stopover.arrival);
-		}
+		processLeg(leg);
 	}
-	return journey;
 };
 
 export const getJourneys = async slug => {

@@ -99,7 +100,8 @@ export const getJourney = async (refreshToken, profile) => {
 	if (!data || JSON.stringify(data.settings) != JSON.stringify(settings)) {
 		data = await refreshJourney(refreshToken, profile);
 	}
-	return processJourney(data);
+	processJourney(data);
+	return data;
 };
 
 export const getMoreJourneys = async (slug, mode) => {

@@ -173,7 +175,8 @@ export const newJourneys = async (params) => {
 	data.settings = requestSettings;
 	data.profile = settings.profile;
 	await addJourneys(data);
-	return processJourneys(data);
+	processJourneys(data);
+	return data;
 };
 
 export const t = (key, ...params) => {
diff --git a/src/journeyView.js b/src/journeyView.js
@@ -43,7 +43,7 @@ const remarksTemplate = ([type, remarks]) => !!remarks.length ? html`
 	<a class="link icon-${type}" @click=${() => showRemarksModal(type, remarks)}></a>
 ` : '';
 
-const legTemplate = leg => {
+const legTemplate = (leg, profile) => {
 	const allRemarks = leg.remarks || [];
 	const remarks = {
 		'status': allRemarks.filter(r => r.type === 'status'),

@@ -51,10 +51,6 @@ const legTemplate = leg => {
 		'other': allRemarks.filter(r => r.type !== 'status' && r.type !== 'hint'),
 	};
 
-	let marudorUrl = null;
-	if (leg.line && (leg.line.product == 'nationalExpress' || leg.line.product == 'national' || leg.line.product == 'regionalExp' || leg.line.product == 'regional')) {
-		marudorUrl = 'https://marudor.de/details/' + encodeURIComponent(getAdditionalName(leg.line) || leg.line.name) + '/' + Number(leg.plannedDeparture);
-	}
 
 	return html`
 		${leg.walking ? html`

@@ -69,11 +65,7 @@ const legTemplate = leg => {
 				<thead>
 					<tr>
 						<td colspan="4">
-							<span>${marudorUrl ? html`
-								<a href="${marudorUrl}">${leg.line.name}${leg.direction ? html` → ${leg.direction}` : ''}</a>
-							` : html `
-								${leg.line.name}${leg.direction ? html` → ${leg.direction}` : ''}
-							`}
+							<span><a href="#/t/${profile}/${leg.tripId}">${leg.line.name}${leg.direction ? html` → ${leg.direction}` : ''}</a>
 							${leg.cancelled ? html`<b class="cancelled-text">${t('cancelled-ride')}</b>` : ''}
 							${Object.entries(remarks).map(remarksTemplate)}
 							${travelynxTemplate(leg)}</span>

@@ -129,7 +121,6 @@ const legTemplate = leg => {
 const journeyTemplate = (data, profile) => {
 	const duration = data.legs[data.legs.length - 1].arrival - data.legs[0].departure;
 
-	console.log(data);
 	const legs = [];
 	let changes = 0;
 	let lastArrival;

@@ -172,7 +163,7 @@ const journeyTemplate = (data, profile) => {
 				<a class="reload icon-reload" title="${t("reload")}" @click=${() => refreshJourneyView(data.refreshToken, profile)}>${t("reload")}</a>
 			</header>
 
-			${legs.map(legTemplate)}
+			${legs.map(leg => legTemplate(leg, profile))}
 		</div>
 	`;
 };
diff --git a/src/main.js b/src/main.js
@@ -2,6 +2,7 @@ import { route, go, start } from './router.js';
 import { searchView } from './searchView.js';
 import { journeysView } from './journeysView.js';
 import { journeyView } from './journeyView.js';
+import { tripView } from './tripView.js';
 import { initSettings, settings } from './settings.js';
 import { initDataStorage } from './dataStorage.js';
 import { initHafasClient } from './hafas_client';

@@ -16,6 +17,7 @@ import { showDiv, hideDiv, ElementById } from './helpers.js';
 	route(/^\/$/, searchView);
 	route(/^\/([a-zA-Z0-9]+)\/([a-z]+)$/, journeysView);
 	route(/^\/j\/([a-z]+)\/(.+)$/, journeyView);
+	route(/^\/t\/([a-z]+)\/(.+)$/, tripView);
 
 	ElementById('overlay').innerHTML = '';
 	hideDiv('overlay');
diff --git a/src/tripView.js b/src/tripView.js
@@ -0,0 +1,193 @@
+import { settings } from './settings.js';
+import { showDiv, hideDiv, ElementById, formatDateTime, formatDuration, formatPrice } from './helpers.js';
+import { ConsoleLog, parseName, ds100Names, t, timeTemplate, processLeg } from './app_functions.js';
+import { showAlertModal, showLoader, hideOverlay, showModal } from './overlays.js';
+import { go } from './router.js';
+import { html, render } from 'lit-html';
+import { getHafasClient, client } from './hafas_client';
+
+const remarksModalTemplate = (type, remarks) => html`
+	<table class="remarks">
+		${remarks.map(element => html`
+			<tr>
+			<td>
+				<span class="remark icon-${type}"></span>
+				<span>${element.text}</span>
+			</td>
+			</tr>
+		`)}
+	</table>
+`;
+
+const getAdditionalName = (line) => {
+	const splitName = line.name.split(' ');
+	if (splitName.length === 2 && line.fahrtNr && line.fahrtNr != splitName[1])
+		return `${splitName[0]} ${line.fahrtNr}`;
+	else
+		return null;
+};
+
+const travelynxTemplate = (element) => {
+	if (settings.travelynx && element.line && element.line.mode === 'train') {
+		const trainName = getAdditionalName(element.line) || element.line.name;
+
+		return html`
+			<a class="link icon-travelynx" href="https://travelynx.de/s/${element.origin.id}?train=${encodeURIComponent(trainName)}"></a>
+		`;
+	}
+};
+
+const showRemarksModal = (type, remarks) => {
+	showModal(t('remarks'), remarksModalTemplate(type, remarks));
+};
+const remarksTemplate = ([type, remarks]) => !!remarks.length ? html`
+	<a class="link icon-${type}" @click=${() => showRemarksModal(type, remarks)}></a>
+` : '';
+
+const tripTemplate = (data, profile) => {
+	let changes = 0;
+	let lastArrival;
+
+	const allRemarks = data.remarks || [];
+	const remarks = {
+		'status': allRemarks.filter(r => r.type === 'status'),
+		'hint': allRemarks.filter(r => r.type === 'hint'),
+		'other': allRemarks.filter(r => r.type !== 'status' && r.type !== 'hint'),
+	};
+
+	let marudorUrl = null;
+	if (data.line && (data.line.product == 'nationalExpress' || data.line.product == 'national' || data.line.product == 'regionalExp' || data.line.product == 'regional')) {
+		marudorUrl = 'https://marudor.de/details/' + encodeURIComponent(getAdditionalName(data.line) || data.line.name) + '/' + Number(data.plannedDeparture);
+	}
+
+					//<p>b>${t('duration')}: ${formatDuration(duration)} | ${t('changes')}: ${changes-1} | ${t('date')}: ${formatDateTime(data.plannedDeparture, 'date')}${settings.showPrices && data.price ? html` | ${t('price')}: <td><span>${formatPrice(data.price)}</span></td>` : ''}</b></p>
+	return html`
+		<div class="journey">
+			<header>
+				<a class="back icon-back invisible"></a>
+				<div class="header-content">
+					<h3>wip</h3>
+				</div>
+				<a class="reload icon-reload" title="${t("reload")}" @click=${() => refreshJourneyView(data.refreshToken, profile)}>${t("reload")}</a>
+			</header>
+
+			<div class="card">
+			<table>
+				<thead>
+					<tr>
+						<td colspan="4">
+							<span>${marudorUrl ? html`
+								<a href="${marudorUrl}">${data.line.name}${data.direction ? html` → ${data.direction}` : ''}</a>
+							` : html `
+								${data.line.name}${data.direction ? html` → ${data.direction}` : ''}
+							`}
+							${data.cancelled ? html`<b class="cancelled-text">${t('cancelled-ride')}</b>` : ''}
+							${Object.entries(remarks).map(remarksTemplate)}
+							${travelynxTemplate(data)}</span>
+						</td>
+					</tr>
+					<tr>
+						<td colspan="4">
+							<div class="train-details">
+								${getAdditionalName(data.line) ? html`
+									<div class="train-detail">
+										Trip: ${getAdditionalName(data.line)}
+									</div>
+								` : ''}
+								${data.line.trainType ? html`
+									<div class="train-detail">
+										Train type: ${data.line.trainType}
+									</div>
+								` : ''}
+								<div class="train-detail">
+									${t('duration')}: ${formatDuration(data.arrival - data.departure)}
+								</div>
+								${data.loadFactor ? html`
+									<div class="train-detail">
+										${t("load-"+data.loadFactor)}
+									</div>
+								` : ''}
+							</div>
+						</td>
+					</tr>
+					<tr>
+						<th>${t('arrival')}</th>
+						<th>${t('departure')}</th>
+						<th class="station-column">${t('station')}</th>
+						<th>${t('platform')}</th>
+					</tr>
+				</thead>
+				<tbody>
+					${(data.stopovers || []).map(stop => html`
+						<tr class="stop ${stop.cancelled ? 'cancelled' : ''}">
+							<td><span>${timeTemplate(stop, 'arrival')}</span></td>
+							<td><span>${timeTemplate(stop, 'departure')}</span></td>
+							<td><span>${stop.stop.name} ${ds100Names(stop.stop.id)}</span></td>
+							<td><span>${stopPlatformTemplate(stop)}</span></td>
+						</tr>
+					`)}
+				</tbody>
+			</table>
+			</div>
+		</div>
+	`;
+};
+
+const stopPlatformTemplate = (data) => {
+	if (data.departurePlatform) {
+		if (data.departurePlatform != data.plannedDeparturePlatform) {
+			return html`<b>${data.departurePlatform}</b>`;
+		} else {
+			return data.plannedDeparturePlatform;
+		}
+	} else if (data.arrivalPlatform) {
+		if (data.arrivalPlatform != data.plannedArrivalPlatform) {
+			return html`<b>${data.arrivalPlatform}</b>`;
+		} else {
+			return data.plannedArrivalPlatform;
+		}
+	} else {
+		return '-';
+	}
+};
+
+export const tripView = async (match, isUpdate) => {
+	if (!isUpdate) showLoader();
+	let profile, refreshToken, data;
+	try {
+		profile = match[0];
+		refreshToken = decodeURIComponent(match[1]);
+		const client = await getHafasClient(profile);
+		data = await client.trip(refreshToken, ".", {stopovers: true});
+		processLeg(data);
+	} catch(e) {
+		showAlertModal(e.toString());
+		throw e;
+	}
+	hideOverlay();
+
+	ConsoleLog(data);
+	render(tripTemplate(data, profile), ElementById('content'));
+
+	//if (!isUpdate) refreshJourneyView(refreshToken); // update data in the background
+
+	/*const history_id = dataStorage.journeysHistory.findIndex(obj => obj.reqId === reqId);
+
+	if (dataStorage.journeysHistory[history_id] !== undefined) {
+		dataStorage.journeysHistory[history_id].journeyId = journeyId;
+		saveDataStorage();
+	}*/
+};
+
+const refreshJourneyView = async (refreshToken, profile) => {
+	document.querySelector('.reload').classList.add('spinning');
+	try {
+		await refreshJourney(refreshToken, profile);
+	} catch(e) {
+		showAlertModal(e.toString());
+		document.querySelector('.reload').classList.remove('spinning');
+		throw e;
+	}
+	journeyView([profile, refreshToken], true);
+	document.querySelector('.reload').classList.remove('spinning');
+};