ctucx.git: trainsearch

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

commit 15b29dd0653d4220058bbd40ac74588bbe13fecc
parent e28aec85191253aabf183b739794b0aee9491bec
Author: Katja (ctucx) <git@ctu.cx>
Date: Sun, 26 Jan 2025 20:26:27 +0100

settingsView: add loyalty/discount card option
7 files changed, 80 insertions(+), 19 deletions(-)
M
src/app_functions.js
|
11
++++++++++-
M
src/dataStorage.js
|
8
++++++++
M
src/helpers.js
|
50
+++++++++++++++++++++++++++++++++++---------------
M
src/languages.js
|
6
++++++
M
src/searchView.js
|
7
++++---
M
src/settings.js
|
1
+
M
src/settingsView.js
|
16
++++++++++++++++
diff --git a/src/app_functions.js b/src/app_functions.js
@@ -4,7 +4,7 @@ import { go } from './router.js';
 import { settings, subscribeSettings } from './settings.js';
 import { showLoader, hideOverlay, showModal, showAlertModal } from './overlays.js';
 import { languages } from './languages.js';
-import { getFrom, getTo } from './helpers.js';
+import { getFrom, getTo, loyaltyCardToString, loyaltyCardFromString } from './helpers.js';
 import { formatDateTime } from './formatters.js';
 import { getHafasClient, client } from './hafas_client';
 import { trainsearchToHafas, hafasToTrainsearch } from './refresh_token';

@@ -41,6 +41,10 @@ const addJourneys = async data => {
 			slug: data.slug,
 		};
 	});
