forked from MapComplete/MapComplete
		
	Fix: opening hours now correctly shows "closes at <point far in the future>"
This commit is contained in:
		
							parent
							
								
									8584a4ba68
								
							
						
					
					
						commit
						d5b0976fb0
					
				
					 3 changed files with 53 additions and 46 deletions
				
			
		|  | @ -326,6 +326,7 @@ | ||||||
|             "openTill": "till", |             "openTill": "till", | ||||||
|             "open_24_7": "Open around the clock", |             "open_24_7": "Open around the clock", | ||||||
|             "open_during_ph": "During a public holiday, it is", |             "open_during_ph": "During a public holiday, it is", | ||||||
|  |             "open_until": "Closes at {date}", | ||||||
|             "opensAt": "from", |             "opensAt": "from", | ||||||
|             "ph_closed": "closed", |             "ph_closed": "closed", | ||||||
|             "ph_not_known": " ", |             "ph_not_known": " ", | ||||||
|  |  | ||||||
|  | @ -160,7 +160,7 @@ export class OH { | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             for (let i = newList.length - 1; i >= 0 && doAddEntry; i--) { |             for (let i = newList.length - 1; i >= 0 && doAddEntry; i--) { | ||||||
|                 let guard = newList[i] |                 const guard = newList[i] | ||||||
|                 if (maybeAdd.weekday != guard.weekday) { |                 if (maybeAdd.weekday != guard.weekday) { | ||||||
|                     // Not the same day
 |                     // Not the same day
 | ||||||
|                     continue |                     continue | ||||||
|  | @ -236,9 +236,8 @@ export class OH { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Gives the number of hours since the start of day. |      * Gives the number of hours since the start of day. | ||||||
|      * E.g. |      * | ||||||
|      * startTime({startHour: 9, startMinuts: 15}) == 9.25 |      * // OH.startTime({startHour: 9, startMinutes: 15}) // => 9.25
 | ||||||
|      * @param oh |  | ||||||
|      */ |      */ | ||||||
|     public static startTime(oh: OpeningHour): number { |     public static startTime(oh: OpeningHour): number { | ||||||
|         return oh.startHour + oh.startMinutes / 60 |         return oh.startHour + oh.startMinutes / 60 | ||||||
|  | @ -346,8 +345,8 @@ export class OH { | ||||||
|             const split = rule.trim().replace(/, */g, ",").split(" ") |             const split = rule.trim().replace(/, */g, ",").split(" ") | ||||||
|             if (split.length == 1) { |             if (split.length == 1) { | ||||||
|                 // First, try to parse this rule as a rule without weekdays
 |                 // First, try to parse this rule as a rule without weekdays
 | ||||||
|                 let timeranges = OH.ParseHhmmRanges(rule) |                 const timeranges = OH.ParseHhmmRanges(rule) | ||||||
|                 let weekdays = [0, 1, 2, 3, 4, 5, 6] |                 const weekdays = [0, 1, 2, 3, 4, 5, 6] | ||||||
|                 return OH.multiply(weekdays, timeranges) |                 return OH.multiply(weekdays, timeranges) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -450,7 +449,7 @@ export class OH { | ||||||
|         return ohs |         return ohs | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* |     /** | ||||||
| This function converts a number of ranges (generated by OpeningHours.js) into all the hours of day that a change occurs. | This function converts a number of ranges (generated by OpeningHours.js) into all the hours of day that a change occurs. | ||||||
| E.g. | E.g. | ||||||
| Monday, some business is opened from 9:00 till 17:00 | Monday, some business is opened from 9:00 till 17:00 | ||||||
|  | @ -458,6 +457,11 @@ Tuesday from 9:30 till 18:00 | ||||||
| Wednesday from 9:30 till 12:30 | Wednesday from 9:30 till 12:30 | ||||||
| This function will extract all those moments of change and will return 9:00, 9:30, 12:30, 17:00 and 18:00 | This function will extract all those moments of change and will return 9:00, 9:30, 12:30, 17:00 and 18:00 | ||||||
| This list will be sorted | This list will be sorted | ||||||
|  | 
 | ||||||
|  | const startDate = new Date(2025,4,20,10,0,0) | ||||||
|  | const endDate = new Date(2025,4,20,17,0,0) | ||||||
|  | const changes = OH.allChangeMoments([[{isOpen: true, isSpecial: false, comment: "", startDate, endDate}]]) | ||||||
|  | changes // => [[36000,61200], ["10:00", "17:00"]]
 | ||||||
| */ | */ | ||||||
|     public static allChangeMoments( |     public static allChangeMoments( | ||||||
|         ranges: { |         ranges: { | ||||||
|  | @ -483,8 +487,7 @@ This list will be sorted | ||||||
|                 startOfDay.setHours(0, 0, 0, 0) |                 startOfDay.setHours(0, 0, 0, 0) | ||||||
| 
 | 
 | ||||||
|                 // The number of seconds since the start of the day
 |                 // The number of seconds since the start of the day
 | ||||||
|                 // @ts-ignore
 |                 const changeMoment: number = (range.startDate.getTime() - startOfDay.getTime()) / 1000 | ||||||
|                 const changeMoment: number = (range.startDate - startOfDay) / 1000 |  | ||||||
|                 if (changeHours.indexOf(changeMoment) < 0) { |                 if (changeHours.indexOf(changeMoment) < 0) { | ||||||
|                     changeHours.push(changeMoment) |                     changeHours.push(changeMoment) | ||||||
|                     changeHourText.push( |                     changeHourText.push( | ||||||
|  | @ -493,8 +496,7 @@ This list will be sorted | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 // The number of seconds till between the start of the day and closing
 |                 // The number of seconds till between the start of the day and closing
 | ||||||
|                 // @ts-ignore
 |                 const changeMomentEnd: number = (range.endDate.getTime() - startOfDay.getTime()) / 1000 | ||||||
|                 let changeMomentEnd: number = (range.endDate - startOfDay) / 1000 |  | ||||||
|                 if (changeMomentEnd >= 24 * 60 * 60) { |                 if (changeMomentEnd >= 24 * 60 * 60) { | ||||||
|                     if (extrachangeHours.indexOf(changeMomentEnd) < 0) { |                     if (extrachangeHours.indexOf(changeMomentEnd) < 0) { | ||||||
|                         extrachangeHours.push(changeMomentEnd) |                         extrachangeHours.push(changeMomentEnd) | ||||||
|  | @ -665,13 +667,18 @@ This list will be sorted | ||||||
|      */ |      */ | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Constructs the opening-ranges for either this week, or for next week if there are no more openings this week |      * Constructs the opening-ranges for either this week, or for next week if there are no more openings this week. | ||||||
|  |      * Note: 'today' is mostly used for testing | ||||||
|  |      * | ||||||
|  |      * const oh = new opening_hours("mar 15 - oct 15") | ||||||
|  |      * const ranges = OH.createRangesForApplicableWeek(oh, new Date(2025,4,20,10,0,0)) | ||||||
|  |      * ranges // => {ranges: [[],[],[],[],[],[]], startingMonday: new Date(2025,4,18,10,0,0)}
 | ||||||
|      */ |      */ | ||||||
|     public static createRangesForApplicableWeek(oh: opening_hours): { |     public static createRangesForApplicableWeek(oh: opening_hours, today?: Date): { | ||||||
|         ranges: OpeningRange[][] |         ranges: OpeningRange[][] | ||||||
|         startingMonday: Date |         startingMonday: Date | ||||||
|     } { |     } { | ||||||
|         const today = new Date() |         today ??= new Date() | ||||||
|         today.setHours(0, 0, 0, 0) |         today.setHours(0, 0, 0, 0) | ||||||
|         const lastMonday = OH.getMondayBefore(today) |         const lastMonday = OH.getMondayBefore(today) | ||||||
|         const nextSunday = new Date(lastMonday) |         const nextSunday = new Date(lastMonday) | ||||||
|  | @ -699,7 +706,7 @@ This list will be sorted | ||||||
|     public static weekdaysIdentical(openingRanges: OpeningRange[][], startday = 0, endday = 4) { |     public static weekdaysIdentical(openingRanges: OpeningRange[][], startday = 0, endday = 4) { | ||||||
|         const monday = openingRanges[startday] |         const monday = openingRanges[startday] | ||||||
|         for (let i = startday + 1; i <= endday; i++) { |         for (let i = startday + 1; i <= endday; i++) { | ||||||
|             let weekday = openingRanges[i] |             const weekday = openingRanges[i] | ||||||
|             if (weekday.length !== monday.length) { |             if (weekday.length !== monday.length) { | ||||||
|                 return false |                 return false | ||||||
|             } |             } | ||||||
|  | @ -831,12 +838,12 @@ This list will be sorted | ||||||
|             } |             } | ||||||
|             return [parsed] |             return [parsed] | ||||||
|         } else if (split.length == 2) { |         } else if (split.length == 2) { | ||||||
|             let start = OH.ParseWeekday(split[0]) |             const start = OH.ParseWeekday(split[0]) | ||||||
|             let end = OH.ParseWeekday(split[1]) |             const end = OH.ParseWeekday(split[1]) | ||||||
|             if ((start ?? null) === null || (end ?? null) === null) { |             if ((start ?? null) === null || (end ?? null) === null) { | ||||||
|                 return null |                 return null | ||||||
|             } |             } | ||||||
|             let range = [] |             const range = [] | ||||||
|             for (let i = start; i <= end; i++) { |             for (let i = start; i <= end; i++) { | ||||||
|                 range.push(i) |                 range.push(i) | ||||||
|             } |             } | ||||||
|  | @ -847,8 +854,8 @@ This list will be sorted | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static ParseWeekdayRanges(weekdays: string): number[] { |     private static ParseWeekdayRanges(weekdays: string): number[] { | ||||||
|         let ranges = [] |         const ranges = [] | ||||||
|         let split = weekdays.split(",") |         const split = weekdays.split(",") | ||||||
|         for (const weekday of split) { |         for (const weekday of split) { | ||||||
|             const parsed = OH.ParseWeekdayRange(weekday) |             const parsed = OH.ParseWeekdayRange(weekday) | ||||||
|             if (parsed === undefined || parsed === null) { |             if (parsed === undefined || parsed === null) { | ||||||
|  | @ -1054,7 +1061,7 @@ export class ToTextualDescription { | ||||||
|                 languages[supportedLanguage] = "{a}. {b}" |                 languages[supportedLanguage] = "{a}. {b}" | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         let chainer = new TypedTranslation<{ a; b }>(languages) |         const chainer = new TypedTranslation<{ a; b }>(languages) | ||||||
|         let tr = trs[0] |         let tr = trs[0] | ||||||
|         for (let i = 1; i < trs.length; i++) { |         for (let i = 1; i < trs.length; i++) { | ||||||
|             tr = chainer.PartialSubsTr("a", tr).PartialSubsTr("b", trs[i]) |             tr = chainer.PartialSubsTr("a", tr).PartialSubsTr("b", trs[i]) | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ import BaseUIElement from "../BaseUIElement" | ||||||
| import Toggle from "../Input/Toggle" | import Toggle from "../Input/Toggle" | ||||||
| import { VariableUiElement } from "../Base/VariableUIElement" | import { VariableUiElement } from "../Base/VariableUIElement" | ||||||
| import Table from "../Base/Table" | import Table from "../Base/Table" | ||||||
| import { Translation } from "../i18n/Translation" | import { Translation, TypedTranslation } from "../i18n/Translation" | ||||||
| import Loading from "../Base/Loading" | import Loading from "../Base/Loading" | ||||||
| import opening_hours from "opening_hours" | import opening_hours from "opening_hours" | ||||||
| import Locale from "../i18n/Locale" | import Locale from "../i18n/Locale" | ||||||
|  | @ -73,7 +73,7 @@ export default class OpeningHoursVisualization extends Toggle { | ||||||
|         ranges: OpeningRange[][], |         ranges: OpeningRange[][], | ||||||
|         lastMonday: Date |         lastMonday: Date | ||||||
|     ): BaseUIElement { |     ): BaseUIElement { | ||||||
|         // First, a small sanity check. The business might be permanently closed, 24/7 opened or something other special
 |         // First, a small sanity check. The business might be permanently closed, 24/7 opened or be another special case
 | ||||||
|         if (ranges.some((range) => range.length > 0)) { |         if (ranges.some((range) => range.length > 0)) { | ||||||
|             // The normal case: we have items for the coming days
 |             // The normal case: we have items for the coming days
 | ||||||
|             return OpeningHoursVisualization.ConstructVizTable(oh, ranges, lastMonday) |             return OpeningHoursVisualization.ConstructVizTable(oh, ranges, lastMonday) | ||||||
|  | @ -98,8 +98,7 @@ export default class OpeningHoursVisualization extends Toggle { | ||||||
|         const today = new Date() |         const today = new Date() | ||||||
|         today.setHours(0, 0, 0, 0) |         today.setHours(0, 0, 0, 0) | ||||||
| 
 | 
 | ||||||
|         // @ts-ignore
 |         const todayIndex = Math.ceil((today.getTime() - rangeStart.getTime()) / (1000 * 60 * 60 * 24)) | ||||||
|         const todayIndex = Math.ceil((today - rangeStart) / (1000 * 60 * 60 * 24)) |  | ||||||
|         // By default, we always show the range between 8 - 19h, in order to give a stable impression
 |         // By default, we always show the range between 8 - 19h, in order to give a stable impression
 | ||||||
|         // Ofc, a bigger range is used if needed
 |         // Ofc, a bigger range is used if needed
 | ||||||
|         const earliestOpen = Math.min(8 * 60 * 60, ...changeHours) |         const earliestOpen = Math.min(8 * 60 * 60, ...changeHours) | ||||||
|  | @ -193,11 +192,9 @@ export default class OpeningHoursVisualization extends Toggle { | ||||||
| 
 | 
 | ||||||
|         const startOfDay: Date = new Date(range.startDate) |         const startOfDay: Date = new Date(range.startDate) | ||||||
|         startOfDay.setHours(0, 0, 0, 0) |         startOfDay.setHours(0, 0, 0, 0) | ||||||
|         // @ts-ignore
 |         const startpoint = (range.startDate.getTime() - startOfDay.getTime()) / 1000 - earliestOpen | ||||||
|         const startpoint = (range.startDate - startOfDay) / 1000 - earliestOpen |  | ||||||
|         // prettier-ignore
 |         // prettier-ignore
 | ||||||
|         // @ts-ignore
 |         const width = (100 * (range.endDate.getTime() - range.startDate.getTime()) / 1000) / (latestclose - earliestOpen) | ||||||
|         const width = (100 * (range.endDate - range.startDate) / 1000) / (latestclose - earliestOpen); |  | ||||||
|         const startPercentage = (100 * startpoint) / availableArea |         const startPercentage = (100 * startpoint) / availableArea | ||||||
|         return new FixedUiElement(textToShow) |         return new FixedUiElement(textToShow) | ||||||
|             .SetStyle(`left:${startPercentage}%; width:${width}%`) |             .SetStyle(`left:${startPercentage}%; width:${width}%`) | ||||||
|  | @ -236,7 +233,7 @@ export default class OpeningHoursVisualization extends Toggle { | ||||||
|         changeHourText: string[], |         changeHourText: string[], | ||||||
|         earliestOpen: number |         earliestOpen: number | ||||||
|     ): [BaseUIElement, string] { |     ): [BaseUIElement, string] { | ||||||
|         let header: BaseUIElement[] = [] |         const header: BaseUIElement[] = [] | ||||||
| 
 | 
 | ||||||
|         header.push( |         header.push( | ||||||
|             ...OpeningHoursVisualization.CreateLinesAtChangeHours( |             ...OpeningHoursVisualization.CreateLinesAtChangeHours( | ||||||
|  | @ -249,7 +246,7 @@ export default class OpeningHoursVisualization extends Toggle { | ||||||
|         let showHigher = false |         let showHigher = false | ||||||
|         let showHigherUsed = false |         let showHigherUsed = false | ||||||
|         for (let i = 0; i < changeHours.length; i++) { |         for (let i = 0; i < changeHours.length; i++) { | ||||||
|             let changeMoment = changeHours[i] |             const changeMoment = changeHours[i] | ||||||
|             const offset = (100 * (changeMoment - earliestOpen)) / availableArea |             const offset = (100 * (changeMoment - earliestOpen)) / availableArea | ||||||
|             if (offset < 0 || offset > 100) { |             if (offset < 0 || offset > 100) { | ||||||
|                 continue |                 continue | ||||||
|  | @ -285,21 +282,23 @@ export default class OpeningHoursVisualization extends Toggle { | ||||||
|     /* |     /* | ||||||
|      * Visualizes any special case: e.g. not open for a long time, 24/7 open, ... |      * Visualizes any special case: e.g. not open for a long time, 24/7 open, ... | ||||||
|      * */ |      * */ | ||||||
|     private static ShowSpecialCase(oh: any) { |     private static ShowSpecialCase(oh: opening_hours) { | ||||||
|         const opensAtDate = oh.getNextChange() |         const nextChange = oh.getNextChange() | ||||||
|         if (opensAtDate === undefined) { |         if (nextChange !== undefined) { | ||||||
|             const comm = oh.getComment() ?? oh.getUnknown() |             const nowOpen = oh.getState(new Date()) | ||||||
|             if (!!comm) { |             const t = Translations.t.general.opening_hours | ||||||
|                 return new FixedUiElement(comm) |             const tr: TypedTranslation<{ date }> = nowOpen ? t.open_until : t.closed_until | ||||||
|             } |             const date = nextChange.toLocaleString() | ||||||
| 
 |             return tr.Subs({ date }) | ||||||
|             if (oh.getState()) { |  | ||||||
|                 return Translations.t.general.opening_hours.open_24_7.Clone() |  | ||||||
|             } |  | ||||||
|             return Translations.t.general.opening_hours.closed_permanently.Clone() |  | ||||||
|         } |         } | ||||||
|         return Translations.t.general.opening_hours.closed_until.Subs({ |         const comment = oh.getComment() ?? oh.getUnknown() | ||||||
|             date: opensAtDate.toLocaleString(), |         if (typeof comment === "string") { | ||||||
|         }) |             return new FixedUiElement(comment) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (oh.getState()) { | ||||||
|  |             return Translations.t.general.opening_hours.open_24_7.Clone() | ||||||
|  |         } | ||||||
|  |         return Translations.t.general.opening_hours.closed_permanently.Clone() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue