add support for opening hours that automatically change over time
This commit is contained in:
@ -169,6 +169,13 @@ class NeufAddressSectionBlock(blocks.StaticBlock):
|
|||||||
admin_text = "Viser adressen og Google-kart."
|
admin_text = "Viser adressen og Google-kart."
|
||||||
|
|
||||||
|
|
||||||
|
class OpeningHoursSectionBlock(blocks.StaticBlock):
|
||||||
|
class Meta:
|
||||||
|
icon = "time"
|
||||||
|
label = "Chateau Neuf: Åpningstider"
|
||||||
|
admin_text = "Viser gjeldende åpningstider."
|
||||||
|
|
||||||
|
|
||||||
BASE_BLOCKS = [
|
BASE_BLOCKS = [
|
||||||
("paragraph", blocks.RichTextBlock(label="Rik tekst")),
|
("paragraph", blocks.RichTextBlock(label="Rik tekst")),
|
||||||
("image", ImageWithTextBlock(label="Bilde")),
|
("image", ImageWithTextBlock(label="Bilde")),
|
||||||
@ -199,6 +206,7 @@ class PageSectionBlock(blocks.StructBlock):
|
|||||||
[block for block in BASE_BLOCKS if block[0] != "page_section_navigation"]
|
[block for block in BASE_BLOCKS if block[0] != "page_section_navigation"]
|
||||||
+ [
|
+ [
|
||||||
("neuf_address", NeufAddressSectionBlock()),
|
("neuf_address", NeufAddressSectionBlock()),
|
||||||
|
("opening_hours", OpeningHoursSectionBlock()),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ INSTALLED_APPS = [
|
|||||||
"events",
|
"events",
|
||||||
"venues",
|
"venues",
|
||||||
"news",
|
"news",
|
||||||
|
"openinghours",
|
||||||
# end cms apps
|
# end cms apps
|
||||||
"grapple",
|
"grapple",
|
||||||
"graphene_django",
|
"graphene_django",
|
||||||
@ -51,6 +52,7 @@ INSTALLED_APPS = [
|
|||||||
"wagtail",
|
"wagtail",
|
||||||
"modelcluster",
|
"modelcluster",
|
||||||
"taggit",
|
"taggit",
|
||||||
|
"django_extensions",
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
"django.contrib.auth",
|
"django.contrib.auth",
|
||||||
"django.contrib.contenttypes",
|
"django.contrib.contenttypes",
|
||||||
@ -195,6 +197,7 @@ GRAPPLE = {
|
|||||||
"events",
|
"events",
|
||||||
"venues",
|
"venues",
|
||||||
"news",
|
"news",
|
||||||
|
"openinghours",
|
||||||
],
|
],
|
||||||
"EXPOSE_GRAPHIQL": True,
|
"EXPOSE_GRAPHIQL": True,
|
||||||
}
|
}
|
||||||
|
22
dnscms/generic/migrations/0016_alter_genericpage_body.py
Normal file
22
dnscms/generic/migrations/0016_alter_genericpage_body.py
Normal file
File diff suppressed because one or more lines are too long
0
dnscms/openinghours/__init__.py
Normal file
0
dnscms/openinghours/__init__.py
Normal file
6
dnscms/openinghours/apps.py
Normal file
6
dnscms/openinghours/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class OpeninghoursConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "openinghours"
|
47
dnscms/openinghours/migrations/0001_initial.py
Normal file
47
dnscms/openinghours/migrations/0001_initial.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2024-07-07 19:27
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import modelcluster.fields
|
||||||
|
import wagtail.blocks
|
||||||
|
import wagtail.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OpeningHoursSet',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(help_text='Enkel beskrivelse av hvilken periode disse åpningstidene dekker, f.eks. <em>Vanlige åpningstider</em> eller <em>Sommer 2032</em>.', max_length=256)),
|
||||||
|
('effective_from', models.DateField()),
|
||||||
|
('effective_to', models.DateField(blank=True, null=True)),
|
||||||
|
('announcement', models.TextField(blank=True, help_text='En kort notis som vil vises i forbindelse med åpningstidene, f.eks. <em>Huset er stengt i perioden A til B.</em>. Kan være blank.')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Opening hours set',
|
||||||
|
'verbose_name_plural': 'Opening hours sets',
|
||||||
|
'ordering': ['effective_from'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OpeningHoursItem',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
|
||||||
|
('function', models.CharField(choices=[('glassbaren', 'Glassbaren'), ('bokcafeen', 'Bokcaféen'), ('ekspedisjonen', 'Ekspedisjonen')], default='', help_text='Hvilket lokale/funksjon?', max_length=32)),
|
||||||
|
('week', wagtail.fields.StreamField([('monday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Mandag')), ('tuesday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Tirsdag')), ('wednesday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Onsdag')), ('thursday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Torsdag')), ('friday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Fredag')), ('saturday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Lørdag')), ('sunday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Søndag'))])),
|
||||||
|
('opening_hours_set', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='openinghours.openinghoursset')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['sort_order'],
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2024-07-07 19:34
|
||||||
|
|
||||||
|
import wagtail.blocks
|
||||||
|
import wagtail.fields
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('openinghours', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='openinghoursitem',
|
||||||
|
name='week',
|
||||||
|
field=wagtail.fields.StreamField([('days', wagtail.blocks.StructBlock([('monday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Mandag')), ('tuesday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Tirsdag')), ('wednesday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Onsdag')), ('thursday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Torsdag')), ('friday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Fredag')), ('saturday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Lørdag')), ('sunday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Søndag'))]))]),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2024-07-08 00:21
|
||||||
|
|
||||||
|
import wagtail.blocks
|
||||||
|
import wagtail.fields
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('openinghours', '0002_alter_openinghoursitem_week'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='openinghoursitem',
|
||||||
|
name='week',
|
||||||
|
field=wagtail.fields.StreamField([('week', wagtail.blocks.StructBlock([('monday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Mandag')), ('tuesday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Tirsdag')), ('wednesday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Onsdag')), ('thursday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Torsdag')), ('friday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Fredag')), ('saturday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Lørdag')), ('sunday', wagtail.blocks.StructBlock([('time_from', wagtail.blocks.TimeBlock(label='Åpner', required=False)), ('time_to', wagtail.blocks.TimeBlock(label='Stenger', required=False)), ('custom', wagtail.blocks.CharBlock(help_text='Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>', label='Fritekst', required=False))], label='Søndag'))]))]),
|
||||||
|
),
|
||||||
|
]
|
0
dnscms/openinghours/migrations/__init__.py
Normal file
0
dnscms/openinghours/migrations/__init__.py
Normal file
149
dnscms/openinghours/models.py
Normal file
149
dnscms/openinghours/models.py
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from grapple.helpers import register_query_field, register_streamfield_block
|
||||||
|
from grapple.models import GraphQLForeignKey, GraphQLStreamfield, GraphQLString
|
||||||
|
from modelcluster.fields import ParentalKey
|
||||||
|
from modelcluster.models import ClusterableModel
|
||||||
|
from wagtail import blocks
|
||||||
|
from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel, TitleFieldPanel
|
||||||
|
from wagtail.models import Orderable, StreamField
|
||||||
|
from wagtail.snippets.models import register_snippet
|
||||||
|
|
||||||
|
|
||||||
|
@register_streamfield_block
|
||||||
|
class OpeningHoursRangeBlock(blocks.StructBlock):
|
||||||
|
# closed = blocks.BooleanBlock(required=False, help_text="Kryss av her om lokalet er stengt.")
|
||||||
|
# blocks.RegexBlock(regex=r'^\d\d:\d\d$', error_messages={'invalid': 'Må være på formatet HH:MM'}, help_text="Tidspunkt på formatet HH:MM")
|
||||||
|
time_from = blocks.TimeBlock(required=False, label="Åpner")
|
||||||
|
time_to = blocks.TimeBlock(required=False, label="Stenger")
|
||||||
|
custom = blocks.CharBlock(
|
||||||
|
required=False,
|
||||||
|
label="Fritekst",
|
||||||
|
help_text=mark_safe(
|
||||||
|
"Tekst som vises istedenfor tidspunkter. F.eks. <em>Åpent ved arrangement</em>"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
graphql_fields = [
|
||||||
|
GraphQLString("time_from", required=False),
|
||||||
|
GraphQLString("time_to", required=False),
|
||||||
|
GraphQLString("custom", required=False),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@register_streamfield_block
|
||||||
|
class OpeningHoursWeekBlock(blocks.StructBlock):
|
||||||
|
monday = OpeningHoursRangeBlock(label="Mandag")
|
||||||
|
tuesday = OpeningHoursRangeBlock(label="Tirsdag")
|
||||||
|
wednesday = OpeningHoursRangeBlock(label="Onsdag")
|
||||||
|
thursday = OpeningHoursRangeBlock(label="Torsdag")
|
||||||
|
friday = OpeningHoursRangeBlock(label="Fredag")
|
||||||
|
saturday = OpeningHoursRangeBlock(label="Lørdag")
|
||||||
|
sunday = OpeningHoursRangeBlock(label="Søndag")
|
||||||
|
|
||||||
|
graphql_fields = [
|
||||||
|
GraphQLStreamfield("monday", required=False, is_list=False),
|
||||||
|
GraphQLStreamfield("tuesday", required=False, is_list=False),
|
||||||
|
GraphQLStreamfield("wednesday", required=False, is_list=False),
|
||||||
|
GraphQLStreamfield("thursday", required=False, is_list=False),
|
||||||
|
GraphQLStreamfield("friday", required=False, is_list=False),
|
||||||
|
GraphQLStreamfield("saturday", required=False, is_list=False),
|
||||||
|
GraphQLStreamfield("sunday", required=False, is_list=False),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OpeningHoursItem(Orderable):
|
||||||
|
FUNCTION_CHOICES = [
|
||||||
|
("glassbaren", "Glassbaren"),
|
||||||
|
("bokcafeen", "Bokcaféen"),
|
||||||
|
("ekspedisjonen", "Ekspedisjonen"),
|
||||||
|
]
|
||||||
|
WEEKDAYS = (
|
||||||
|
("monday", "Mandag"),
|
||||||
|
("tuesday", "Tirsdag"),
|
||||||
|
("wednesday", "Onsdag"),
|
||||||
|
("thursday", "Torsdag"),
|
||||||
|
("friday", "Fredag"),
|
||||||
|
("saturday", "Lørdag"),
|
||||||
|
("sunday", "Søndag"),
|
||||||
|
)
|
||||||
|
|
||||||
|
opening_hours_set = ParentalKey(
|
||||||
|
"openinghours.OpeningHoursSet", on_delete=models.CASCADE, related_name="items"
|
||||||
|
)
|
||||||
|
function = models.CharField(
|
||||||
|
max_length=32,
|
||||||
|
default="",
|
||||||
|
choices=FUNCTION_CHOICES,
|
||||||
|
blank=False,
|
||||||
|
help_text="Hvilket lokale/funksjon?",
|
||||||
|
)
|
||||||
|
|
||||||
|
week = StreamField(
|
||||||
|
[("week", OpeningHoursWeekBlock())],
|
||||||
|
min_num=1,
|
||||||
|
max_num=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
panels = [
|
||||||
|
FieldPanel("function"),
|
||||||
|
FieldPanel("week"),
|
||||||
|
]
|
||||||
|
|
||||||
|
graphql_fields = [
|
||||||
|
GraphQLString("function", required=True),
|
||||||
|
GraphQLStreamfield("week", required=False),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@register_snippet
|
||||||
|
@register_query_field("openingHoursSet", "openingHoursSets")
|
||||||
|
class OpeningHoursSet(ClusterableModel):
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=256,
|
||||||
|
null=False,
|
||||||
|
blank=False,
|
||||||
|
help_text=mark_safe(
|
||||||
|
"Enkel beskrivelse av hvilken periode disse åpningstidene dekker, "
|
||||||
|
"f.eks. <em>Vanlige åpningstider</em> eller <em>Sommer 2032</em>."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
effective_from = models.DateField(null=False, blank=False)
|
||||||
|
effective_to = models.DateField(null=True, blank=True)
|
||||||
|
|
||||||
|
announcement = models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text=mark_safe(
|
||||||
|
"En kort notis som vil vises i forbindelse med åpningstidene, "
|
||||||
|
"f.eks. <em>Huset er stengt i perioden A til B.</em>. Kan være blank."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
panels = [
|
||||||
|
TitleFieldPanel("name"),
|
||||||
|
FieldPanel("announcement"),
|
||||||
|
MultiFieldPanel(
|
||||||
|
heading="Gyldighet",
|
||||||
|
children=[
|
||||||
|
FieldPanel("effective_from", heading="Fra"),
|
||||||
|
FieldPanel("effective_to", heading="Til"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
InlinePanel("items", label="Lokaler/funksjoner"),
|
||||||
|
]
|
||||||
|
|
||||||
|
graphql_fields = [
|
||||||
|
GraphQLString("name", required=True),
|
||||||
|
GraphQLString("announcement", required=False),
|
||||||
|
GraphQLString("effective_from", required=True),
|
||||||
|
GraphQLString("effective_to", required=False),
|
||||||
|
GraphQLForeignKey("items", "openinghours.OpeningHoursItem", required=False, is_list=True),
|
||||||
|
]
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Opening hours set"
|
||||||
|
verbose_name_plural = "Opening hours sets"
|
||||||
|
ordering = ["effective_from"]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name} ({self.effective_from} - {self.effective_to or '∞'})"
|
3
dnscms/openinghours/tests.py
Normal file
3
dnscms/openinghours/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
3
dnscms/openinghours/views.py
Normal file
3
dnscms/openinghours/views.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
16
dnscms/poetry.lock
generated
16
dnscms/poetry.lock
generated
@ -201,6 +201,20 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
|||||||
argon2 = ["argon2-cffi (>=19.1.0)"]
|
argon2 = ["argon2-cffi (>=19.1.0)"]
|
||||||
bcrypt = ["bcrypt"]
|
bcrypt = ["bcrypt"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-extensions"
|
||||||
|
version = "3.2.3"
|
||||||
|
description = "Extensions for Django"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"},
|
||||||
|
{file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
Django = ">=3.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-filter"
|
name = "django-filter"
|
||||||
version = "24.2"
|
version = "24.2"
|
||||||
@ -960,4 +974,4 @@ wand = ["Wand (>=0.6,<1.0)"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "24cb4c360211d1c02576389f51d3ba863b0da6ef2800e39e716ff4bf1f8f8a16"
|
content-hash = "2e3e69272f994e66fb5e9e8b50e9925e99a11e0ca45d6a60814e499c3bdd9229"
|
||||||
|
@ -11,6 +11,7 @@ wagtail = "^6.0.1"
|
|||||||
django = "^5.0.4"
|
django = "^5.0.4"
|
||||||
wagtail-grapple = "^0.25.1"
|
wagtail-grapple = "^0.25.1"
|
||||||
psycopg2-binary = "^2.9.9"
|
psycopg2-binary = "^2.9.9"
|
||||||
|
django-extensions = "^3.2.3"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
ruff = "*"
|
ruff = "*"
|
||||||
|
@ -8,6 +8,8 @@ import { ContactSectionBlock, ContactSubsectionBlock } from "./ContactSection";
|
|||||||
import { ContactListBlock } from "./ContactListBlock";
|
import { ContactListBlock } from "./ContactListBlock";
|
||||||
import { ContactEntityBlock } from "./ContactEntityBlock";
|
import { ContactEntityBlock } from "./ContactEntityBlock";
|
||||||
import { NeufAddressSectionBlock } from "./NeufAddressSectionBlock";
|
import { NeufAddressSectionBlock } from "./NeufAddressSectionBlock";
|
||||||
|
import { OpeningHoursSectionBlock } from "./OpeningHoursSectionBlock";
|
||||||
|
|
||||||
|
|
||||||
export const Blocks = ({ blocks }: any) => {
|
export const Blocks = ({ blocks }: any) => {
|
||||||
const sections = blocks.filter(
|
const sections = blocks.filter(
|
||||||
@ -52,6 +54,9 @@ export const Blocks = ({ blocks }: any) => {
|
|||||||
case "NeufAddressSectionBlock":
|
case "NeufAddressSectionBlock":
|
||||||
return <NeufAddressSectionBlock />;
|
return <NeufAddressSectionBlock />;
|
||||||
break;
|
break;
|
||||||
|
case "OpeningHoursSectionBlock":
|
||||||
|
return <OpeningHoursSectionBlock />;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return <div>Unsupported block type {block.blockType}</div>;
|
return <div>Unsupported block type {block.blockType}</div>;
|
||||||
console.log("unsupported block", block);
|
console.log("unsupported block", block);
|
||||||
|
66
web/src/components/blocks/OpeningHoursSectionBlock.tsx
Normal file
66
web/src/components/blocks/OpeningHoursSectionBlock.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import {
|
||||||
|
getOpeningHours,
|
||||||
|
getOpeningHoursForFunction,
|
||||||
|
getPrettyOpeningHoursForFunction,
|
||||||
|
groupOpeningHours,
|
||||||
|
PrettyOpeningHours,
|
||||||
|
} from "@/lib/openinghours";
|
||||||
|
import styles from "./openingHoursSectionBlock.module.scss";
|
||||||
|
|
||||||
|
function OpeningHoursSubsection({
|
||||||
|
title,
|
||||||
|
prettyHours,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
prettyHours: PrettyOpeningHours[];
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<section className={styles.openingHoursSubsection}>
|
||||||
|
<h3>{title}</h3>
|
||||||
|
<ul>
|
||||||
|
{prettyHours.map(({ range, time, custom }) => (
|
||||||
|
<li key={range}>
|
||||||
|
<span className={styles.dayRange}>{range}</span>:
|
||||||
|
{time && <span className={styles.timeRange}>{time}</span>}
|
||||||
|
{custom && <span className={styles.timeRange}>{custom}</span>}
|
||||||
|
{!time && !custom && <span className={styles.closed}>Stengt</span>}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function OpeningHoursSectionBlock() {
|
||||||
|
const allOpeningHours = await getOpeningHours();
|
||||||
|
const subsections = [
|
||||||
|
["glassbaren", "Glassbaren"],
|
||||||
|
["bokcafeen", "Bokcaféen"],
|
||||||
|
["ekspedisjonen", "Ekspedisjonen"],
|
||||||
|
];
|
||||||
|
const { announcement } = allOpeningHours;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.openingHoursSection}>
|
||||||
|
{announcement && <p>{announcement}</p>}
|
||||||
|
{subsections.map((subsection) => {
|
||||||
|
const [slug, title] = subsection;
|
||||||
|
const prettyHours = getPrettyOpeningHoursForFunction(
|
||||||
|
allOpeningHours,
|
||||||
|
slug
|
||||||
|
);
|
||||||
|
console.log("prettyHours", prettyHours, slug);
|
||||||
|
if (!prettyHours || prettyHours?.length === 0) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<OpeningHoursSubsection
|
||||||
|
key={slug}
|
||||||
|
title={title}
|
||||||
|
prettyHours={prettyHours}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
@ -5,8 +5,9 @@ import {
|
|||||||
formatDate,
|
formatDate,
|
||||||
formatExtendedDateTime,
|
formatExtendedDateTime,
|
||||||
isTodayOrFuture,
|
isTodayOrFuture,
|
||||||
|
compareDates,
|
||||||
} from "@/lib/date";
|
} from "@/lib/date";
|
||||||
import { EventFragment, EventOccurrence, compareDates } from "@/lib/event";
|
import { EventFragment, EventOccurrence } from "@/lib/event";
|
||||||
import styles from "./dateList.module.scss";
|
import styles from "./dateList.module.scss";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
@ -42,6 +42,10 @@ const documents = {
|
|||||||
"\n fragment News on NewsPage {\n __typename\n id\n slug\n title\n firstPublishedAt\n excerpt\n featuredImage {\n ...Image\n }\n body {\n ...Blocks\n }\n }\n": types.NewsFragmentDoc,
|
"\n fragment News on NewsPage {\n __typename\n id\n slug\n title\n firstPublishedAt\n excerpt\n featuredImage {\n ...Image\n }\n body {\n ...Blocks\n }\n }\n": types.NewsFragmentDoc,
|
||||||
"\n fragment NewsIndex on NewsIndex {\n __typename\n id\n slug\n title\n lead\n }\n": types.NewsIndexFragmentDoc,
|
"\n fragment NewsIndex on NewsIndex {\n __typename\n id\n slug\n title\n lead\n }\n": types.NewsIndexFragmentDoc,
|
||||||
"\n query news {\n index: newsIndex {\n ... on NewsIndex {\n ...NewsIndex\n }\n }\n news: pages(contentType: \"news.NewsPage\") {\n ... on NewsPage {\n ...News\n }\n }\n }\n": types.NewsDocument,
|
"\n query news {\n index: newsIndex {\n ... on NewsIndex {\n ...NewsIndex\n }\n }\n news: pages(contentType: \"news.NewsPage\") {\n ... on NewsPage {\n ...News\n }\n }\n }\n": types.NewsDocument,
|
||||||
|
"\n query openingHoursSets {\n openingHoursSets {\n ...OpeningHoursSetFragment\n }\n }\n": types.OpeningHoursSetsDocument,
|
||||||
|
"\n fragment OpeningHoursSetFragment on OpeningHoursSet {\n name\n effectiveFrom\n effectiveTo\n announcement\n items {\n id\n function\n week {\n id\n blockType\n ... on OpeningHoursWeekBlock {\n ...OpeningHoursWeekBlock\n }\n }\n }\n }\n": types.OpeningHoursSetFragmentFragmentDoc,
|
||||||
|
"\n fragment OpeningHoursRangeBlock on OpeningHoursRangeBlock {\n timeFrom\n timeTo\n custom\n }\n": types.OpeningHoursRangeBlockFragmentDoc,
|
||||||
|
"\n fragment OpeningHoursWeekBlock on OpeningHoursWeekBlock {\n monday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n tuesday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n wednesday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n thursday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n friday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n saturday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n sunday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n }\n": types.OpeningHoursWeekBlockFragmentDoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -174,6 +178,22 @@ export function graphql(source: "\n fragment NewsIndex on NewsIndex {\n __ty
|
|||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query news {\n index: newsIndex {\n ... on NewsIndex {\n ...NewsIndex\n }\n }\n news: pages(contentType: \"news.NewsPage\") {\n ... on NewsPage {\n ...News\n }\n }\n }\n"): (typeof documents)["\n query news {\n index: newsIndex {\n ... on NewsIndex {\n ...NewsIndex\n }\n }\n news: pages(contentType: \"news.NewsPage\") {\n ... on NewsPage {\n ...News\n }\n }\n }\n"];
|
export function graphql(source: "\n query news {\n index: newsIndex {\n ... on NewsIndex {\n ...NewsIndex\n }\n }\n news: pages(contentType: \"news.NewsPage\") {\n ... on NewsPage {\n ...News\n }\n }\n }\n"): (typeof documents)["\n query news {\n index: newsIndex {\n ... on NewsIndex {\n ...NewsIndex\n }\n }\n news: pages(contentType: \"news.NewsPage\") {\n ... on NewsPage {\n ...News\n }\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n query openingHoursSets {\n openingHoursSets {\n ...OpeningHoursSetFragment\n }\n }\n"): (typeof documents)["\n query openingHoursSets {\n openingHoursSets {\n ...OpeningHoursSetFragment\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n fragment OpeningHoursSetFragment on OpeningHoursSet {\n name\n effectiveFrom\n effectiveTo\n announcement\n items {\n id\n function\n week {\n id\n blockType\n ... on OpeningHoursWeekBlock {\n ...OpeningHoursWeekBlock\n }\n }\n }\n }\n"): (typeof documents)["\n fragment OpeningHoursSetFragment on OpeningHoursSet {\n name\n effectiveFrom\n effectiveTo\n announcement\n items {\n id\n function\n week {\n id\n blockType\n ... on OpeningHoursWeekBlock {\n ...OpeningHoursWeekBlock\n }\n }\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n fragment OpeningHoursRangeBlock on OpeningHoursRangeBlock {\n timeFrom\n timeTo\n custom\n }\n"): (typeof documents)["\n fragment OpeningHoursRangeBlock on OpeningHoursRangeBlock {\n timeFrom\n timeTo\n custom\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n fragment OpeningHoursWeekBlock on OpeningHoursWeekBlock {\n monday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n tuesday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n wednesday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n thursday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n friday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n saturday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n sunday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n }\n"): (typeof documents)["\n fragment OpeningHoursWeekBlock on OpeningHoursWeekBlock {\n monday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n tuesday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n wednesday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n thursday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n friday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n saturday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n sunday {\n ... on OpeningHoursRangeBlock {\n ...OpeningHoursRangeBlock\n }\n }\n }\n"];
|
||||||
|
|
||||||
export function graphql(source: string) {
|
export function graphql(source: string) {
|
||||||
return (documents as any)[source] ?? {};
|
return (documents as any)[source] ?? {};
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
|||||||
import { isToday, isAfter, parse } from "date-fns";
|
import { isToday, isAfter, parse, compareAsc } from "date-fns";
|
||||||
import { nb } from "date-fns/locale";
|
import { nb } from "date-fns/locale";
|
||||||
import { toZonedTime, format } from "date-fns-tz";
|
import { toZonedTime, format } from "date-fns-tz";
|
||||||
|
|
||||||
@ -47,3 +47,7 @@ export function isTodayOrFuture(
|
|||||||
const zonedDate = toLocalTime(date);
|
const zonedDate = toLocalTime(date);
|
||||||
return isToday(zonedDate) || isAfter(zonedDate, zonedNow);
|
return isToday(zonedDate) || isAfter(zonedDate, zonedNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function compareDates(a: Date | string, b: Date | string) {
|
||||||
|
return compareAsc(new Date(a), new Date(b));
|
||||||
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
compareAsc,
|
|
||||||
endOfWeek,
|
endOfWeek,
|
||||||
startOfToday,
|
startOfToday,
|
||||||
startOfWeek,
|
startOfWeek,
|
||||||
@ -8,10 +7,10 @@ import {
|
|||||||
addWeeks,
|
addWeeks,
|
||||||
parseISO,
|
parseISO,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import { toLocalTime, formatDate } from "./date";
|
import { toLocalTime, formatDate, compareDates } from "./date";
|
||||||
import { graphql } from "@/gql";
|
import { graphql } from "@/gql";
|
||||||
import { EventFragment, EventCategory, EventOccurrence } from "@/gql/graphql";
|
import { EventFragment, EventOccurrence } from "@/gql/graphql";
|
||||||
import { PIG_NAMES, PigName, randomElement } from "@/lib/common";
|
import { PIG_NAMES, randomElement } from "@/lib/common";
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
EventFragment,
|
EventFragment,
|
||||||
@ -134,10 +133,6 @@ export function getSingularEvents(events: EventFragment[]): SingularEvent[] {
|
|||||||
.flat();
|
.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compareDates(a: Date | string, b: Date | string) {
|
|
||||||
return compareAsc(new Date(a), new Date(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sortSingularEvents(events: SingularEvent[]) {
|
export function sortSingularEvents(events: SingularEvent[]) {
|
||||||
return events.sort((a, b) =>
|
return events.sort((a, b) =>
|
||||||
compareDates(a.occurrence.start, b.occurrence.start)
|
compareDates(a.occurrence.start, b.occurrence.start)
|
||||||
|
258
web/src/lib/openinghours.ts
Normal file
258
web/src/lib/openinghours.ts
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
import {
|
||||||
|
startOfToday,
|
||||||
|
isAfter,
|
||||||
|
parseISO,
|
||||||
|
isSameDay,
|
||||||
|
compareDesc,
|
||||||
|
} from "date-fns";
|
||||||
|
|
||||||
|
import { graphql } from "@/gql";
|
||||||
|
import {
|
||||||
|
OpeningHoursRangeBlock,
|
||||||
|
OpeningHoursSet,
|
||||||
|
OpeningHoursWeekBlock,
|
||||||
|
} from "@/gql/graphql";
|
||||||
|
import { getClient } from "@/app/client";
|
||||||
|
|
||||||
|
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",
|
||||||
|
"øørdag",
|
||||||
|
"søndag",
|
||||||
|
];
|
||||||
|
|
||||||
|
const openingHoursQuery = graphql(`
|
||||||
|
query openingHoursSets {
|
||||||
|
openingHoursSets {
|
||||||
|
...OpeningHoursSetFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
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 week = item.week[0] as OpeningHoursWeekBlock;
|
||||||
|
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.friday as OpeningHoursRangeBlock,
|
||||||
|
sunday: week.friday as OpeningHoursRangeBlock,
|
||||||
|
}
|
||||||
|
const grouped = groupOpeningHours(perDay);
|
||||||
|
return formatGroupedHours(grouped);
|
||||||
|
}
|
||||||
|
|
||||||
|
const OpeningHoursSetFragmentDefinition = graphql(`
|
||||||
|
fragment OpeningHoursSetFragment on OpeningHoursSet {
|
||||||
|
name
|
||||||
|
effectiveFrom
|
||||||
|
effectiveTo
|
||||||
|
announcement
|
||||||
|
items {
|
||||||
|
id
|
||||||
|
function
|
||||||
|
week {
|
||||||
|
id
|
||||||
|
blockType
|
||||||
|
... on OpeningHoursWeekBlock {
|
||||||
|
...OpeningHoursWeekBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const OpeningHoursRangeBlockFragmentDefinition = graphql(`
|
||||||
|
fragment OpeningHoursRangeBlock on OpeningHoursRangeBlock {
|
||||||
|
timeFrom
|
||||||
|
timeTo
|
||||||
|
custom
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
Reference in New Issue
Block a user