+
+
+	if (typeof data.params.loyaltyCard === 'object') data.params.loyaltyCard = loyaltyCardToString(data.params.loyaltyCard);
+
 	const journeysOverviewEntry = {
 		...data,
 		journeys: data.journeys.map(j => j.refreshToken),

@@ -100,6 +104,9 @@ export const getJourney = async (refreshToken, profile) => {
 export const getMoreJourneys = async (slug, mode) => {
 	const saved = await db.getJourneysOverview(slug);
 	const params = { ...saved.params, ...journeySettings() };
+
+	if (typeof params.loyaltyCard === 'string') params.loyaltyCard = loyaltyCardFromString(params.loyaltyCard);
+
 	params[mode+'Than'] = saved[mode+'Ref'];
 	let { departure, arrival, from, to, ...moreOpt } = params;
 	const [newData, ...existingJourneys] = await Promise.all(

@@ -111,9 +118,11 @@ export const getMoreJourneys = async (slug, mode) => {
 		...saved,
 		...newData,
 	};
+
 	for (const journey of newData.journeys) {
 		journey.refreshToken = hafasToTrainsearch(journey.refreshToken);
 	}
+
 	if (mode === 'earlier') {
 		res.journeys = newData.journeys.concat(existingJourneys);
 		res.indexOffset += newData.journeys.length;
diff --git a/src/dataStorage.js b/src/dataStorage.js
@@ -57,6 +57,14 @@ class IDBStorage {
 					}
 
 					await transaction.objectStore('settings').put(settings, 'settings');
+				case 4:
+					settings = await transaction.objectStore('settings').get('settings');
+
+					if (settings !== undefined && settings.loyaltyCard === undefined) {
+						settings.loyaltyCard = 'NONE';
+					}
+
+					await transaction.objectStore('settings').put(settings, 'settings');
 				}
 			},
 			blocking: async () => {
diff --git a/src/helpers.js b/src/helpers.js
@@ -1,18 +1,38 @@
-export const ElementById = (id) => document.getElementById(id);
+const loyaltyCards = {
+	NONE: Symbol('no loyalty card'),
+	BAHNCARD: Symbol('Bahncard'),
+	VORTEILSCARD: Symbol('VorteilsCard'),
+	HALBTAXABO: Symbol('HalbtaxAbo'),
+	VOORDEELURENABO: Symbol('Voordeelurenabo'),
+	SHCARD: Symbol('SH-Card'),
+	GENERALABONNEMENT: Symbol('General-Abonnement'),
+};
+
+const loyaltyCardsReverse = {
+	'Symbol(no loyalty card)':    'NONE',
+	'Symbol(Bahncard)':           'BAHNCARD',
+	'Symbol(VorteilsCard)':       'VORTEILSCARD',
+	'Symbol(HalbtaxAbo)':         'HALBTAXABO',
+	'Symbol(Voordeelurenabo)':    'VOORDEELURENABO',
+	'Symbol(SH-Card)':            'SHCARD',
+	'Symbol(General-Abonnement)': 'GENERALABONNEMENT',
+};
 
-export const showElement   = (element) => element.classList.remove('hidden');
-export const hideElement   = (element) => element.classList.add('hidden');
-export const elementHidden = (element) => element.classList.contains('hidden');
+export const ElementById   = id      => document.getElementById(id);
 
-export const unflipElement = (element) => element.classList.remove('flipped');
-export const flipElement = (element) => element.classList.add('flipped');
+export const showElement   = element => element.classList.remove('hidden');
+export const hideElement   = element => element.classList.add('hidden');
+export const elementHidden = element => element.classList.contains('hidden');
 
+export const unflipElement = element => element.classList.remove('flipped');
+export const flipElement   = element => element.classList.add('flipped');
 
-export const padZeros = (str) => {
+
+export const padZeros = str => {
 	return ('00' + str).slice(-2);
 };
 
-export const isValidDate = (date) => {
+export const isValidDate = date => {
 	const matches = /^(\d{4})[-\/](\d{2})[-\/](\d{2})$/.exec(date);
 	if (matches == null) {
 		return false;

@@ -27,14 +47,14 @@ export const isValidDate = (date) => {
 		   composedDate.getFullYear() == y;
 };
 
-export const isEmptyObject = (obj) => {
-  for (const prop in obj) {
-    if (Object.hasOwn(obj, prop)) {
-      return false;
-    }
-  }
+export const loyaltyCardFromString = string => {
+	const splitedString = string.split('-');
+	if (splitedString[0] === 'NONE') return { type: loyaltyCards[splitedString[0]] };
+	return { type: loyaltyCards[splitedString[0]], discount: splitedString[1], class: splitedString[2] };
+};
 
-  return true;
+export const loyaltyCardToString = loyaltyCard => {
+	return `${loyaltyCardsReverse[loyaltyCard.type.toString()]}-${loyaltyCard.discount}-${loyaltyCard.class}`;
 }
 
 export const getFrom = journeys => {
diff --git a/src/languages.js b/src/languages.js
@@ -65,6 +65,9 @@ export const languages = {
 		'show-prices':       'Preise anzeigen',
 		'titleSetDateTimeNow': 'Setze Uhrzeit & Datum auf jetzt',
 		'titleBikeFriendly':   'Fahrradmitnahme möglich',
+		'loyaltyCard':         'Ermäßigungskarte',
+		'loyaltyCardNone':     'keine Ermäßigungskarte',
+		'class':               'Klasse',
 	},
 
 	'nl': {

@@ -204,5 +207,8 @@ export const languages = {
 		'combineDateTime':   'Use combined DateTime-input',
 		'titleSetDateTimeNow': 'Set Date & Time to now',
 		'titleBikeFriendly':   'Bicycle transport possible',
+		'loyaltyCard':         'Discount Card',
+		'loyaltyCardNone':     'No discount card',
+		'class':               'Class',
 	}
 };
diff --git a/src/searchView.js b/src/searchView.js
@@ -1,5 +1,5 @@
 import { html, nothing, render } from 'lit-html';
-import { ElementById, hideElement, showElement, elementHidden, flipElement, unflipElement, padZeros, isValidDate, isEmptyObject } from './helpers.js';
+import { ElementById, hideElement, showElement, elementHidden, flipElement, unflipElement, padZeros, isValidDate, loyaltyCardFromString } from './helpers.js';
 import { db } from './dataStorage.js';
 import { t, getJourneys, newJourneys, ds100Reverse } from './app_functions.js';
 import { formatName, formatFromTo } from './formatters.js';

@@ -104,7 +104,7 @@ const searchTemplate = (journeysHistory) => html`
 				<input type="datetime-local" name="datetime" id="datetime" title="${t('date')} & ${t('time')}" value="${viewState.dateTimeValue}" class="${!settings.combineDateTime ? 'hidden' : nothing}" required>
 				<input type="time"           name="time"     id="time"     title="${t('time')}"                value="${viewState.timeValue}"     class="${!settings.combineDateTime ? nothing : 'hidden'}" required>
 				<input type="date"           name="date"     id="date"     title="${t('date')}"                value="${viewState.dateValue}"     class="${!settings.combineDateTime ? nothing : 'hidden'}" required>
-				<div class="button icon-clock" title="${t('now')}" @click=${setDateTimeNow}></div>
+				<div class="button icon-clock" title="${t('titleSetDateTimeNow')}" @click=${setDateTimeNow}></div>
 			</div>
 
 			<div class="row">

@@ -326,6 +326,7 @@ const submitForm = async (event) => {
 		accessibility: settings.accessibility,
 		bike: settings.bikeFriendly,
 		products: settings.products,
+		loyaltyCard: loyaltyCardFromString(settings.loyaltyCard),
 	};
 
 	if (via)                  params.via       = via;

@@ -476,7 +477,7 @@ const focusNextElement = (currentElementId) => {
 
 		case 'to':
 			hideSuggestions(currentElementId);
-			ElementById('go').click();
+			ElementById('go').focus();
 			break;
 	}
 };
diff --git a/src/settings.js b/src/settings.js
@@ -23,6 +23,7 @@ const defaultSettings = {
 	},
 	accessibility: 'none',
 	bikeFriendly: false,
+	loyaltyCard: 'NONE',
 	journeysViewMode: 'canvas',
 	combineDateTime: false,
 	showPrices: true,
diff --git a/src/settingsView.js b/src/settingsView.js
@@ -35,6 +35,19 @@ const settingsTemplate = () => html`
 			</select>
 		</div>
 
+		<div class="row" id="loyaltyCardElement">
+			<label for="loyaltyCard">${t('loyaltyCard')}:</label>
+			<select id="loyaltyCard">
+				<option value="NONE"          ?selected=${settings.loyaltyCard === 'NONE'}>${t('loyaltyCardNone')}</option>
+				<option value="BAHNCARD-25-2" ?selected=${settings.loyaltyCard === 'BAHNCARD-25-2'}>BahnCard 25, 2. ${t("class")}</option>
+				<option value="BAHNCARD-25-1" ?selected=${settings.loyaltyCard === 'BAHNCARD-25-1'}>BahnCard 25, 1. ${t("class")}</option>
+				<option value="BAHNCARD-50-2" ?selected=${settings.loyaltyCard === 'BAHNCARD-50-2'}>BahnCard 50, 2. ${t("class")}</option>
+				<option value="BAHNCARD-50-1" ?selected=${settings.loyaltyCard === 'BAHNCARD-50-1'}>BahnCard 50, 1. ${t("class")}</option>
+				<option value="BAHNCARD-100-2" ?selected=${settings.loyaltyCard === 'BAHNCARD-100-2'}>BahnCard 100, 2. ${t("class")}</option>
+				<option value="BAHNCARD-100-1" ?selected=${settings.loyaltyCard === 'BAHNCARD-100-1'}>BahnCard 100, 1. ${t("class")}</option>
+			</select>
+		</div>
+
 		<div class="column">
 			<span>${t('options')}:</span>
 			<label id="showPricesElement"><input type="checkbox" id="showPrices" ?checked=${settings.showPrices}> ${t('show-prices')} (${t("experimental")})<br></label>

@@ -53,10 +66,12 @@ const profileChangeHandler = (profile) => {
 	switch (profile) {
 		case 'db':
 			showElement(ElementById('showPricesElement'))
+			showElement(ElementById('loyaltyCardElement'))
 			break;
 
 		default:
 			hideElement(ElementById('showPricesElement'))
+			hideElement(ElementById('loyaltyCardElement'))
 			break;
 	};
 };

@@ -75,6 +90,7 @@ const saveSettings = async () => {
 		settings.showPrices      = ElementById('showPrices').checked;
 		settings.language        = ElementById('language').value;
 		settings.profile         = ElementById('profile').value;
+		settings.loyaltyCard     = ElementById('loyaltyCard').value;
 
 		return settings;
 	});