diff --git a/Logic/OpeningHours.ts b/Logic/OpeningHours.ts index 82dfef0..18e8d5e 100644 --- a/Logic/OpeningHours.ts +++ b/Logic/OpeningHours.ts @@ -1,3 +1,5 @@ +import {Utils} from "../Utils"; + export interface OpeningHour { weekday: number, // 0 is monday, 1 is tuesday, ... startHour: number, @@ -7,6 +9,35 @@ export interface OpeningHour { } export class OpeningHourUtils { + + + private static readonly days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"] + private static readonly daysIndexed = { + mo: 0, + tu: 1, + we: 2, + th: 3, + fr: 4, + sa: 5, + su: 6 + } + + public static ToString(ohs: OpeningHour[]) { + const parts = []; + + function hhmm(h, m) { + return Utils.TwoDigits(h) + ":" + Utils.TwoDigits(m); + } + + for (const oh of ohs) { + parts.push( + OpeningHourUtils.days[oh.weekday] + " " + hhmm(oh.startHour, oh.startMinutes) + "-" + hhmm(oh.endHour, oh.endMinutes) + ) + } + + return parts.join("; ")+";" + } + /** * Merge duplicate opening-hour element in place. * Returns true if something changed @@ -18,12 +49,12 @@ export class OpeningHourUtils { const newList = []; while (queue.length > 0) { let maybeAdd = queue.pop(); - + let doAddEntry = true; if(maybeAdd.weekday == undefined){ doAddEntry = false; } - + for (let i = newList.length - 1; i >= 0 && doAddEntry; i--) { let guard = newList[i]; if (maybeAdd.weekday != guard.weekday) { @@ -60,7 +91,7 @@ export class OpeningHourUtils { endHour = maybeAdd.endHour; endMinutes = maybeAdd.endMinutes; } - + queue.push({ startHour: startHour, startMinutes: startMinutes, @@ -107,5 +138,47 @@ export class OpeningHourUtils { return OpeningHourUtils.startTime(mightLieIn) <= OpeningHourUtils.endTime(checked) && OpeningHourUtils.endTime(checked) <= OpeningHourUtils.endTime(mightLieIn) } + + static Parse(str: string) { + if (str === undefined || str === "") { + return [] + } + + const parts = str.toLowerCase().split(";"); + const ohs = [] + + function parseTime(hhmm) { + const spl = hhmm.trim().split(":"); + return [Number(spl[0].trim()), Number(spl[1].trim())] + } + + for (const part of parts) { + if(part === ""){ + continue; + } + try { + + const partSplit = part.trim().split(" "); + const weekday = OpeningHourUtils.daysIndexed[partSplit[0]] + const timings = partSplit[1].split("-"); + const start = parseTime(timings[0]) + const end = parseTime(timings[1]); + + const oh: OpeningHour = { + weekday: weekday, + startHour: start[0], + startMinutes: start[1], + endHour: end[0], + endMinutes: end[1], + } + ohs.push(oh); + + } catch (e) { + console.error("Could not parse opening hours part", part, ", skipping it due to ", e) + } + } + + return ohs; + } } diff --git a/State.ts b/State.ts index e19a4be..6c3f556 100644 --- a/State.ts +++ b/State.ts @@ -23,7 +23,7 @@ export default class State { // The singleton of the global state public static state: State; - public static vNumber = "0.0.9b"; + public static vNumber = "0.1.0"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/UI/Input/OpeningHours/OpeningHoursPicker.ts b/UI/Input/OpeningHours/OpeningHoursPicker.ts index 1ae9663..0c6ef17 100644 --- a/UI/Input/OpeningHours/OpeningHoursPicker.ts +++ b/UI/Input/OpeningHours/OpeningHoursPicker.ts @@ -14,7 +14,7 @@ export default class OpeningHoursPicker extends InputElement { private readonly _weekdays: UIEventSource = new UIEventSource([]); - constructor(ohs: UIEventSource) { + constructor(ohs: UIEventSource = new UIEventSource([])) { super(); this._ohs = ohs; this._backgroundTable = new OpeningHoursPickerTable(this._weekdays); diff --git a/UI/Input/OpeningHours/OpeningHoursPickerTable.ts b/UI/Input/OpeningHours/OpeningHoursPickerTable.ts index 00f9f0f..3b38e03 100644 --- a/UI/Input/OpeningHours/OpeningHoursPickerTable.ts +++ b/UI/Input/OpeningHours/OpeningHoursPickerTable.ts @@ -48,8 +48,8 @@ export default class OpeningHoursPickerTable extends InputElement { Utils.Times(id => `
`, 7) + ''; } - let days = OpeningHoursPickerTable.days.join(""); - return `${rows}
${days}
`; + let days = OpeningHoursPickerTable.days.join(""); + return `${rows}
${days}
`; } protected InnerUpdate() { diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 8586084..4ea3126 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -8,13 +8,15 @@ import {UIElement} from "../UIElement"; import {UIEventSource} from "../../Logic/UIEventSource"; import CombinedInputElement from "./CombinedInputElement"; import SimpleDatePicker from "./SimpleDatePicker"; +import OpeningHoursPicker from "./OpeningHours/OpeningHoursPicker"; +import {OpeningHour, OpeningHourUtils} from "../../Logic/OpeningHours"; interface TextFieldDef { name: string, explanation: string, isValid: ((s: string, country?: string) => boolean), reformat?: ((s: string, country?: string) => string), - inputHelper?: (value:UIEventSource) => InputElement, + inputHelper?: (value: UIEventSource) => InputElement, } export default class ValidatedTextField { @@ -141,6 +143,24 @@ export default class ValidatedTextField { return parsePhoneNumberFromString(str, country?.toUpperCase())?.isValid() ?? false }, (str, country: any) => parsePhoneNumberFromString(str, country?.toUpperCase()).formatInternational() + ), + ValidatedTextField.tp( + "opening_hours", + "Has extra elements to easily input when a POI is opened", + (s, country) => true, // TODO + str => str, // TODO reformat with opening_hours.js + (value) => { + const input = new InputElementMap(new OpeningHoursPicker(), + (a, b) => a === b, + ohs => OpeningHourUtils.ToString(ohs), + str => OpeningHourUtils.Parse(str) + ) + input.GetValue().addCallback(latest => { + console.log(latest); + value.setData(latest); + }) + return input; + } ) ] diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index e7f752f..f40704b 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -214,6 +214,14 @@ "type": "email" } }, + { + "render": "Shop is open {opening_hours}", + "question": "When is this shop opened?", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + } + }, { "question": { "en": "Does this shop sell bikes?", diff --git a/css/openinghourstable.css b/css/openinghourstable.css index a54c700..a3591d6 100644 --- a/css/openinghourstable.css +++ b/css/openinghourstable.css @@ -92,10 +92,8 @@ } .oh-timerange-inner input { - width: calc(100% - 2em); + width: 100%; box-sizing: border-box; - margin-left: 1em; - margin-right:1em; } .oh-timerange-inner-small { @@ -109,8 +107,6 @@ .oh-timerange-inner-small input { width: min-content; box-sizing: border-box; - margin-left: 1em; - margin-right:1em; } .oh-delete-range{