287 lines
6.7 KiB
TypeScript
287 lines
6.7 KiB
TypeScript
import {
|
|
compareDesc,
|
|
getISODay,
|
|
isAfter,
|
|
isSameDay,
|
|
parseISO,
|
|
startOfToday,
|
|
} from "date-fns";
|
|
|
|
import { getClient } from "@/app/client";
|
|
import { graphql, unmaskFragment } from "@/gql";
|
|
import type {
|
|
OpeningHoursRangeBlockFragment as OpeningHoursRangeBlock,
|
|
OpeningHoursSetFragment as OpeningHoursSet,
|
|
} from "@/gql/graphql";
|
|
|
|
const MISSING_OPENING_HOURS = {
|
|
name: "Åpningstider mangler",
|
|
effectiveFrom: "",
|
|
effectiveTo: null,
|
|
announcement: "Åpningstider mangler",
|
|
items: [],
|
|
};
|
|
|
|
const WEEKDAYS = [
|
|
"monday",
|
|
"tuesday",
|
|
"wednesday",
|
|
"thursday",
|
|
"friday",
|
|
"saturday",
|
|
"sunday",
|
|
];
|
|
const WEEKDAYS_NORWEGIAN = [
|
|
"mandag",
|
|
"tirsdag",
|
|
"onsdag",
|
|
"torsdag",
|
|
"fredag",
|
|
"lørdag",
|
|
"søndag",
|
|
];
|
|
|
|
const openingHoursQuery = graphql(`
|
|
query openingHoursSets {
|
|
openingHoursSets {
|
|
...OpeningHoursSet
|
|
}
|
|
}
|
|
`);
|
|
|
|
export async function fetchOpeningHoursSets() {
|
|
const { data, error } = await getClient().query(openingHoursQuery, {});
|
|
const sets = (data?.openingHoursSets ?? []) as OpeningHoursSet[];
|
|
return sets;
|
|
}
|
|
|
|
export async function getOpeningHours() {
|
|
const today = startOfToday();
|
|
const sets = await fetchOpeningHoursSets();
|
|
const validSets = sets
|
|
.filter((set) => {
|
|
const from = parseISO(set.effectiveFrom);
|
|
return isAfter(today, from) || isSameDay(today, from);
|
|
})
|
|
.filter((set) => {
|
|
if (!set.effectiveTo) {
|
|
return true;
|
|
}
|
|
const to = parseISO(set.effectiveTo);
|
|
return isAfter(to, today) || isSameDay(today, to);
|
|
});
|
|
if (validSets.length === 0) {
|
|
return MISSING_OPENING_HOURS as OpeningHoursSet;
|
|
}
|
|
if (validSets.length === 1) {
|
|
return validSets[0];
|
|
}
|
|
// pick the set that msot recently took effect
|
|
return validSets.sort((a, b) =>
|
|
compareDesc(a.effectiveFrom, b.effectiveFrom),
|
|
)[0];
|
|
}
|
|
|
|
type OpeningHoursGroup = {
|
|
days: string[];
|
|
timeFrom: string | null;
|
|
timeTo: string | null;
|
|
custom: string | null;
|
|
};
|
|
|
|
type OpeningHoursPerDay = Record<string, OpeningHoursRangeBlock>;
|
|
|
|
export function groupOpeningHours(
|
|
week: OpeningHoursPerDay,
|
|
): OpeningHoursGroup[] {
|
|
const grouped: OpeningHoursGroup[] = [];
|
|
let previous: string | null = null;
|
|
|
|
for (const day of WEEKDAYS) {
|
|
if (!week.hasOwnProperty(day)) {
|
|
continue;
|
|
}
|
|
|
|
const hours = week[day];
|
|
if (
|
|
hours === null ||
|
|
previous === null ||
|
|
week[previous]?.timeFrom !== hours.timeFrom ||
|
|
week[previous]?.timeTo !== hours.timeTo ||
|
|
week[previous]?.custom !== hours.custom
|
|
) {
|
|
grouped.push({
|
|
days: [day],
|
|
timeFrom: hours.timeFrom ?? null,
|
|
timeTo: hours.timeTo ?? null,
|
|
custom: hours.custom ?? null,
|
|
});
|
|
} else {
|
|
grouped[grouped.length - 1].days.push(day);
|
|
}
|
|
previous = day;
|
|
}
|
|
return grouped;
|
|
}
|
|
|
|
export type PrettyOpeningHours = {
|
|
range: string;
|
|
time?: string;
|
|
custom?: string;
|
|
};
|
|
|
|
function formatGroupedHours(
|
|
grouped: OpeningHoursGroup[],
|
|
): PrettyOpeningHours[] {
|
|
return grouped.map((group) => {
|
|
const startDayIndex = WEEKDAYS.indexOf(group.days[0]);
|
|
const endDayIndex = WEEKDAYS.indexOf(group.days[group.days.length - 1]);
|
|
|
|
const startDayName = WEEKDAYS_NORWEGIAN[startDayIndex];
|
|
const endDayName =
|
|
group.days.length > 1 ? WEEKDAYS_NORWEGIAN[endDayIndex] : "";
|
|
|
|
const rangeName = startDayName + (endDayName ? "—" + endDayName : "");
|
|
|
|
const formattedRange = {
|
|
range: rangeName,
|
|
...(group.timeFrom && group.timeTo
|
|
? {
|
|
time: `${group.timeFrom.slice(0, 5)}—${group.timeTo.slice(0, 5)}`,
|
|
}
|
|
: {}),
|
|
...(group.custom ? { custom: group.custom } : {}),
|
|
};
|
|
|
|
return formattedRange;
|
|
});
|
|
}
|
|
|
|
export function getOpeningHoursForFunction(
|
|
openingHours: OpeningHoursSet,
|
|
name: string,
|
|
) {
|
|
const item = openingHours.items?.find((x) => x?.function === name);
|
|
if (!item || !Array.isArray(item?.week) || item?.week.length !== 1) {
|
|
return;
|
|
}
|
|
const maskedWeek = item.week[0];
|
|
if (maskedWeek?.__typename !== "OpeningHoursWeekBlock") {
|
|
return;
|
|
}
|
|
const week = unmaskFragment(
|
|
OpeningHoursWeekBlockFragmentDefinition,
|
|
maskedWeek,
|
|
);
|
|
return week;
|
|
}
|
|
|
|
export function getPrettyOpeningHoursForFunction(
|
|
openingHours: OpeningHoursSet,
|
|
name: string,
|
|
) {
|
|
const week = getOpeningHoursForFunction(openingHours, name);
|
|
if (!week) {
|
|
return [];
|
|
}
|
|
// just trying to satisfy the type checker, this is crap
|
|
const perDay: OpeningHoursPerDay = {
|
|
monday: week.monday as OpeningHoursRangeBlock,
|
|
tuesday: week.tuesday as OpeningHoursRangeBlock,
|
|
wednesday: week.wednesday as OpeningHoursRangeBlock,
|
|
thursday: week.thursday as OpeningHoursRangeBlock,
|
|
friday: week.friday as OpeningHoursRangeBlock,
|
|
saturday: week.saturday as OpeningHoursRangeBlock,
|
|
sunday: week.sunday as OpeningHoursRangeBlock,
|
|
};
|
|
const grouped = groupOpeningHours(perDay);
|
|
return formatGroupedHours(grouped);
|
|
}
|
|
|
|
export function getTodaysOpeningHoursForFunction(
|
|
openingHours: OpeningHoursSet,
|
|
name: string,
|
|
): string {
|
|
const week: any = getOpeningHoursForFunction(openingHours, name);
|
|
if (!week) {
|
|
return "?";
|
|
}
|
|
const weekdayIndex = getISODay(startOfToday()) - 1;
|
|
const weekday = WEEKDAYS[weekdayIndex];
|
|
const hours = week[weekday];
|
|
if (hours.timeFrom && hours.timeTo) {
|
|
return `${hours.timeFrom.slice(0, 5)}—${hours.timeTo.slice(0, 5)}`;
|
|
}
|
|
if (hours.custom && hours.custom.length) {
|
|
return hours.custom;
|
|
}
|
|
return "Stengt";
|
|
}
|
|
|
|
const OpeningHoursSetFragmentDefinition = graphql(`
|
|
fragment OpeningHoursSet on OpeningHoursSet {
|
|
name
|
|
effectiveFrom
|
|
effectiveTo
|
|
announcement
|
|
items {
|
|
id
|
|
function
|
|
week {
|
|
__typename
|
|
... on OpeningHoursWeekBlock {
|
|
...OpeningHoursWeekBlock
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`);
|
|
|
|
const OpeningHoursRangeBlockFragmentDefinition = graphql(`
|
|
fragment OpeningHoursRangeBlock on OpeningHoursRangeBlock {
|
|
timeFrom
|
|
timeTo
|
|
custom
|
|
}
|
|
`);
|
|
|
|
export const OpeningHoursWeekBlockFragmentDefinition = graphql(`
|
|
fragment OpeningHoursWeekBlock on OpeningHoursWeekBlock {
|
|
monday {
|
|
... on OpeningHoursRangeBlock {
|
|
...OpeningHoursRangeBlock
|
|
}
|
|
}
|
|
tuesday {
|
|
... on OpeningHoursRangeBlock {
|
|
...OpeningHoursRangeBlock
|
|
}
|
|
}
|
|
wednesday {
|
|
... on OpeningHoursRangeBlock {
|
|
...OpeningHoursRangeBlock
|
|
}
|
|
}
|
|
thursday {
|
|
... on OpeningHoursRangeBlock {
|
|
...OpeningHoursRangeBlock
|
|
}
|
|
}
|
|
friday {
|
|
... on OpeningHoursRangeBlock {
|
|
...OpeningHoursRangeBlock
|
|
}
|
|
}
|
|
saturday {
|
|
... on OpeningHoursRangeBlock {
|
|
...OpeningHoursRangeBlock
|
|
}
|
|
}
|
|
sunday {
|
|
... on OpeningHoursRangeBlock {
|
|
...OpeningHoursRangeBlock
|
|
}
|
|
}
|
|
}
|
|
`);
|