Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f354972d9 | |||
| 6a9fff8917 | |||
| 1073adacbb | |||
| 7e114adcc3 | |||
| 0e074b5f1f | |||
| e960da6f1c | |||
| a5ebb897f1 | |||
| f91c67f526 | |||
| 0c5a9876d6 | |||
| cf945d8647 | |||
| 0e5f9f7769 | |||
| 10c8ce194c | |||
| 509a50c321 | |||
| 843062bb13 | |||
| f7e0200a0a | |||
| b09ce9808d | |||
| bc8642b1fc | |||
| 2155a149e8 | |||
| 676e58c361 | |||
| a9e59b947a | |||
| 202cfe47f3 | |||
| d0a886a4ae | |||
| cc5b53011d | |||
| f769898698 | |||
| e71db3a0cc | |||
| 355c0b38a5 | |||
| bba98fe868 | |||
| 978aae4fc3 | |||
| ffc3a583b2 | |||
| 12aea04779 | |||
|
6bcf9bbfbd
|
|||
|
2e4ca34f5c
|
|||
|
d76b16781d
|
|||
| 8942bcc9da | |||
| 196f000a2d | |||
| 4655f67a9e | |||
| 8506fd1c2d | |||
| ca341f5f22 | |||
| 1431b8d6ff | |||
| 3c225aa68a | |||
| 16629a2fc0 | |||
| b18f9ec54f | |||
| 2a00d21717 | |||
| 09e69a7093 | |||
| d3f8b8f0bb | |||
| fcd5231c28 |
@@ -0,0 +1,36 @@
|
|||||||
|
# neuf-www
|
||||||
|
|
||||||
|
The neuf.no website. Wagtail CMS backend (`dnscms/`) feeding a Next.js frontend (`web/`) over GraphQL.
|
||||||
|
|
||||||
|
Tools are managed by [mise](https://mise.jdx.dev/). Run `mise install` to get python, uv, node, and prek.
|
||||||
|
|
||||||
|
## Backend (`dnscms/`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd dnscms
|
||||||
|
uv sync
|
||||||
|
uv run ./manage.py migrate
|
||||||
|
uv run ./manage.py runserver
|
||||||
|
uv run pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
GraphQL endpoint: <http://127.0.0.1:8000/api/graphql/>.
|
||||||
|
|
||||||
|
## Frontend (`web/`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd web
|
||||||
|
npm install
|
||||||
|
npm run dev # http://localhost:3000
|
||||||
|
npm run codegen # regenerate GraphQL types (needs the backend running)
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pre-commit hooks
|
||||||
|
|
||||||
|
[prek](https://github.com/j178/prek) runs ruff lint + format on `dnscms/**/*.py` plus a few sanity hooks. Hooks are configured in [prek.toml](prek.toml).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
prek install # registers the git hook
|
||||||
|
prek run --all-files # run on everything
|
||||||
|
```
|
||||||
@@ -9,6 +9,7 @@ __pycache__
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.swp
|
*.swp
|
||||||
/venv/
|
/venv/
|
||||||
|
/.venv/
|
||||||
/tmp/
|
/tmp/
|
||||||
/.vagrant/
|
/.vagrant/
|
||||||
/Vagrantfile.local
|
/Vagrantfile.local
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.swp
|
*.swp
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.coverage
|
||||||
/venv/
|
/venv/
|
||||||
/.venv/
|
/.venv/
|
||||||
/static/
|
/static/
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.12-slim-bullseye
|
FROM python:3.14-slim-trixie
|
||||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||||
|
|
||||||
RUN useradd wagtail
|
RUN useradd wagtail
|
||||||
@@ -37,4 +37,4 @@ USER wagtail
|
|||||||
ENV PATH="/app/.venv/bin:$PATH"
|
ENV PATH="/app/.venv/bin:$PATH"
|
||||||
RUN python manage.py collectstatic --noinput --clear
|
RUN python manage.py collectstatic --noinput --clear
|
||||||
|
|
||||||
CMD ["sh", "-c", "set -xe; python manage.py migrate --noinput && gunicorn dnscms.wsgi:application"]
|
CMD ["sh", "-c", "set -xe; python manage.py migrate --noinput && gunicorn dnscms.wsgi:application --workers 3"]
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ from wagtail.admin.panels import FieldPanel
|
|||||||
from wagtail.fields import RichTextField
|
from wagtail.fields import RichTextField
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
from wagtail.search import index
|
from wagtail.search import index
|
||||||
|
from wagtail_headless_preview.models import HeadlessMixin
|
||||||
|
|
||||||
from dnscms.fields import CommonStreamField
|
from dnscms.fields import CommonStreamField
|
||||||
from dnscms.wordpress.models import WPImportedPageMixin
|
from dnscms.wordpress.models import WPImportedPageMixin
|
||||||
|
|
||||||
|
|
||||||
@register_singular_query_field("associationIndex")
|
@register_singular_query_field("associationIndex")
|
||||||
class AssociationIndex(Page):
|
class AssociationIndex(HeadlessMixin, Page):
|
||||||
max_count = 1
|
max_count = 1
|
||||||
subpage_types = ["associations.AssociationPage"]
|
subpage_types = ["associations.AssociationPage"]
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ class AssociationIndex(Page):
|
|||||||
search_fields = Page.search_fields
|
search_fields = Page.search_fields
|
||||||
|
|
||||||
|
|
||||||
class AssociationPage(WPImportedPageMixin, Page):
|
class AssociationPage(HeadlessMixin, WPImportedPageMixin, Page):
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
parent_page_types = ["associations.AssociationIndex"]
|
parent_page_types = ["associations.AssociationIndex"]
|
||||||
show_in_menus = False
|
show_in_menus = False
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@@ -11,6 +11,7 @@ from wagtail.fields import RichTextField, StreamField
|
|||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
from wagtail.search import index
|
from wagtail.search import index
|
||||||
from wagtail.snippets.models import register_snippet
|
from wagtail.snippets.models import register_snippet
|
||||||
|
from wagtail_headless_preview.models import HeadlessMixin
|
||||||
|
|
||||||
from contacts.blocks import ContactSectionBlock
|
from contacts.blocks import ContactSectionBlock
|
||||||
from dnscms.blocks import BASE_BLOCKS
|
from dnscms.blocks import BASE_BLOCKS
|
||||||
@@ -22,7 +23,7 @@ PHONE_REGEX_VALIDATOR = RegexValidator(
|
|||||||
|
|
||||||
|
|
||||||
@register_singular_query_field("contactIndex")
|
@register_singular_query_field("contactIndex")
|
||||||
class ContactIndex(Page):
|
class ContactIndex(HeadlessMixin, Page):
|
||||||
max_count = 1
|
max_count = 1
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,11 @@ INSTALLED_APPS = [
|
|||||||
"news",
|
"news",
|
||||||
"openinghours",
|
"openinghours",
|
||||||
"sponsors",
|
"sponsors",
|
||||||
|
"studio",
|
||||||
# end cms apps
|
# end cms apps
|
||||||
"grapple",
|
"grapple",
|
||||||
"graphene_django",
|
"graphene_django",
|
||||||
|
"wagtail_headless_preview",
|
||||||
"wagtail.contrib.forms",
|
"wagtail.contrib.forms",
|
||||||
"wagtail.contrib.redirects",
|
"wagtail.contrib.redirects",
|
||||||
"wagtail.contrib.settings",
|
"wagtail.contrib.settings",
|
||||||
@@ -61,6 +63,7 @@ INSTALLED_APPS = [
|
|||||||
"django.contrib.sessions",
|
"django.contrib.sessions",
|
||||||
"django.contrib.messages",
|
"django.contrib.messages",
|
||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
|
"django.contrib.postgres",
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@@ -141,6 +144,8 @@ USE_L10N = True
|
|||||||
|
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
|
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||||
@@ -172,6 +177,7 @@ WAGTAIL_SITE_NAME = "dnscms"
|
|||||||
WAGTAIL_ALLOW_UNICODE_SLUGS = False
|
WAGTAIL_ALLOW_UNICODE_SLUGS = False
|
||||||
|
|
||||||
WAGTAILIMAGES_IMAGE_MODEL = "images.CustomImage"
|
WAGTAILIMAGES_IMAGE_MODEL = "images.CustomImage"
|
||||||
|
WAGTAILIMAGES_EXTENSIONS = ["avif", "gif", "jpg", "jpeg", "png", "webp", "svg"]
|
||||||
|
|
||||||
# Search
|
# Search
|
||||||
# https://docs.wagtail.org/en/stable/topics/search/backends.html
|
# https://docs.wagtail.org/en/stable/topics/search/backends.html
|
||||||
@@ -182,11 +188,25 @@ WAGTAILSEARCH_BACKENDS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Base URL to use when referring to full URLs within the Wagtail admin backend -
|
# Base URL to use when referring to full URLs within the Wagtail admin backend -
|
||||||
# e.g. in notification emails. Don't include '/admin' or a trailing slash
|
# e.g. in notification emails. Don't include '/admin' or a trailing slash.
|
||||||
WAGTAILADMIN_BASE_URL = "http://example.com"
|
# Also used by wagtail-grapple to make image URLs absolute.
|
||||||
|
WAGTAIL_BASE_URL = os.environ.get("WAGTAIL_BASE_URL", "http://127.0.0.1:8000").rstrip("/")
|
||||||
|
WAGTAILADMIN_BASE_URL = WAGTAIL_BASE_URL
|
||||||
|
BASE_URL = WAGTAIL_BASE_URL
|
||||||
|
|
||||||
# Required by wagtail-grapple to make image URLs absolute
|
# Public URL of the Next.js frontend. Used to direct preview iframes and to
|
||||||
BASE_URL = "http://example.com"
|
# redirect "View Live" clicks on the CMS host over to the headless frontend.
|
||||||
|
FRONTEND_BASE_URL = os.environ.get("FRONTEND_BASE_URL", "http://localhost:3000").rstrip("/")
|
||||||
|
|
||||||
|
WAGTAIL_HEADLESS_PREVIEW = {
|
||||||
|
"CLIENT_URLS": {"default": f"{FRONTEND_BASE_URL}/api/preview"},
|
||||||
|
"SERVE_BASE_URL": FRONTEND_BASE_URL,
|
||||||
|
"ENFORCE_TRAILING_SLASH": False,
|
||||||
|
"REDIRECT_ON_PREVIEW": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
# https://docs.wagtail.org/en/latest/releases/6.4.html#data-upload-max-number-fields-update
|
||||||
|
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10_000
|
||||||
|
|
||||||
# GraphQL
|
# GraphQL
|
||||||
GRAPHENE = {"SCHEMA": "grapple.schema.schema"}
|
GRAPHENE = {"SCHEMA": "grapple.schema.schema"}
|
||||||
@@ -202,8 +222,11 @@ GRAPPLE = {
|
|||||||
"news",
|
"news",
|
||||||
"openinghours",
|
"openinghours",
|
||||||
"sponsors",
|
"sponsors",
|
||||||
|
"studio",
|
||||||
],
|
],
|
||||||
"EXPOSE_GRAPHIQL": True,
|
"EXPOSE_GRAPHIQL": True,
|
||||||
|
"PAGE_SIZE": 100,
|
||||||
|
"MAX_PAGE_SIZE": 5000,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Wgtail WordPress import
|
# Wgtail WordPress import
|
||||||
|
|||||||
@@ -11,13 +11,6 @@ ALLOWED_HOSTS = ["*"]
|
|||||||
|
|
||||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||||
|
|
||||||
# Base URL to use when referring to full URLs within the Wagtail admin backend -
|
|
||||||
# e.g. in notification emails. Don't include '/admin' or a trailing slash
|
|
||||||
WAGTAILADMIN_BASE_URL = "http://127.0.0.1:8000"
|
|
||||||
|
|
||||||
# Required by wagtail-grapple to make image URLs absolute
|
|
||||||
BASE_URL = "http://127.0.0.1:8000"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .local import *
|
from .local import *
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from .base import * # noqa: F401, F403
|
||||||
|
|
||||||
|
SECRET_KEY = "test-secret-key"
|
||||||
|
DEBUG = False
|
||||||
|
ALLOWED_HOSTS = ["*"]
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
|
"NAME": ":memory:",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STORAGES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||||
|
},
|
||||||
|
"staticfiles": {
|
||||||
|
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
||||||
@@ -6,7 +6,6 @@ from wagtail import hooks
|
|||||||
from wagtail.admin.menu import MenuItem
|
from wagtail.admin.menu import MenuItem
|
||||||
|
|
||||||
from associations.models import AssociationIndex
|
from associations.models import AssociationIndex
|
||||||
from events.models import EventIndex
|
|
||||||
from news.models import NewsIndex
|
from news.models import NewsIndex
|
||||||
|
|
||||||
|
|
||||||
@@ -15,15 +14,6 @@ def enable_additional_rich_text_features(features):
|
|||||||
features.default_features.extend(["h5", "h6", "blockquote"])
|
features.default_features.extend(["h5", "h6", "blockquote"])
|
||||||
|
|
||||||
|
|
||||||
@hooks.register("register_admin_menu_item")
|
|
||||||
def register_events_menu_item():
|
|
||||||
page = EventIndex.objects.first()
|
|
||||||
events_url = "#"
|
|
||||||
if page:
|
|
||||||
events_url = reverse("wagtailadmin_explore", args=(quote(page.pk),))
|
|
||||||
return MenuItem("Arrangementer", events_url, icon_name="date", order=1)
|
|
||||||
|
|
||||||
|
|
||||||
@hooks.register("register_admin_menu_item")
|
@hooks.register("register_admin_menu_item")
|
||||||
def register_associations_menu_item():
|
def register_associations_menu_item():
|
||||||
page = AssociationIndex.objects.first()
|
page = AssociationIndex.objects.first()
|
||||||
@@ -34,7 +24,7 @@ def register_associations_menu_item():
|
|||||||
|
|
||||||
|
|
||||||
@hooks.register("register_admin_menu_item")
|
@hooks.register("register_admin_menu_item")
|
||||||
def register_associations_menu_item():
|
def register_news_menu_item():
|
||||||
page = NewsIndex.objects.first()
|
page = NewsIndex.objects.first()
|
||||||
news_url = "#"
|
news_url = "#"
|
||||||
if page:
|
if page:
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
|
from wagtail.admin.ui.tables import Column, DateColumn
|
||||||
|
from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn
|
||||||
|
from wagtail.admin.views.pages.choose_parent import ChooseParentView
|
||||||
|
from wagtail.admin.views.pages.listing import IndexView
|
||||||
|
from wagtail.admin.viewsets.pages import PageListingViewSet
|
||||||
|
|
||||||
|
from events.models import EventPage
|
||||||
|
|
||||||
|
|
||||||
|
class EventDateColumn(Column):
|
||||||
|
def get_value(self, instance):
|
||||||
|
occurrences = list(instance.occurrences.order_by("start"))
|
||||||
|
if not occurrences:
|
||||||
|
return "—"
|
||||||
|
if len(occurrences) == 1:
|
||||||
|
local = timezone.localtime(occurrences[0].start)
|
||||||
|
return local.strftime("%Y-%m-%d kl %H:%M")
|
||||||
|
return f"{len(occurrences)} forekomster"
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizersColumn(Column):
|
||||||
|
def get_value(self, instance):
|
||||||
|
names = list(instance.organizers.values_list("name", flat=True))
|
||||||
|
if not names:
|
||||||
|
return "—"
|
||||||
|
if len(names) == 1:
|
||||||
|
return names[0]
|
||||||
|
return f"{names[0]} (+{len(names) - 1})"
|
||||||
|
|
||||||
|
|
||||||
|
class EventPageIndexView(IndexView):
|
||||||
|
def annotate_queryset(self, pages):
|
||||||
|
pages = super().annotate_queryset(pages)
|
||||||
|
return pages.prefetch_related(
|
||||||
|
"occurrences",
|
||||||
|
"organizer_links__organizer",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EventChooseParentView(ChooseParentView):
|
||||||
|
"""Redirect newly-created EventPages back to the events listing."""
|
||||||
|
|
||||||
|
def _with_next(self, response):
|
||||||
|
if response.status_code != 302:
|
||||||
|
return response
|
||||||
|
url = response["Location"]
|
||||||
|
sep = "&" if "?" in url else "?"
|
||||||
|
response["Location"] = f"{url}{sep}{urlencode({'next': reverse('events:index')})}"
|
||||||
|
return response
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
return self._with_next(super().get(request, *args, **kwargs))
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
return self._with_next(super().form_valid(form))
|
||||||
|
|
||||||
|
|
||||||
|
class EventPageListingViewSet(PageListingViewSet):
|
||||||
|
model = EventPage
|
||||||
|
index_view_class = EventPageIndexView
|
||||||
|
choose_parent_view_class = EventChooseParentView
|
||||||
|
icon = "date"
|
||||||
|
menu_label = "Arrangementer"
|
||||||
|
menu_order = 1
|
||||||
|
add_to_admin_menu = True
|
||||||
|
ordering = "-latest_revision_created_at"
|
||||||
|
|
||||||
|
columns = [
|
||||||
|
PageTitleColumn("title", label="Tittel", sort_key="title", classname="title"),
|
||||||
|
EventDateColumn("event_date", label="Dato", width="13%"),
|
||||||
|
OrganizersColumn("organizers", label="Arrangører", width="12%"),
|
||||||
|
DateColumn(
|
||||||
|
"latest_revision_created_at",
|
||||||
|
label="Oppdatert",
|
||||||
|
sort_key="latest_revision_created_at",
|
||||||
|
width="10%",
|
||||||
|
),
|
||||||
|
PageStatusColumn("status", label="Status", sort_key="live", width="10%"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
event_page_listing_viewset = EventPageListingViewSet("events")
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 6.0.5 on 2026-05-19 18:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('events', '0053_eventpage_lead'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='eventpage',
|
||||||
|
options={'verbose_name': 'event', 'verbose_name_plural': 'events'},
|
||||||
|
),
|
||||||
|
]
|
||||||
+14
-6
@@ -30,6 +30,7 @@ from wagtail.fields import RichTextField
|
|||||||
from wagtail.models import Orderable, Page, PageManager, PageQuerySet
|
from wagtail.models import Orderable, Page, PageManager, PageQuerySet
|
||||||
from wagtail.search import index
|
from wagtail.search import index
|
||||||
from wagtail.snippets.models import register_snippet
|
from wagtail.snippets.models import register_snippet
|
||||||
|
from wagtail_headless_preview.models import HeadlessMixin
|
||||||
|
|
||||||
from associations.widgets import AssociationChooserWidget
|
from associations.widgets import AssociationChooserWidget
|
||||||
from dnscms.fields import CommonStreamField
|
from dnscms.fields import CommonStreamField
|
||||||
@@ -39,7 +40,7 @@ from venues.models import VenuePage
|
|||||||
|
|
||||||
|
|
||||||
@register_singular_query_field("eventIndex")
|
@register_singular_query_field("eventIndex")
|
||||||
class EventIndex(Page):
|
class EventIndex(HeadlessMixin, Page):
|
||||||
max_count = 1
|
max_count = 1
|
||||||
subpage_types = ["events.EventPage"]
|
subpage_types = ["events.EventPage"]
|
||||||
|
|
||||||
@@ -209,15 +210,18 @@ class EventOrganizer(ClusterableModel):
|
|||||||
|
|
||||||
class EventPageQuerySet(PageQuerySet):
|
class EventPageQuerySet(PageQuerySet):
|
||||||
def future(self):
|
def future(self):
|
||||||
today = timezone.localtime(timezone.now()).date()
|
now = timezone.now()
|
||||||
next_occurrence = Min("occurrences__start", filter=Q(occurrences__start__gte=today))
|
today_start = timezone.localtime(now).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
return self.filter(occurrences__start__gte=today).annotate(next_occurrence=next_occurrence)
|
next_occurrence = Min("occurrences__start", filter=Q(occurrences__start__gte=today_start))
|
||||||
|
return self.filter(occurrences__start__gte=today_start).annotate(
|
||||||
|
next_occurrence=next_occurrence
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
EventPageManager = PageManager.from_queryset(EventPageQuerySet)
|
EventPageManager = PageManager.from_queryset(EventPageQuerySet)
|
||||||
|
|
||||||
|
|
||||||
class EventPage(WPImportedPageMixin, Page):
|
class EventPage(HeadlessMixin, WPImportedPageMixin, Page):
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
parent_page_types = ["events.EventIndex"]
|
parent_page_types = ["events.EventIndex"]
|
||||||
show_in_menus = False
|
show_in_menus = False
|
||||||
@@ -355,7 +359,7 @@ class EventPage(WPImportedPageMixin, Page):
|
|||||||
GraphQLImage("featured_image"),
|
GraphQLImage("featured_image"),
|
||||||
GraphQLRichText("lead"),
|
GraphQLRichText("lead"),
|
||||||
GraphQLStreamfield("body"),
|
GraphQLStreamfield("body"),
|
||||||
GraphQLString("pig"),
|
GraphQLString("pig", required=True),
|
||||||
GraphQLString("ticket_url"),
|
GraphQLString("ticket_url"),
|
||||||
GraphQLString("facebook_url"),
|
GraphQLString("facebook_url"),
|
||||||
GraphQLBoolean("free"),
|
GraphQLBoolean("free"),
|
||||||
@@ -387,6 +391,10 @@ class EventPage(WPImportedPageMixin, Page):
|
|||||||
|
|
||||||
search_fields = Page.search_fields + [index.SearchField("body")]
|
search_fields = Page.search_fields + [index.SearchField("body")]
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("event")
|
||||||
|
verbose_name_plural = _("events")
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
from wagtail import hooks
|
from wagtail import hooks
|
||||||
|
|
||||||
|
from .admin import event_page_listing_viewset
|
||||||
from .views import event_organizer_chooser_viewset
|
from .views import event_organizer_chooser_viewset
|
||||||
|
|
||||||
|
|
||||||
@hooks.register("register_admin_viewset")
|
@hooks.register("register_admin_viewset")
|
||||||
def register_viewset():
|
def register_viewset():
|
||||||
return event_organizer_chooser_viewset
|
return event_organizer_chooser_viewset
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.register("register_admin_viewset")
|
||||||
|
def register_event_page_listing_viewset():
|
||||||
|
return event_page_listing_viewset
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ from wagtail.admin.panels import FieldPanel
|
|||||||
from wagtail.fields import RichTextField, StreamField
|
from wagtail.fields import RichTextField, StreamField
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
from wagtail.search import index
|
from wagtail.search import index
|
||||||
|
from wagtail_headless_preview.models import HeadlessMixin
|
||||||
|
|
||||||
from dnscms.blocks import PageSectionBlock
|
from dnscms.blocks import PageSectionBlock
|
||||||
from dnscms.fields import BASE_BLOCKS
|
from dnscms.fields import BASE_BLOCKS
|
||||||
from dnscms.options import ALL_PIGS
|
from dnscms.options import ALL_PIGS
|
||||||
|
|
||||||
|
|
||||||
class GenericPage(Page):
|
class GenericPage(HeadlessMixin, Page):
|
||||||
subpage_types = ["generic.GenericPage"]
|
subpage_types = ["generic.GenericPage"]
|
||||||
show_in_menus = True
|
show_in_menus = True
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ from wagtail.admin.panels import (
|
|||||||
PageChooserPanel,
|
PageChooserPanel,
|
||||||
)
|
)
|
||||||
from wagtail.models import Orderable, Page
|
from wagtail.models import Orderable, Page
|
||||||
|
from wagtail_headless_preview.models import HeadlessMixin
|
||||||
|
|
||||||
|
|
||||||
class HomePage(Page):
|
class HomePage(HeadlessMixin, Page):
|
||||||
max_count = 1
|
max_count = 1
|
||||||
|
|
||||||
content_panels = Page.content_panels + [
|
content_panels = Page.content_panels + [
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% block body_class %}template-homepage{% endblock %}
|
|
||||||
|
|
||||||
{% block extra_css %}
|
|
||||||
|
|
||||||
{% comment %}
|
|
||||||
Delete the line below if you're just getting started and want to remove the welcome screen!
|
|
||||||
{% endcomment %}
|
|
||||||
<link rel="stylesheet" href="{% static 'css/welcome_page.css' %}">
|
|
||||||
{% endblock extra_css %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% comment %}
|
|
||||||
Delete the line below if you're just getting started and want to remove the welcome screen!
|
|
||||||
{% endcomment %}
|
|
||||||
{% include 'home/welcome_page.html' %}
|
|
||||||
|
|
||||||
{% endblock content %}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
{% load i18n wagtailcore_tags %}
|
|
||||||
|
|
||||||
<header class="header">
|
|
||||||
<div class="logo">
|
|
||||||
<a href="https://wagtail.org/">
|
|
||||||
<svg class="figure-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 342.5 126.2"><title>{% trans "Visit the Wagtail website" %}</title><path fill="#FFF" d="M84 1.9v5.7s-10.2-3.8-16.8 3.1c-4.8 5-5.2 10.6-3 18.1 21.6 0 25 12.1 25 12.1L87 27l6.8-8.3c0-9.8-8.1-16.3-9.8-16.8z"/><circle cx="85.9" cy="15.9" r="2.6"/><path d="M89.2 40.9s-3.3-16.6-24.9-12.1c-2.2-7.5-1.8-13 3-18.1C73.8 3.8 84 7.6 84 7.6V1.9C80.4.3 77 0 73.2 0 59.3 0 51.6 10.4 48.3 17.4L9.2 89.3l11-2.1-20.2 39 14.1-2.5L24.9 93c30.6 0 69.8-11 64.3-52.1z"/><path d="M102.4 27l-8.6-8.3L87 27z"/><path fill="#FFF" d="M30 84.1s1-.2 2.8-.6c1.8-.4 4.3-1 7.3-1.8 1.5-.4 3.1-.9 4.8-1.5 1.7-.6 3.5-1.2 5.2-2 1.8-.7 3.6-1.6 5.4-2.6 1.8-1 3.5-2.1 5.1-3.4.4-.3.8-.6 1.2-1l1.2-1c.7-.7 1.5-1.4 2.2-2.2.7-.7 1.3-1.5 1.9-2.3l.9-1.2.4-.6.4-.6c.2-.4.5-.8.7-1.2.2-.4.4-.8.7-1.2l.3-.6.3-.6c.2-.4.4-.8.5-1.2l.9-2.4c.2-.8.5-1.6.7-2.3.2-.7.3-1.5.5-2.1.1-.7.2-1.3.3-2 .1-.6.2-1.2.2-1.7.1-.5.1-1 .2-1.5.1-1.8.1-2.8.1-2.8l1.6.1s-.1 1.1-.2 2.9c-.1.5-.1 1-.2 1.5-.1.6-.1 1.2-.3 1.8-.1.6-.3 1.3-.4 2-.2.7-.4 1.4-.6 2.2-.2.8-.5 1.5-.8 2.4-.3.8-.6 1.6-1 2.5l-.6 1.2-.3.6-.3.6c-.2.4-.5.8-.7 1.3-.3.4-.5.8-.8 1.2-.1.2-.3.4-.4.6l-.4.6-.9 1.2c-.7.8-1.3 1.6-2.1 2.3-.7.8-1.5 1.4-2.3 2.2l-1.2 1c-.4.3-.8.6-1.3.9-1.7 1.2-3.5 2.3-5.3 3.3-1.8.9-3.7 1.8-5.5 2.5-1.8.7-3.6 1.3-5.3 1.8-1.7.5-3.3 1-4.9 1.3-3 .7-5.6 1.3-7.4 1.6-1.6.6-2.6.8-2.6.8z"/><g fill="#231F20"><path d="M127 83.9h-8.8l-12.6-36.4h7.9l9 27.5 9-27.5h7.9l9 27.5 9-27.5h7.9L153 83.9h-8.8L135.6 59 127 83.9zM200.1 83.9h-7V79c-3 3.6-7 5.4-12.1 5.4-3.8 0-6.9-1.1-9.4-3.2s-3.7-5-3.7-8.6c0-3.6 1.3-6.3 4-8 2.6-1.8 6.2-2.7 10.7-2.7h9.9v-1.4c0-4.8-2.7-7.3-8.1-7.3-3.4 0-6.9 1.2-10.5 3.7l-3.4-4.8c4.4-3.5 9.4-5.3 15.1-5.3 4.3 0 7.8 1.1 10.5 3.2 2.7 2.2 4.1 5.6 4.1 10.2v23.7zm-7.7-13.6v-3.1h-8.6c-5.5 0-8.3 1.7-8.3 5.2 0 1.8.7 3.1 2.1 4.1 1.4.9 3.3 1.4 5.7 1.4 2.4 0 4.6-.7 6.4-2.1 1.8-1.3 2.7-3.1 2.7-5.5zM241.7 47.5v31.7c0 6.4-1.7 11.3-5.2 14.5-3.5 3.2-8 4.8-13.4 4.8-5.5 0-10.4-1.7-14.8-5.1l3.6-5.8c3.6 2.7 7.1 4 10.8 4 3.6 0 6.5-.9 8.6-2.8 2.1-1.9 3.2-4.9 3.2-9v-4.7c-1.1 2.1-2.8 3.9-4.9 5.1-2.1 1.3-4.5 1.9-7.1 1.9-4.8 0-8.8-1.7-11.9-5.1-3.1-3.4-4.7-7.6-4.7-12.6s1.6-9.2 4.7-12.6c3.1-3.4 7.1-5.1 11.9-5.1 4.8 0 8.7 2 11.7 6v-5.4h7.5zm-28.4 16.8c0 3 .9 5.6 2.8 7.7 1.8 2.2 4.3 3.2 7.5 3.2 3.1 0 5.7-1 7.6-3.1 1.9-2.1 2.9-4.7 2.9-7.8 0-3.1-1-5.8-2.9-7.9-2-2.2-4.5-3.2-7.6-3.2-3.1 0-5.6 1.1-7.4 3.4-2 2.1-2.9 4.7-2.9 7.7zM260.9 53.6v18.5c0 1.7.5 3.1 1.4 4.1.9 1 2.2 1.5 3.8 1.5 1.6 0 3.2-.8 4.7-2.4l3.1 5.4c-2.7 2.4-5.7 3.6-8.9 3.6-3.3 0-6-1.1-8.3-3.4-2.3-2.3-3.5-5.3-3.5-9.1V53.6h-4.6v-6.2h4.6V36.1h7.7v11.4h9.6v6.2h-9.6zM309.5 83.9h-7V79c-3 3.6-7 5.4-12.1 5.4-3.8 0-6.9-1.1-9.4-3.2s-3.7-5-3.7-8.6c0-3.6 1.3-6.3 4-8 2.6-1.8 6.2-2.7 10.7-2.7h9.9v-1.4c0-4.8-2.7-7.3-8.1-7.3-3.4 0-6.9 1.2-10.5 3.7l-3.4-4.8c4.4-3.5 9.4-5.3 15.1-5.3 4.3 0 7.8 1.1 10.5 3.2 2.7 2.2 4.1 5.6 4.1 10.2v23.7zm-7.7-13.6v-3.1h-8.6c-5.5 0-8.3 1.7-8.3 5.2 0 1.8.7 3.1 2.1 4.1 1.4.9 3.3 1.4 5.7 1.4 2.4 0 4.6-.7 6.4-2.1 1.8-1.3 2.7-3.1 2.7-5.5zM319.3 40.2c-1-1-1.4-2.1-1.4-3.4 0-1.3.5-2.5 1.4-3.4 1-1 2.1-1.4 3.4-1.4 1.3 0 2.5.5 3.4 1.4 1 1 1.4 2.1 1.4 3.4 0 1.3-.5 2.5-1.4 3.4s-2.1 1.4-3.4 1.4c-1.3.1-2.4-.4-3.4-1.4zm7.2 43.7h-7.7V47.5h7.7v36.4zM342.5 83.9h-7.7V33.1h7.7v50.8z"/></g></svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="header-link">
|
|
||||||
{% comment %}
|
|
||||||
This works for all cases but prerelease versions:
|
|
||||||
{% endcomment %}
|
|
||||||
<a href="{% wagtail_documentation_path %}/releases/{% wagtail_release_notes_path %}">
|
|
||||||
{% trans "View the release notes" %}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main class="main">
|
|
||||||
<div class="figure">
|
|
||||||
<svg class="figure-space" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300" aria-hidden="true">
|
|
||||||
<path class="egg" fill="currentColor" d="M150 250c-42.741 0-75-32.693-75-90s42.913-110 75-110c32.088 0 75 52.693 75 110s-32.258 90-75 90z"/>
|
|
||||||
<ellipse fill="#ddd" cx="150" cy="270" rx="40" ry="7"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="main-text">
|
|
||||||
<h1>{% trans "Welcome to your new Wagtail site!" %}</h1>
|
|
||||||
<p>{% trans 'Please feel free to <a href="https://github.com/wagtail/wagtail/wiki/Slack">join our community on Slack</a>, or get started with one of the links below.' %}</p>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<footer class="footer" role="contentinfo">
|
|
||||||
<a class="option option-one" href="{% wagtail_documentation_path %}/">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 21c0 .5.4 1 1 1h4c.6 0 1-.5 1-1v-1H9v1zm3-19C8.1 2 5 5.1 5 9c0 2.4 1.2 4.5 3 5.7V17c0 .5.4 1 1 1h6c.6 0 1-.5 1-1v-2.3c1.8-1.3 3-3.4 3-5.7 0-3.9-3.1-7-7-7zm2.9 11.1l-.9.6V16h-4v-2.3l-.9-.6C7.8 12.2 7 10.6 7 9c0-2.8 2.2-5 5-5s5 2.2 5 5c0 1.6-.8 3.2-2.1 4.1z"/></svg>
|
|
||||||
<div>
|
|
||||||
<h2>{% trans "Wagtail Documentation" %}</h2>
|
|
||||||
<p>{% trans "Topics, references, & how-tos" %}</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a class="option option-two" href="{% wagtail_documentation_path %}/getting_started/tutorial.html">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
|
|
||||||
<div>
|
|
||||||
<h2>{% trans "Tutorial" %}</h2>
|
|
||||||
<p>{% trans "Build your first Wagtail site" %}</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a class="option option-three" href="{% url 'wagtailadmin_home' %}">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"/></svg>
|
|
||||||
<div>
|
|
||||||
<h2>{% trans "Admin Interface" %}</h2>
|
|
||||||
<p>{% trans "Create your superuser first!" %}</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-08-12 21:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('images', '0004_alter_customimage_alt_alter_customimage_attribution'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customimage',
|
||||||
|
name='description',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=255, verbose_name='description'),
|
||||||
|
),
|
||||||
|
]
|
||||||
Binary file not shown.
@@ -0,0 +1,43 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: dnscms\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2026-05-19 20:44+0200\n"
|
||||||
|
"Language: nb\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: events/models.py:73 events/models.py:155
|
||||||
|
msgid "slug"
|
||||||
|
msgstr "permalenke"
|
||||||
|
|
||||||
|
#: events/models.py:75
|
||||||
|
msgid "The name of the category as it will appear in URLs."
|
||||||
|
msgstr "Navnet på kategorien slik det vil vises i URL-er."
|
||||||
|
|
||||||
|
#: events/models.py:157
|
||||||
|
msgid "The name of the organizer as it will appear in URLs."
|
||||||
|
msgstr "Navnet på arrangøren slik det vil vises i URL-er."
|
||||||
|
|
||||||
|
#: events/models.py:395
|
||||||
|
msgid "event"
|
||||||
|
msgstr "arrangement"
|
||||||
|
|
||||||
|
#: events/models.py:396
|
||||||
|
msgid "events"
|
||||||
|
msgstr "arrangementer"
|
||||||
|
|
||||||
|
#: images/models.py:40
|
||||||
|
msgid "image"
|
||||||
|
msgstr "bilde"
|
||||||
|
|
||||||
|
#: images/models.py:41
|
||||||
|
msgid "images"
|
||||||
|
msgstr "bilder"
|
||||||
+8
-3
@@ -1,3 +1,8 @@
|
|||||||
[tools]
|
# Translation tasks
|
||||||
python = "3.13"
|
[tasks.compilemessages]
|
||||||
uv = "latest"
|
description = "Compile translation files for Norwegian Bokmal"
|
||||||
|
run = "uv run manage.py compilemessages -l nb"
|
||||||
|
|
||||||
|
[tasks.makemessages]
|
||||||
|
description = "Extract translation strings for Norwegian Bokmal"
|
||||||
|
run = "uv run manage.py makemessages -l nb"
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ from wagtail.admin.panels import FieldPanel
|
|||||||
from wagtail.fields import RichTextField
|
from wagtail.fields import RichTextField
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
from wagtail.search import index
|
from wagtail.search import index
|
||||||
|
from wagtail_headless_preview.models import HeadlessMixin
|
||||||
|
|
||||||
from dnscms.fields import CommonStreamField
|
from dnscms.fields import CommonStreamField
|
||||||
from dnscms.wordpress.models import WPImportedPageMixin
|
from dnscms.wordpress.models import WPImportedPageMixin
|
||||||
|
|
||||||
|
|
||||||
@register_singular_query_field("newsIndex")
|
@register_singular_query_field("newsIndex")
|
||||||
class NewsIndex(Page):
|
class NewsIndex(HeadlessMixin, Page):
|
||||||
max_count = 1
|
max_count = 1
|
||||||
subpage_types = ["news.NewsPage"]
|
subpage_types = ["news.NewsPage"]
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ class NewsIndex(Page):
|
|||||||
search_fields = []
|
search_fields = []
|
||||||
|
|
||||||
|
|
||||||
class NewsPage(WPImportedPageMixin, Page):
|
class NewsPage(HeadlessMixin, WPImportedPageMixin, Page):
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
parent_page_types = ["news.NewsIndex"]
|
parent_page_types = ["news.NewsIndex"]
|
||||||
show_in_menus = False
|
show_in_menus = False
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ from modelcluster.fields import ParentalKey
|
|||||||
from modelcluster.models import ClusterableModel
|
from modelcluster.models import ClusterableModel
|
||||||
from wagtail import blocks
|
from wagtail import blocks
|
||||||
from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel, TitleFieldPanel
|
from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel, TitleFieldPanel
|
||||||
from wagtail.models import Orderable, StreamField
|
from wagtail.fields import StreamField
|
||||||
|
from wagtail.models import Orderable
|
||||||
from wagtail.snippets.models import register_snippet
|
from wagtail.snippets.models import register_snippet
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
+32
-10
@@ -3,20 +3,26 @@ name = "dnscms"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = ""
|
description = ""
|
||||||
authors = [{ name = "EDB", email = "edb@neuf.no" }]
|
authors = [{ name = "EDB", email = "edb@neuf.no" }]
|
||||||
requires-python = ">=3.12, <3.13"
|
requires-python = ">=3.14, <3.15"
|
||||||
readme = "README.md"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wagtail>=6.1.3,<7",
|
"wagtail>=7.4.1,<8",
|
||||||
"django>=5.0.7,<6",
|
"wagtail-grapple>=0.31.0,<0.32",
|
||||||
"wagtail-grapple>=0.26.0,<0.27",
|
"wagtail-headless-preview>=0.8,<0.9",
|
||||||
"psycopg2-binary>=2.9.10,<3",
|
"django>=6.0.5,<7",
|
||||||
"django-extensions>=3.2.3,<4",
|
"django-extensions>=4.1,<5",
|
||||||
"whitenoise>=6.7.0,<7",
|
"psycopg2-binary>=2.9.12,<3",
|
||||||
"gunicorn>=23.0.0",
|
"gunicorn>=26.0.0,<27",
|
||||||
|
"whitenoise>=6.12.0,<7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = ["ruff"]
|
dev = [
|
||||||
|
"ruff>=0.15.13,<0.16",
|
||||||
|
"pytest>=9.0.3,<10",
|
||||||
|
"pytest-cov>=7.0.0,<8",
|
||||||
|
"pytest-django>=4.12.0,<5",
|
||||||
|
"wagtail-factories>=4.4.0,<5",
|
||||||
|
]
|
||||||
|
|
||||||
[tool.uv]
|
[tool.uv]
|
||||||
package = false
|
package = false
|
||||||
@@ -32,3 +38,19 @@ line-length = 99
|
|||||||
select = ["F", "E", "W", "Q", "UP", "DJ"]
|
select = ["F", "E", "W", "Q", "UP", "DJ"]
|
||||||
ignore = []
|
ignore = []
|
||||||
exclude = ["**/migrations/*.py"]
|
exclude = ["**/migrations/*.py"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
DJANGO_SETTINGS_MODULE = "dnscms.settings.test"
|
||||||
|
python_files = ["test_*.py"]
|
||||||
|
testpaths = ["tests"]
|
||||||
|
addopts = "--cov=. --cov-report=term-missing"
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
omit = [
|
||||||
|
"*/migrations/*",
|
||||||
|
"tests/*",
|
||||||
|
"manage.py",
|
||||||
|
"dnscms/settings/*",
|
||||||
|
"dnscms/wsgi.py",
|
||||||
|
"dnscms/asgi.py",
|
||||||
|
]
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from wagtail.fields import RichTextField, StreamField
|
|||||||
from wagtail.images.blocks import ImageChooserBlock
|
from wagtail.images.blocks import ImageChooserBlock
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
from wagtail.search import index
|
from wagtail.search import index
|
||||||
|
from wagtail_headless_preview.models import HeadlessMixin
|
||||||
|
|
||||||
from dnscms.blocks import BASE_BLOCKS
|
from dnscms.blocks import BASE_BLOCKS
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ class SponsorBlock(blocks.StructBlock):
|
|||||||
|
|
||||||
|
|
||||||
@register_singular_query_field("sponsorsPage")
|
@register_singular_query_field("sponsorsPage")
|
||||||
class SponsorsPage(Page):
|
class SponsorsPage(HeadlessMixin, Page):
|
||||||
max_count = 1
|
max_count = 1
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class StudioConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "studio"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 6.0.5 on 2026-05-19 02:59
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import wagtail.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('images', '0005_customimage_description'),
|
||||||
|
('wagtailcore', '0097_baselogentry_uuid_action_timestamp_indexes'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='StudioPage',
|
||||||
|
fields=[
|
||||||
|
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
|
||||||
|
('lead', wagtail.fields.RichTextField(blank=True)),
|
||||||
|
('body', wagtail.fields.StreamField([('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('page_section_navigation', 19), ('accordion', 23), ('fact_box', 26), ('embed', 27), ('raw_html', 28), ('page_section', 33)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'label': 'Rik tekst'}), 1: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Bilde'}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], 'icon': 'cup', 'label': 'Bildeformat'}), 3: ('wagtail.blocks.CharBlock', (), {'label': 'Bildetekst', 'max_length': 512, 'required': False}), 4: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {}), 5: ('wagtail.blocks.CharBlock', (), {'label': 'Tekst', 'max_length': 512, 'required': False}), 6: ('wagtail.blocks.StructBlock', [[('image', 1), ('text', 5)]], {}), 7: ('wagtail.blocks.ListBlock', (6,), {'label': 'Bilder', 'min_num': 1}), 8: ('wagtail.blocks.StructBlock', [[('images', 7)]], {}), 9: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Farge', 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('color', 9)]], {}), 11: ('wagtail.blocks.CharBlock', (), {'label': 'Tittel', 'max_length': 64, 'required': True}), 12: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Tekst', 'required': True}), 13: ('wagtail.blocks.PageChooserBlock', (), {'header': 'Fremhevet side', 'required': True}), 14: ('wagtail.blocks.CharBlock', (), {'default': 'Les mer', 'help_text': 'Lenketeksten som tar deg videre til siden. Tips: Ikke start med "Trykk her"', 'label': 'Lenketekst', 'max_length': 64, 'required': True}), 15: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge'}), 16: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('left', 'Venstre'), ('right', 'Høyre')], 'label': 'Bildeplassering'}), 17: ('wagtail.images.blocks.ImageChooserBlock', (), {'header': 'Overstyr bilde', 'help_text': 'Bildet som er tilknyttet undersiden du vil fremheve, vil automatisk brukes. Om det mangler eller du vil overstyre hvilket bilde som et brukes, kan du velge et her.', 'required': False}), 18: ('wagtail.blocks.StructBlock', [[('title', 11), ('text', 12), ('featured_page', 13), ('link_text', 14), ('background_color', 15), ('image_position', 16), ('featured_image_override', 17)]], {}), 19: ('dnscms.blocks.PageSectionNavigationBlock', (), {}), 20: ('wagtail.blocks.CharBlock', (), {'label': 'Overskrift', 'max_length': 64, 'required': True}), 21: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {'label': 'Bilde'}), 22: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 21)]], {}), 23: ('wagtail.blocks.StructBlock', [[('heading', 20), ('body', 22)]], {}), 24: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge', 'required': False}), 25: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'h2', 'h3'], 'label': 'Innhold'}), 26: ('wagtail.blocks.StructBlock', [[('background_color', 24), ('body', 25)]], {}), 27: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 28: ('wagtail.blocks.RawHTMLBlock', (), {}), 29: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('pigHeadLogo', 'Grisehodelogo'), ('key', 'Nøkkel'), ('ticket', 'Billett'), ('shield', 'Skjold'), ('bottle', 'Flaske'), ('lostProperty', 'Hittegods'), ('pigsty', 'Grisebinge'), ('wheelchair', 'Rullestol'), ('clock', 'Klokke'), ('parking', 'Parkering'), ('coins', 'Mynter')], 'label': 'Ikon', 'required': False}), 30: ('dnscms.blocks.NeufAddressSectionBlock', (), {}), 31: ('dnscms.blocks.OpeningHoursSectionBlock', (), {}), 32: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('accordion', 23), ('fact_box', 26), ('embed', 27), ('raw_html', 28), ('neuf_address', 30), ('opening_hours', 31)]], {}), 33: ('wagtail.blocks.StructBlock', [[('title', 11), ('background_color', 24), ('icon', 29), ('body', 32)]], {})})),
|
||||||
|
('pig', models.CharField(blank=True, choices=[('', 'Ingen'), ('logo', 'Logogrisen'), ('music', 'Musikergrisen'), ('drink', 'Drikkegrisen'), ('dance', 'Dansegrisen'), ('point', 'Pekegrisen'), ('student', 'Studentgrisen'), ('listen', 'Lyttegrisen'), ('guard', 'Vaktgrisen'), ('key', 'Nøkkelgrisen'), ('chill', 'Liggegrisen'), ('peek', 'Tittegrisen')], default='', help_text='Grisen nedi hjørnet.', max_length=32)),
|
||||||
|
('logo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.customimage')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
bases=('wagtailcore.page',),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
from django.db import models
|
||||||
|
from grapple.helpers import register_singular_query_field
|
||||||
|
from grapple.models import (
|
||||||
|
GraphQLImage,
|
||||||
|
GraphQLRichText,
|
||||||
|
GraphQLStreamfield,
|
||||||
|
GraphQLString,
|
||||||
|
)
|
||||||
|
from wagtail.admin.panels import FieldPanel
|
||||||
|
from wagtail.fields import RichTextField, StreamField
|
||||||
|
from wagtail.models import Page
|
||||||
|
from wagtail.search import index
|
||||||
|
from wagtail_headless_preview.models import HeadlessMixin
|
||||||
|
|
||||||
|
from dnscms.blocks import BASE_BLOCKS, PageSectionBlock
|
||||||
|
from dnscms.options import ALL_PIGS
|
||||||
|
|
||||||
|
|
||||||
|
@register_singular_query_field("studioPage")
|
||||||
|
class StudioPage(HeadlessMixin, Page):
|
||||||
|
max_count = 1
|
||||||
|
subpage_types = []
|
||||||
|
show_in_menus = True
|
||||||
|
|
||||||
|
logo = models.ForeignKey(
|
||||||
|
"images.CustomImage",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="+",
|
||||||
|
)
|
||||||
|
lead = RichTextField(features=["link"], blank=True)
|
||||||
|
body = StreamField(BASE_BLOCKS + [("page_section", PageSectionBlock())])
|
||||||
|
|
||||||
|
PIG_CHOICES = [
|
||||||
|
("", "Ingen"),
|
||||||
|
] + ALL_PIGS
|
||||||
|
|
||||||
|
pig = models.CharField(
|
||||||
|
max_length=32,
|
||||||
|
choices=PIG_CHOICES,
|
||||||
|
default="",
|
||||||
|
blank=True,
|
||||||
|
help_text="Grisen nedi hjørnet.",
|
||||||
|
)
|
||||||
|
|
||||||
|
content_panels = Page.content_panels + [
|
||||||
|
FieldPanel("logo"),
|
||||||
|
FieldPanel("lead", heading="Ingress"),
|
||||||
|
FieldPanel("body", heading="Innhold"),
|
||||||
|
FieldPanel("pig", heading="Gris"),
|
||||||
|
]
|
||||||
|
|
||||||
|
graphql_fields = [
|
||||||
|
GraphQLImage("logo"),
|
||||||
|
GraphQLRichText("lead"),
|
||||||
|
GraphQLStreamfield("body"),
|
||||||
|
GraphQLString("pig", required=True),
|
||||||
|
]
|
||||||
|
|
||||||
|
search_fields = Page.search_fields + [
|
||||||
|
index.SearchField("lead"),
|
||||||
|
index.SearchField("body"),
|
||||||
|
]
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
import factory
|
||||||
|
import pytest
|
||||||
|
import wagtail_factories
|
||||||
|
from wagtail.models import Page
|
||||||
|
|
||||||
|
from associations.models import AssociationIndex, AssociationPage
|
||||||
|
from events.models import EventIndex, EventPage
|
||||||
|
from generic.models import GenericPage
|
||||||
|
from images.models import CustomImage
|
||||||
|
from news.models import NewsIndex, NewsPage
|
||||||
|
from studio.models import StudioPage
|
||||||
|
from venues.models import VenueIndex, VenuePage
|
||||||
|
|
||||||
|
|
||||||
|
class CustomImageFactory(wagtail_factories.ImageFactory):
|
||||||
|
class Meta:
|
||||||
|
model = CustomImage
|
||||||
|
|
||||||
|
|
||||||
|
class AssociationIndexFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Associations {n}")
|
||||||
|
lead = "<p>Foreninger og utvalg.</p>"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = AssociationIndex
|
||||||
|
|
||||||
|
|
||||||
|
class AssociationPageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Association {n}")
|
||||||
|
excerpt = "Et utdrag."
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = AssociationPage
|
||||||
|
|
||||||
|
|
||||||
|
class EventIndexFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Events {n}")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = EventIndex
|
||||||
|
|
||||||
|
|
||||||
|
class EventPageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Event {n}")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = EventPage
|
||||||
|
|
||||||
|
|
||||||
|
class GenericPageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Page {n}")
|
||||||
|
lead = "<p>Ingress.</p>"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = GenericPage
|
||||||
|
|
||||||
|
|
||||||
|
class StudioPageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Studio {n}")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = StudioPage
|
||||||
|
|
||||||
|
|
||||||
|
class NewsIndexFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"News {n}")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = NewsIndex
|
||||||
|
|
||||||
|
|
||||||
|
class NewsPageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Article {n}")
|
||||||
|
excerpt = "Et utdrag."
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = NewsPage
|
||||||
|
|
||||||
|
|
||||||
|
class VenueIndexFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Venues {n}")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = VenueIndex
|
||||||
|
|
||||||
|
|
||||||
|
class VenuePageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Venue {n}")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = VenuePage
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def root_page(db):
|
||||||
|
return Page.objects.get(depth=1)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def home_page(root_page):
|
||||||
|
# Wagtail's initial migration creates a default "Welcome" page at depth=2.
|
||||||
|
# Reuse it so we don't fight slug collisions across tests.
|
||||||
|
return root_page.get_children().first() or root_page.add_child(
|
||||||
|
instance=Page(title="Home", slug="home")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def event_index(home_page):
|
||||||
|
return EventIndexFactory(parent=home_page)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def news_index(home_page):
|
||||||
|
return NewsIndexFactory(parent=home_page)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def association_index(home_page):
|
||||||
|
return AssociationIndexFactory(parent=home_page)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def venue(home_page):
|
||||||
|
venue_index = VenueIndexFactory(parent=home_page)
|
||||||
|
return VenuePageFactory(parent=venue_index)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def graphql_post(client):
|
||||||
|
def _post(query, variables=None):
|
||||||
|
payload = {"query": query}
|
||||||
|
if variables is not None:
|
||||||
|
payload["variables"] = variables
|
||||||
|
response = client.post(
|
||||||
|
"/api/graphql/",
|
||||||
|
data=json.dumps(payload),
|
||||||
|
content_type="application/json",
|
||||||
|
)
|
||||||
|
return response, response.json()
|
||||||
|
|
||||||
|
return _post
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from events.admin import EventDateColumn, OrganizersColumn
|
||||||
|
from events.models import (
|
||||||
|
EventCategory,
|
||||||
|
EventOccurrence,
|
||||||
|
EventOrganizer,
|
||||||
|
EventOrganizerLink,
|
||||||
|
EventPage,
|
||||||
|
)
|
||||||
|
from tests.conftest import (
|
||||||
|
AssociationPageFactory,
|
||||||
|
CustomImageFactory,
|
||||||
|
EventPageFactory,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventpage_clean_unsets_specific_pricing_when_free():
|
||||||
|
page = EventPage(
|
||||||
|
title="Free event",
|
||||||
|
slug="free-event",
|
||||||
|
free=True,
|
||||||
|
price_regular="100",
|
||||||
|
price_student="50",
|
||||||
|
price_member="25",
|
||||||
|
)
|
||||||
|
|
||||||
|
page.clean()
|
||||||
|
|
||||||
|
assert page.price_regular == ""
|
||||||
|
assert page.price_student == ""
|
||||||
|
assert page.price_member == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventpage_clean_keeps_specific_pricing_when_not_free():
|
||||||
|
page = EventPage(
|
||||||
|
title="Paid event",
|
||||||
|
slug="paid-event",
|
||||||
|
free=False,
|
||||||
|
price_regular="100",
|
||||||
|
price_student="50",
|
||||||
|
price_member="25",
|
||||||
|
)
|
||||||
|
|
||||||
|
page.clean()
|
||||||
|
|
||||||
|
assert page.price_regular == "100"
|
||||||
|
assert page.price_student == "50"
|
||||||
|
assert page.price_member == "25"
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventpage_clean_dedupes_organizers_by_name(event_index):
|
||||||
|
org_a = EventOrganizer.objects.create(name="DNS", slug="dns-a")
|
||||||
|
org_b = EventOrganizer.objects.create(name="DNS", slug="dns-b")
|
||||||
|
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=org_a)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=org_b)
|
||||||
|
|
||||||
|
event = EventPage.objects.get(pk=event.pk)
|
||||||
|
assert event.organizer_links.count() == 2
|
||||||
|
|
||||||
|
event.clean()
|
||||||
|
|
||||||
|
assert event.organizer_links.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventpage_clean_dedupes_three_duplicates_and_keeps_distinct(event_index):
|
||||||
|
dup_1 = EventOrganizer.objects.create(name="DNS", slug="dns-1")
|
||||||
|
dup_2 = EventOrganizer.objects.create(name="DNS", slug="dns-2")
|
||||||
|
dup_3 = EventOrganizer.objects.create(name="DNS", slug="dns-3")
|
||||||
|
distinct = EventOrganizer.objects.create(name="Studentersamfundet", slug="ss")
|
||||||
|
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
for organizer in (dup_1, dup_2, dup_3, distinct):
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=organizer)
|
||||||
|
|
||||||
|
event = EventPage.objects.get(pk=event.pk)
|
||||||
|
assert event.organizer_links.count() == 4
|
||||||
|
|
||||||
|
event.clean()
|
||||||
|
|
||||||
|
names = sorted(link.organizer.name for link in event.organizer_links.all())
|
||||||
|
assert names == ["DNS", "Studentersamfundet"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventoccurrence_clean_rejects_both_venue_and_venue_custom(event_index, venue):
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
occurrence = EventOccurrence(
|
||||||
|
event=event,
|
||||||
|
start=timezone.now(),
|
||||||
|
venue=venue,
|
||||||
|
venue_custom="Frederikkeplassen",
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError) as exc:
|
||||||
|
occurrence.clean()
|
||||||
|
assert "venue_custom" in exc.value.message_dict
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventoccurrence_clean_requires_venue_or_venue_custom(event_index):
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
occurrence = EventOccurrence(event=event, start=timezone.now())
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError) as exc:
|
||||||
|
occurrence.clean()
|
||||||
|
assert "venue" in exc.value.message_dict
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventpage_manager_future_filters_past_and_annotates(event_index):
|
||||||
|
now = timezone.now()
|
||||||
|
|
||||||
|
past = EventPageFactory(parent=event_index, title="Past")
|
||||||
|
EventOccurrence.objects.create(event=past, start=now - timedelta(days=7), venue_custom="Old")
|
||||||
|
|
||||||
|
future = EventPageFactory(parent=event_index, title="Future")
|
||||||
|
EventOccurrence.objects.create(event=future, start=now + timedelta(days=7), venue_custom="New")
|
||||||
|
|
||||||
|
results = list(EventPage.objects.live().future().order_by("next_occurrence"))
|
||||||
|
|
||||||
|
assert [p.pk for p in results] == [future.pk]
|
||||||
|
assert results[0].next_occurrence is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_future_includes_occurrence_late_today(event_index):
|
||||||
|
today_start = timezone.localtime(timezone.now()).replace(
|
||||||
|
hour=0, minute=0, second=0, microsecond=0
|
||||||
|
)
|
||||||
|
late_today = today_start + timedelta(hours=23, minutes=59)
|
||||||
|
|
||||||
|
event = EventPageFactory(parent=event_index, title="Late today")
|
||||||
|
EventOccurrence.objects.create(event=event, start=late_today, venue_custom="X")
|
||||||
|
|
||||||
|
assert event.pk in EventPage.objects.future().values_list("pk", flat=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_future_excludes_occurrence_just_before_today(event_index):
|
||||||
|
today_start = timezone.localtime(timezone.now()).replace(
|
||||||
|
hour=0, minute=0, second=0, microsecond=0
|
||||||
|
)
|
||||||
|
just_before_today = today_start - timedelta(seconds=1)
|
||||||
|
|
||||||
|
event = EventPageFactory(parent=event_index, title="Just past")
|
||||||
|
EventOccurrence.objects.create(event=event, start=just_before_today, venue_custom="X")
|
||||||
|
|
||||||
|
assert event.pk not in EventPage.objects.future().values_list("pk", flat=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_future_next_occurrence_picks_earliest_future_ignoring_past(event_index):
|
||||||
|
now = timezone.now()
|
||||||
|
soonest_future = now + timedelta(days=3)
|
||||||
|
|
||||||
|
event = EventPageFactory(parent=event_index, title="With history")
|
||||||
|
EventOccurrence.objects.create(event=event, start=now - timedelta(days=30), venue_custom="X")
|
||||||
|
EventOccurrence.objects.create(event=event, start=soonest_future, venue_custom="X")
|
||||||
|
EventOccurrence.objects.create(event=event, start=now + timedelta(days=10), venue_custom="X")
|
||||||
|
|
||||||
|
annotated = EventPage.objects.future().filter(pk=event.pk).first()
|
||||||
|
assert annotated is not None
|
||||||
|
assert abs((annotated.next_occurrence - soonest_future).total_seconds()) < 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_event_index_future_events_query(event_index, graphql_post):
|
||||||
|
upcoming = EventPageFactory(parent=event_index, title="Upcoming gig")
|
||||||
|
EventOccurrence.objects.create(
|
||||||
|
event=upcoming,
|
||||||
|
start=timezone.now() + timedelta(days=3),
|
||||||
|
venue_custom="Storsalen",
|
||||||
|
)
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
eventIndex {
|
||||||
|
futureEvents { title }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
titles = [e["title"] for e in body["data"]["eventIndex"]["futureEvents"]]
|
||||||
|
assert "Upcoming gig" in titles
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_event_index_future_events_ordered_by_next_occurrence(event_index, graphql_post):
|
||||||
|
now = timezone.now()
|
||||||
|
|
||||||
|
later = EventPageFactory(parent=event_index, title="Later gig")
|
||||||
|
EventOccurrence.objects.create(event=later, start=now + timedelta(days=10), venue_custom="X")
|
||||||
|
|
||||||
|
sooner = EventPageFactory(parent=event_index, title="Sooner gig")
|
||||||
|
EventOccurrence.objects.create(event=sooner, start=now + timedelta(days=3), venue_custom="X")
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
eventIndex {
|
||||||
|
futureEvents { title }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
titles = [e["title"] for e in body["data"]["eventIndex"]["futureEvents"]]
|
||||||
|
assert titles.index("Sooner gig") < titles.index("Later gig")
|
||||||
|
|
||||||
|
|
||||||
|
def test_event_date_column_no_occurrences(event_index):
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
column = EventDateColumn("event_date")
|
||||||
|
|
||||||
|
assert column.get_value(event) == "—"
|
||||||
|
|
||||||
|
|
||||||
|
def test_event_date_column_single_occurrence(event_index):
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
start = timezone.make_aware(datetime(2025, 7, 22, 19, 30))
|
||||||
|
EventOccurrence.objects.create(event=event, start=start, venue_custom="X")
|
||||||
|
column = EventDateColumn("event_date")
|
||||||
|
|
||||||
|
assert column.get_value(event) == "2025-07-22 kl 19:30"
|
||||||
|
|
||||||
|
|
||||||
|
def test_event_date_column_multiple_occurrences_shows_count(event_index):
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
now = timezone.now()
|
||||||
|
EventOccurrence.objects.create(event=event, start=now, venue_custom="X")
|
||||||
|
EventOccurrence.objects.create(event=event, start=now + timedelta(days=1), venue_custom="X")
|
||||||
|
EventOccurrence.objects.create(event=event, start=now + timedelta(days=2), venue_custom="X")
|
||||||
|
column = EventDateColumn("event_date")
|
||||||
|
|
||||||
|
assert column.get_value(event) == "3 forekomster"
|
||||||
|
|
||||||
|
|
||||||
|
def test_organizers_column_no_organizers(event_index):
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
column = OrganizersColumn("organizers")
|
||||||
|
|
||||||
|
assert column.get_value(event) == "—"
|
||||||
|
|
||||||
|
|
||||||
|
def test_organizers_column_single_organizer_shows_name(event_index):
|
||||||
|
org = EventOrganizer.objects.create(name="Forening A", slug="forening-a")
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=org)
|
||||||
|
column = OrganizersColumn("organizers")
|
||||||
|
|
||||||
|
assert column.get_value(event) == "Forening A"
|
||||||
|
|
||||||
|
|
||||||
|
def test_organizers_column_multiple_organizers_truncates_with_count(event_index):
|
||||||
|
org_a = EventOrganizer.objects.create(name="Forening A", slug="forening-a")
|
||||||
|
org_b = EventOrganizer.objects.create(name="Forening B", slug="forening-b")
|
||||||
|
org_c = EventOrganizer.objects.create(name="Forening C", slug="forening-c")
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=org_a, sort_order=0)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=org_b, sort_order=1)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=org_c, sort_order=2)
|
||||||
|
column = OrganizersColumn("organizers")
|
||||||
|
|
||||||
|
assert column.get_value(event) == "Forening A (+2)"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def comprehensive_event(event_index, venue, association_index):
|
||||||
|
"""A fully-populated paid EventPage exercising every field exposed via GraphQL."""
|
||||||
|
image = CustomImageFactory(
|
||||||
|
title="Cover",
|
||||||
|
alt="Et fotografi av en gris med solbriller",
|
||||||
|
attribution="Foto: Test",
|
||||||
|
)
|
||||||
|
|
||||||
|
konsert = EventCategory.objects.create(
|
||||||
|
name="Konsert", slug="konsert", show_in_filters=True, pig="pigHeadLogo"
|
||||||
|
)
|
||||||
|
klubb = EventCategory.objects.create(name="Klubb", slug="klubb")
|
||||||
|
|
||||||
|
association = AssociationPageFactory(
|
||||||
|
parent=association_index,
|
||||||
|
title="Internal",
|
||||||
|
association_type="forening",
|
||||||
|
)
|
||||||
|
internal_org = EventOrganizer.objects.create(
|
||||||
|
name="Internal", slug="internal", association=association
|
||||||
|
)
|
||||||
|
external_org = EventOrganizer.objects.create(
|
||||||
|
name="External",
|
||||||
|
slug="external",
|
||||||
|
external_url="https://external.example.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
event = EventPageFactory(
|
||||||
|
parent=event_index,
|
||||||
|
title="Et arrangement",
|
||||||
|
slug="et-arrangement",
|
||||||
|
subtitle="En undertekst",
|
||||||
|
lead="<p>Ingress.</p>",
|
||||||
|
body=[("paragraph", "<p>Body content.</p>")],
|
||||||
|
pig="automatic",
|
||||||
|
free=False,
|
||||||
|
price_regular="150",
|
||||||
|
price_student="100",
|
||||||
|
price_member="75",
|
||||||
|
ticket_url="https://example.com/tickets",
|
||||||
|
facebook_url="https://facebook.com/example",
|
||||||
|
featured_image=image,
|
||||||
|
)
|
||||||
|
event.categories.add(konsert, klubb)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=internal_org)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=external_org)
|
||||||
|
|
||||||
|
now = timezone.now()
|
||||||
|
EventOccurrence.objects.create(
|
||||||
|
event=event,
|
||||||
|
start=now + timedelta(days=5),
|
||||||
|
end=now + timedelta(days=5, hours=3),
|
||||||
|
venue=venue,
|
||||||
|
)
|
||||||
|
EventOccurrence.objects.create(
|
||||||
|
event=event,
|
||||||
|
start=now + timedelta(days=12),
|
||||||
|
end=now + timedelta(days=12, hours=2),
|
||||||
|
venue_custom="Frederikkeplassen",
|
||||||
|
)
|
||||||
|
|
||||||
|
event.save()
|
||||||
|
return event
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_event_index_returns_all_fields_for_comprehensive_event(
|
||||||
|
comprehensive_event, graphql_post
|
||||||
|
):
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
eventIndex {
|
||||||
|
futureEvents {
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
subtitle
|
||||||
|
lead
|
||||||
|
body {
|
||||||
|
blockType
|
||||||
|
field
|
||||||
|
... on RichTextBlock {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pig
|
||||||
|
free
|
||||||
|
priceRegular
|
||||||
|
priceStudent
|
||||||
|
priceMember
|
||||||
|
ticketUrl
|
||||||
|
facebookUrl
|
||||||
|
featuredImage {
|
||||||
|
alt
|
||||||
|
attribution
|
||||||
|
}
|
||||||
|
categories {
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
showInFilters
|
||||||
|
pig
|
||||||
|
}
|
||||||
|
organizers {
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
externalUrl
|
||||||
|
association {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
occurrences {
|
||||||
|
start
|
||||||
|
end
|
||||||
|
venueCustom
|
||||||
|
venue {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
|
||||||
|
events = body["data"]["eventIndex"]["futureEvents"]
|
||||||
|
event = next(e for e in events if e["title"] == "Et arrangement")
|
||||||
|
|
||||||
|
assert event["slug"] == "et-arrangement"
|
||||||
|
assert event["subtitle"] == "En undertekst"
|
||||||
|
assert "Ingress." in event["lead"]
|
||||||
|
assert event["pig"] == "automatic"
|
||||||
|
assert event["free"] is False
|
||||||
|
assert event["priceRegular"] == "150"
|
||||||
|
assert event["priceStudent"] == "100"
|
||||||
|
assert event["priceMember"] == "75"
|
||||||
|
assert event["ticketUrl"] == "https://example.com/tickets"
|
||||||
|
assert event["facebookUrl"] == "https://facebook.com/example"
|
||||||
|
|
||||||
|
assert event["featuredImage"]["alt"] == "Et fotografi av en gris med solbriller"
|
||||||
|
assert event["featuredImage"]["attribution"] == "Foto: Test"
|
||||||
|
|
||||||
|
assert event["body"][0]["blockType"] == "RichTextBlock"
|
||||||
|
assert "Body content." in event["body"][0]["value"]
|
||||||
|
|
||||||
|
categories_by_name = {c["name"]: c for c in event["categories"]}
|
||||||
|
assert set(categories_by_name) == {"Konsert", "Klubb"}
|
||||||
|
assert categories_by_name["Konsert"]["slug"] == "konsert"
|
||||||
|
assert categories_by_name["Konsert"]["showInFilters"] is True
|
||||||
|
assert categories_by_name["Konsert"]["pig"] == "pigHeadLogo"
|
||||||
|
assert categories_by_name["Klubb"]["showInFilters"] is False
|
||||||
|
|
||||||
|
organizers_by_name = {o["name"]: o for o in event["organizers"]}
|
||||||
|
assert set(organizers_by_name) == {"Internal", "External"}
|
||||||
|
assert organizers_by_name["Internal"]["association"]["title"] == "Internal"
|
||||||
|
assert organizers_by_name["Internal"]["externalUrl"] == ""
|
||||||
|
assert organizers_by_name["External"]["association"] is None
|
||||||
|
assert organizers_by_name["External"]["externalUrl"] == "https://external.example.com"
|
||||||
|
|
||||||
|
assert len(event["occurrences"]) == 2
|
||||||
|
venue_occ = next(o for o in event["occurrences"] if o["venue"] is not None)
|
||||||
|
custom_occ = next(o for o in event["occurrences"] if o["venueCustom"])
|
||||||
|
assert venue_occ["venueCustom"] == ""
|
||||||
|
assert venue_occ["venue"]["title"]
|
||||||
|
assert custom_occ["venue"] is None
|
||||||
|
assert custom_occ["venueCustom"] == "Frederikkeplassen"
|
||||||
|
|
||||||
|
venue_occ_db = comprehensive_event.occurrences.exclude(venue=None).get()
|
||||||
|
custom_occ_db = comprehensive_event.occurrences.exclude(venue_custom="").get()
|
||||||
|
assert datetime.fromisoformat(venue_occ["start"]) == venue_occ_db.start
|
||||||
|
assert datetime.fromisoformat(venue_occ["end"]) == venue_occ_db.end
|
||||||
|
assert datetime.fromisoformat(custom_occ["start"]) == custom_occ_db.start
|
||||||
|
assert datetime.fromisoformat(custom_occ["end"]) == custom_occ_db.end
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
from generic.models import GenericPage
|
||||||
|
from tests.conftest import GenericPageFactory
|
||||||
|
|
||||||
|
|
||||||
|
def test_generic_page_persists_via_factory(home_page):
|
||||||
|
page = GenericPageFactory(
|
||||||
|
parent=home_page,
|
||||||
|
title="Om oss",
|
||||||
|
slug="om-oss",
|
||||||
|
lead="<p>Ingress.</p>",
|
||||||
|
body=[("paragraph", "<p>Body content.</p>")],
|
||||||
|
pig="drink",
|
||||||
|
)
|
||||||
|
|
||||||
|
reloaded = GenericPage.objects.get(pk=page.pk)
|
||||||
|
assert reloaded.title == "Om oss"
|
||||||
|
assert reloaded.slug == "om-oss"
|
||||||
|
assert "Ingress." in reloaded.lead
|
||||||
|
assert reloaded.pig == "drink"
|
||||||
|
assert reloaded.body[0].block_type == "paragraph"
|
||||||
|
|
||||||
|
|
||||||
|
def test_generic_page_allows_recursive_children(home_page):
|
||||||
|
parent = GenericPageFactory(parent=home_page, title="Parent", slug="parent")
|
||||||
|
child = GenericPageFactory(parent=parent, title="Child", slug="child")
|
||||||
|
|
||||||
|
assert child.get_parent().specific == parent
|
||||||
|
assert list(parent.get_children().specific()) == [child]
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_generic_page_query(home_page, graphql_post):
|
||||||
|
GenericPageFactory(
|
||||||
|
parent=home_page,
|
||||||
|
title="Om oss",
|
||||||
|
slug="om-oss",
|
||||||
|
lead="<p>Ingress text.</p>",
|
||||||
|
body=[("paragraph", "<p>Body content.</p>")],
|
||||||
|
pig="drink",
|
||||||
|
)
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
page(slug: "om-oss", contentType: "generic.GenericPage") {
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
... on GenericPage {
|
||||||
|
lead
|
||||||
|
pig
|
||||||
|
body {
|
||||||
|
blockType
|
||||||
|
field
|
||||||
|
... on RichTextBlock {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
data = body["data"]["page"]
|
||||||
|
assert data["title"] == "Om oss"
|
||||||
|
assert data["slug"] == "om-oss"
|
||||||
|
assert "Ingress text." in data["lead"]
|
||||||
|
assert data["pig"] == "drink"
|
||||||
|
assert data["body"][0]["blockType"] == "RichTextBlock"
|
||||||
|
assert "Body content." in data["body"][0]["value"]
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
def test_graphql_endpoint_responds(db, graphql_post):
|
||||||
|
response, body = graphql_post("{ __schema { queryType { name } } }")
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body
|
||||||
|
assert body["data"]["__schema"]["queryType"]["name"] == "Query"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
from news.models import NewsPage
|
||||||
|
from tests.conftest import NewsPageFactory
|
||||||
|
|
||||||
|
|
||||||
|
def test_news_page_persists_via_factory(news_index):
|
||||||
|
page = NewsPageFactory(parent=news_index, title="Big news", excerpt="Short summary")
|
||||||
|
|
||||||
|
reloaded = NewsPage.objects.get(pk=page.pk)
|
||||||
|
assert reloaded.title == "Big news"
|
||||||
|
assert reloaded.excerpt == "Short summary"
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_news_index_query(news_index, graphql_post):
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
newsIndex {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
assert body["data"]["newsIndex"]["title"] == news_index.title
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openinghours.models import OpeningHoursItem, OpeningHoursSet
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def opening_hours_set(db):
|
||||||
|
return OpeningHoursSet.objects.create(
|
||||||
|
name="Vanlige åpningstider",
|
||||||
|
effective_from=datetime.date(2025, 1, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_opening_hours_set_str_with_end_date():
|
||||||
|
ohs = OpeningHoursSet(
|
||||||
|
name="Sommer",
|
||||||
|
effective_from=datetime.date(2025, 6, 1),
|
||||||
|
effective_to=datetime.date(2025, 8, 31),
|
||||||
|
)
|
||||||
|
assert str(ohs) == "Sommer (2025-06-01 - 2025-08-31)"
|
||||||
|
|
||||||
|
|
||||||
|
def test_opening_hours_set_str_uses_infinity_when_open_ended():
|
||||||
|
ohs = OpeningHoursSet(
|
||||||
|
name="Forever",
|
||||||
|
effective_from=datetime.date(2025, 1, 1),
|
||||||
|
effective_to=None,
|
||||||
|
)
|
||||||
|
assert str(ohs) == "Forever (2025-01-01 - ∞)"
|
||||||
|
|
||||||
|
|
||||||
|
def test_opening_hours_streamfield_week_roundtrip(opening_hours_set):
|
||||||
|
OpeningHoursItem.objects.create(
|
||||||
|
opening_hours_set=opening_hours_set,
|
||||||
|
function="glassbaren",
|
||||||
|
week=[
|
||||||
|
(
|
||||||
|
"week",
|
||||||
|
{
|
||||||
|
"monday": {
|
||||||
|
"time_from": datetime.time(15, 0),
|
||||||
|
"time_to": datetime.time(23, 0),
|
||||||
|
"custom": "",
|
||||||
|
},
|
||||||
|
"tuesday": {"time_from": None, "time_to": None, "custom": "Stengt"},
|
||||||
|
"wednesday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
"thursday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
"friday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
"saturday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
"sunday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
reloaded = OpeningHoursSet.objects.get(pk=opening_hours_set.pk)
|
||||||
|
item = reloaded.items.get()
|
||||||
|
assert item.function == "glassbaren"
|
||||||
|
|
||||||
|
week_block = item.week[0]
|
||||||
|
assert week_block.block_type == "week"
|
||||||
|
assert week_block.value["monday"]["time_from"] == datetime.time(15, 0)
|
||||||
|
assert week_block.value["monday"]["time_to"] == datetime.time(23, 0)
|
||||||
|
assert week_block.value["tuesday"]["custom"] == "Stengt"
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_opening_hours_sets_query(db, graphql_post):
|
||||||
|
OpeningHoursSet.objects.create(
|
||||||
|
name="Sommer 2025",
|
||||||
|
effective_from=datetime.date(2025, 6, 1),
|
||||||
|
effective_to=datetime.date(2025, 8, 31),
|
||||||
|
)
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
openingHoursSets {
|
||||||
|
name
|
||||||
|
effectiveFrom
|
||||||
|
effectiveTo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
names = [s["name"] for s in body["data"]["openingHoursSets"]]
|
||||||
|
assert "Sommer 2025" in names
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"""Round-trip tests for wagtail-headless-preview token resolution via grapple."""
|
||||||
|
|
||||||
|
from generic.models import GenericPage
|
||||||
|
from tests.conftest import GenericPageFactory
|
||||||
|
|
||||||
|
|
||||||
|
def test_generic_page_preview_token_resolves_draft(home_page, graphql_post):
|
||||||
|
"""A minted preview token returns the unsaved draft via grapple's page(token: …)."""
|
||||||
|
# Publish a baseline so there's a live revision to diverge from.
|
||||||
|
page = GenericPageFactory(parent=home_page, title="Original title", slug="generic-preview")
|
||||||
|
|
||||||
|
# Mutate in-memory to simulate unsaved editor state, then mint a token.
|
||||||
|
# create_page_preview() snapshots the current to_json() into a PagePreview row.
|
||||||
|
page.title = "Edited title (draft)"
|
||||||
|
preview = page.create_page_preview()
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query previewPage($token: String!) {
|
||||||
|
page: page(token: $token) {
|
||||||
|
__typename
|
||||||
|
... on GenericPage {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
variables={"token": preview.token},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
assert body["data"]["page"]["__typename"] == "GenericPage"
|
||||||
|
assert body["data"]["page"]["title"] == "Edited title (draft)"
|
||||||
|
|
||||||
|
# Live revision is unchanged — token short-circuits the published query.
|
||||||
|
assert GenericPage.objects.get(pk=page.pk).title == "Original title"
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
from studio.models import StudioPage
|
||||||
|
from tests.conftest import CustomImageFactory, StudioPageFactory
|
||||||
|
|
||||||
|
|
||||||
|
def test_studio_page_persists_via_factory(home_page):
|
||||||
|
logo = CustomImageFactory()
|
||||||
|
page = StudioPageFactory(
|
||||||
|
parent=home_page,
|
||||||
|
title="STUDiO",
|
||||||
|
slug="studio",
|
||||||
|
lead="<p>Ingress.</p>",
|
||||||
|
body=[("paragraph", "<p>Body content.</p>")],
|
||||||
|
pig="drink",
|
||||||
|
logo=logo,
|
||||||
|
)
|
||||||
|
|
||||||
|
reloaded = StudioPage.objects.get(pk=page.pk)
|
||||||
|
assert reloaded.title == "STUDiO"
|
||||||
|
assert reloaded.slug == "studio"
|
||||||
|
assert "Ingress." in reloaded.lead
|
||||||
|
assert reloaded.pig == "drink"
|
||||||
|
assert reloaded.body[0].block_type == "paragraph"
|
||||||
|
assert reloaded.logo == logo
|
||||||
|
|
||||||
|
|
||||||
|
def test_studio_page_is_singleton(home_page):
|
||||||
|
StudioPageFactory(parent=home_page, slug="studio")
|
||||||
|
|
||||||
|
assert StudioPage.can_create_at(home_page) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_studio_page_query(home_page, graphql_post):
|
||||||
|
logo = CustomImageFactory(alt="STUDiO-logo")
|
||||||
|
StudioPageFactory(
|
||||||
|
parent=home_page,
|
||||||
|
title="STUDiO",
|
||||||
|
slug="studio",
|
||||||
|
lead="<p>Ingress text.</p>",
|
||||||
|
body=[("paragraph", "<p>Body content.</p>")],
|
||||||
|
pig="drink",
|
||||||
|
logo=logo,
|
||||||
|
)
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
page: studioPage {
|
||||||
|
... on StudioPage {
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
lead
|
||||||
|
pig
|
||||||
|
logo {
|
||||||
|
alt
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
blockType
|
||||||
|
field
|
||||||
|
... on RichTextBlock {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
data = body["data"]["page"]
|
||||||
|
assert data["title"] == "STUDiO"
|
||||||
|
assert data["slug"] == "studio"
|
||||||
|
assert "Ingress text." in data["lead"]
|
||||||
|
assert data["pig"] == "drink"
|
||||||
|
assert data["logo"]["alt"] == "STUDiO-logo"
|
||||||
|
assert data["body"][0]["blockType"] == "RichTextBlock"
|
||||||
|
assert "Body content." in data["body"][0]["value"]
|
||||||
Generated
+399
-195
@@ -1,77 +1,113 @@
|
|||||||
version = 1
|
version = 1
|
||||||
revision = 2
|
revision = 3
|
||||||
requires-python = "==3.12.*"
|
requires-python = "==3.14.*"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aniso8601"
|
|
||||||
version = "9.0.1"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/cb/72/be3db445b03944bfbb2b02b82d00cb2a2bcf96275c4543f14bf60fa79e12/aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973", size = 47345, upload-time = "2021-03-02T01:33:22.944Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e3/04/e97c12dc034791d7b504860acfcdd2963fa21ae61eaca1c9d31245f812c3/aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f", size = 52754, upload-time = "2021-03-02T01:33:20.669Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyascii"
|
name = "anyascii"
|
||||||
version = "0.3.2"
|
version = "0.3.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/9f/52/93b9ea99063f7cf37fb67f5e3f49480686cbe7f228c48b9d713326223b6e/anyascii-0.3.2.tar.gz", hash = "sha256:9d5d32ef844fe225b8bc7cba7f950534fae4da27a9bf3a6bea2cb0ea46ce4730", size = 214052, upload-time = "2023-03-16T00:24:42.431Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/db/ba/edebda727008390936da4a9bf677c19cd63b32d51e864656d2cbd1028e25/anyascii-0.3.3.tar.gz", hash = "sha256:c94e9dd9d47b3d9494eca305fef9447d00b4bf1a32aff85aa746fa3ec7fb95c3", size = 264680, upload-time = "2025-06-29T03:33:30.427Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/4f/7b/a9a747e0632271d855da379532b05a62c58e979813814a57fa3b3afeb3a4/anyascii-0.3.2-py3-none-any.whl", hash = "sha256:3b3beef6fc43d9036d3b0529050b0c48bfad8bc960e9e562d7223cfb94fe45d4", size = 289923, upload-time = "2023-03-16T00:24:39.649Z" },
|
{ url = "https://files.pythonhosted.org/packages/c2/76/783b75a21ce3563b8709050de030ae253853b147bd52e141edc1025aa268/anyascii-0.3.3-py3-none-any.whl", hash = "sha256:f5ab5e53c8781a36b5a40e1296a0eeda2f48c649ef10c3921c1381b1d00dee7a", size = 345090, upload-time = "2025-06-29T03:33:28.356Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "asgiref"
|
name = "asgiref"
|
||||||
version = "3.8.1"
|
version = "3.9.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186, upload-time = "2024-03-22T14:39:36.863Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" },
|
{ url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "beautifulsoup4"
|
name = "beautifulsoup4"
|
||||||
version = "4.12.3"
|
version = "4.13.4"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "soupsieve" },
|
{ name = "soupsieve" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181, upload-time = "2024-01-17T16:53:17.902Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067, upload-time = "2025-04-15T17:05:13.836Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925, upload-time = "2024-01-17T16:53:12.779Z" },
|
{ url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2024.8.30"
|
version = "2025.8.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507, upload-time = "2024-08-30T01:55:04.365Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321, upload-time = "2024-08-30T01:55:02.591Z" },
|
{ url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "charset-normalizer"
|
name = "charset-normalizer"
|
||||||
version = "3.3.2"
|
version = "3.4.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809, upload-time = "2023-11-01T04:04:59.997Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892, upload-time = "2023-11-01T04:03:24.135Z" },
|
{ url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213, upload-time = "2023-11-01T04:03:25.66Z" },
|
{ url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404, upload-time = "2023-11-01T04:03:27.04Z" },
|
{ url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275, upload-time = "2023-11-01T04:03:28.466Z" },
|
{ url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518, upload-time = "2023-11-01T04:03:29.82Z" },
|
{ url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182, upload-time = "2023-11-01T04:03:31.511Z" },
|
{ url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869, upload-time = "2023-11-01T04:03:32.887Z" },
|
{ url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042, upload-time = "2023-11-01T04:03:34.412Z" },
|
{ url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275, upload-time = "2023-11-01T04:03:35.759Z" },
|
{ url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819, upload-time = "2023-11-01T04:03:37.216Z" },
|
{ url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415, upload-time = "2023-11-01T04:03:38.694Z" },
|
{ url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212, upload-time = "2023-11-01T04:03:40.07Z" },
|
{ url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167, upload-time = "2023-11-01T04:03:41.491Z" },
|
]
|
||||||
{ url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041, upload-time = "2023-11-01T04:03:42.836Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397, upload-time = "2023-11-01T04:03:44.467Z" },
|
[[package]]
|
||||||
{ url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543, upload-time = "2023-11-01T04:04:58.622Z" },
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "coverage"
|
||||||
|
version = "7.14.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/23/7f/d0720730a397a999ffc0fd3f5bebef347338e3a47b727da66fbb228e2ff2/coverage-7.14.0.tar.gz", hash = "sha256:057a6af2f160a85384cde4ab36f0d2777bae1057bae255f95413cdd382aa5c74", size = 919489, upload-time = "2026-05-10T18:02:31.397Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1c/18/b9a6586d73992807c26f9a5f274131be3d76b56b18a82b9392e2a25d2e45/coverage-7.14.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9aed9fa983514ca032790f3fe0d1c0e42ca7e16b42432af1706b50a9a46bef5d", size = 220036, upload-time = "2026-05-10T18:01:33.057Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f3/9b/4165a1d56ddc302a0e2d518fd9d412a4fd0b57562618c78c5f21c57194f5/coverage-7.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ba3b8390db29296dbbf49e91b6fe08f990743a90c8f447ba4c2ffc29670dfa63", size = 220368, upload-time = "2026-05-10T18:01:34.705Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/69/aa/c12e52a5ba148d9995229d557e3be6e554fe469addc0e9241b2f0956d8ea/coverage-7.14.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3a5d8e876dfa2f102e970b183863d6dedd023d3c0eeca1fe7a9787bc5f28b212", size = 251417, upload-time = "2026-05-10T18:01:36.949Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/51/ec641c26e6dca1b25a7d2035ba6ecb7c884ef1a100a9e42fbe4ce4405139/coverage-7.14.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ebb8f4614a3787d567e610bbfdf96a4798dd69a1afb1bd8ad228d4111fe6ff3", size = 253924, upload-time = "2026-05-10T18:01:38.985Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/c4/59c3de0bd1b538824173fd518fed51c1ce740ca5ed68e74545983f4053a9/coverage-7.14.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b9bf47223dd8db3d4c4b2e443b02bace480d428f0822c3f991600448a176c97", size = 255269, upload-time = "2026-05-10T18:01:40.957Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/a9/36dfa153a62040296f6e7febfdb20a5720622f6ef5a81a41e8237b9a5344/coverage-7.14.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3485a836550b303d006d57cc06e3d5afaabc642c77050b7c985a97b13e3776b8", size = 257583, upload-time = "2026-05-10T18:01:42.607Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/26/7b/cc2c048d4114d9ab1c2409e9ee365e5ae10736df6dffcfc9444effa6c708/coverage-7.14.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e7e88110bae996d199d1693ca8ec3fd52441d426401ae963437598667b4c5eb", size = 251434, upload-time = "2026-05-10T18:01:44.537Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ee/df/6770eaa576e604575e9a78055313250faef5faa84bd6f71a39fece519c43/coverage-7.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15228a6800ce7bdf1b74800595e56db7138cecb338fdbf044806e10dcf182dfe", size = 253280, upload-time = "2026-05-10T18:01:46.175Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ad/9e/1c0264514a3f98259a6d64765a397b2c8373e3ba59ee722a4802d3ec0c61/coverage-7.14.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9d26ac7f5398bafc5b57421ad994e8a4749e8a7a0e62d05ec7d53014d5963bfa", size = 251241, upload-time = "2026-05-10T18:01:48.732Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/64/16/4efdf3e3c4079cdbf0ece56a2fea872df9e8a3e15a13a0af4400e1075944/coverage-7.14.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2fb73254ff43c911c967a899e1359bc5049b4b115d6e8fbdde4937d0a2246cd5", size = 255516, upload-time = "2026-05-10T18:01:50.819Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/69/b1de96346603881b3d1bc8d6447c83200e1c9700ffbaff926ba01ff5724c/coverage-7.14.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:454a380af72c6adada298ed270d38c7a391288198dbfb8467f786f588751a90c", size = 251059, upload-time = "2026-05-10T18:01:52.773Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a4/66/2881853e0363a5e0a724d1103e53650795367471b6afb234f8b49e713bc6/coverage-7.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:65c86fb646d2bd2972e96bd1a8b45817ed907cee68655d6295fe7ec031d04cca", size = 252716, upload-time = "2026-05-10T18:01:54.506Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/55/5c/0d3305d002c41dcde873dbe456491e663dc55152ca526b630b5c47efd62f/coverage-7.14.0-cp314-cp314-win32.whl", hash = "sha256:6a6516b02a6101398e19a3f44820f69bab2590697f7def4331f668b14adaf828", size = 222788, upload-time = "2026-05-10T18:01:56.487Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f9/58/6e1b8f52fdc3184b47dc5037f5070d83a3d11042db1594b02d2a44d786c8/coverage-7.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:45e0f79d8351fa76e256716df91eab12890d32678b9590df7ae1042e4bd4cf5d", size = 223600, upload-time = "2026-05-10T18:01:58.497Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/70/a18c408e674bc26281cadaedc7351f929bd2094e191e4b15271c30b084cc/coverage-7.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:4b899594a8b2d81e5cc064a0d7f9cac2081fed91049456cae7676787e41549c9", size = 222168, upload-time = "2026-05-10T18:02:00.411Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/89/2681f071d238b62aff8dfc2ab44fc24cfdb38d1c01f391a80522ff5d3a16/coverage-7.14.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f580f8c80acd94ac72e863efe2cab791d8c38d153e0b463b92dfa000d5c84cd1", size = 220766, upload-time = "2026-05-10T18:02:02.313Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bd/c7/c987babafd9207ffa1995e1ef1f9b26762cf4963aa768a66b6f0501e4616/coverage-7.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a2bd259c442cd43c49b30fbafc51776eb19ea396faf159d26a83e6a0a5f13b0c", size = 221035, upload-time = "2026-05-10T18:02:04.017Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/e9/d6a5ac3b333088143d6fc877d398a9a674dc03124a2f776e131f03864823/coverage-7.14.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a706b908dfa85538863504c624b237a3cc34232bf403c057414ebfdb3b4d9f84", size = 262405, upload-time = "2026-05-10T18:02:05.915Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/38/b1/e70838d29a7c08e22d44398a46db90815bbcbf28de06992bd9210d1a8d8e/coverage-7.14.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7333cd944ee4393b9b3d3c1b598c936d4fc8d70573a4c7dacfec5590dd50e436", size = 264530, upload-time = "2026-05-10T18:02:07.582Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/73/5c31ef97763288d03d9995152b96d5475b527c63d91c84b01caea894b83a/coverage-7.14.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f162bc9a15b82d947b02651b0c7e1609d6f7a8735ca330cfadec8481dd97d5a", size = 266932, upload-time = "2026-05-10T18:02:09.401Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e1/76/dd56d80f29c5f05b4d76f7e7c6d47cafacae017189c75c5759d24f9ff0cc/coverage-7.14.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:362cb78e01a5dc82009d88004cf60f2e6b6d6fcbfdec05b05af73b0abf40118f", size = 268062, upload-time = "2026-05-10T18:02:11.399Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6e/c7/27ba85cd5b95614f159ff93ebff1901584a8d192e2e5e24c4943a7453f59/coverage-7.14.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:acebd068fca5512c3a6fde9c045f901613478781a73f0e82b307b214daef23fb", size = 261504, upload-time = "2026-05-10T18:02:13.257Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/2e/e8149f60ab5d5684c6eee881bdf34b127115cddbb958b196768dd9d63473/coverage-7.14.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:29fe3da551dface75deb2ccbf87b6b66e2e7ef38f6d89050b428be94afff3490", size = 264398, upload-time = "2026-05-10T18:02:15.063Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d9/7f/1261b025285323225f4b4abffa5a643649dfd67e25ddca7ebcbdea3b7cb3/coverage-7.14.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b4cc4fce8672fffcb09b0eafc167b396b3ba53c4a7230f54b7aaffbf6c835fa9", size = 262000, upload-time = "2026-05-10T18:02:16.756Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d3/dc/829c54f60b9d08389439c00f813c752781c496fc5788c78d8006db4b4f2b/coverage-7.14.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5d4a51aad8ba8bdcd2b8bd8f03d4aca19693fa2327a3470e4718a25b03481020", size = 265732, upload-time = "2026-05-10T18:02:18.817Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ed/b0/70bd1419941652fa062689cba9c3eeafb8f5e6fbb890bce41c3bdda5dbd6/coverage-7.14.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9f323af3e1e4f68b60b7b247e37b8515563a61375518fa59de1af48ba28a3db6", size = 260847, upload-time = "2026-05-10T18:02:20.528Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/73/be40b2390656c654d35ea0015ea7ba3d945769cf80790ad5e0bb2d56d2ba/coverage-7.14.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1a0abc7342ea9711c469dd8b821c6c311e6bc6aac1442e5fbd6b27fae0a8f3db", size = 263166, upload-time = "2026-05-10T18:02:22.337Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/29/55/4a643f712fcf7cf2881f8ec1e0ccb7b164aff3108f69b51801246c8799f2/coverage-7.14.0-cp314-cp314t-win32.whl", hash = "sha256:a9f864ef57b7172e2db87a096642dd51e179e085ab6b2c371c29e885f65c8fb2", size = 223573, upload-time = "2026-05-10T18:02:24.11Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/27/96/3acae5da0953be042c0b4dea6d6789d2f080701c77b88e44d5bd41b9219b/coverage-7.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:29943e552fdc08e082eb51400fb2f58e118a83b5542bd06531214e084399b644", size = 224680, upload-time = "2026-05-10T18:02:25.896Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/3d/6ab5d2dd8325d838737c6f8d83d62eb6230e0d70b87b51b57bbfd08fa767/coverage-7.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:742a73ea621953b012f2c4c2219b512180dd84489acf5b1596b0aafc55b9100b", size = 222703, upload-time = "2026-05-10T18:02:27.822Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/e8/cb8e80d6f9f55b99588625062822bf946cf03ed06315df4bd8397f5632a1/coverage-7.14.0-py3-none-any.whl", hash = "sha256:8de5b61163aee3d05c8a2beab6f47913df7981dad1baf82c414d99158c286ab1", size = 211764, upload-time = "2026-05-10T18:02:29.538Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -85,53 +121,52 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django"
|
name = "django"
|
||||||
version = "5.1"
|
version = "6.0.5"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "asgiref" },
|
{ name = "asgiref" },
|
||||||
{ name = "sqlparse" },
|
{ name = "sqlparse" },
|
||||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/1e/0c/d854d25bb74a8a3b41e642bbd27fe6af12fadd0edfd07d487809cf0ef719/Django-5.1.tar.gz", hash = "sha256:848a5980e8efb76eea70872fb0e4bc5e371619c70fffbe48e3e1b50b2c09455d", size = 10681050, upload-time = "2024-08-07T13:34:10.857Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/5e/f1/bf85f0d29ef76abf901f193fe8fef4769d3da7794197832bc30151c071d8/django-6.0.5.tar.gz", hash = "sha256:bc6d6872e98a2864c836e42edd644b362db311147dd5aa8d5b82ba7a032f5269", size = 10924131, upload-time = "2026-05-05T13:54:39.329Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/28/b4/110532cebfea2244d76119904da98c6fa045ebb202aee9ec7cbf36ea3cad/Django-5.1-py3-none-any.whl", hash = "sha256:d3b811bf5371a26def053d7ee42a9df1267ef7622323fe70a601936725aa4557", size = 8246099, upload-time = "2024-08-07T13:33:52.959Z" },
|
{ url = "https://files.pythonhosted.org/packages/94/5b/1328f8b84fce040c404f76822bf8c57d254e368e8cbd8bd67ec2b26d75f5/django-6.0.5-py3-none-any.whl", hash = "sha256:9d58a7cb49244e74c8e161d5e403a46d6209f1009ba40f5a66d6aa0d0786a8f0", size = 8368680, upload-time = "2026-05-05T13:54:33.532Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-extensions"
|
name = "django-extensions"
|
||||||
version = "3.2.3"
|
version = "4.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/8a/f1/318684c9466968bf9a9c221663128206e460c1a67f595055be4b284cde8a/django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a", size = 277216, upload-time = "2023-06-05T17:09:01.447Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/6d/b3/ed0f54ed706ec0b54fd251cc0364a249c6cd6c6ec97f04dc34be5e929eac/django_extensions-4.1.tar.gz", hash = "sha256:7b70a4d28e9b840f44694e3f7feb54f55d495f8b3fa6c5c0e5e12bcb2aa3cdeb", size = 283078, upload-time = "2025-04-11T01:15:39.617Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/a7/7e/ba12b9660642663f5273141018d2bec0a1cae1711f4f6d1093920e157946/django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401", size = 229868, upload-time = "2023-06-05T17:08:58.197Z" },
|
{ url = "https://files.pythonhosted.org/packages/64/96/d967ca440d6a8e3861120f51985d8e5aec79b9a8bdda16041206adfe7adc/django_extensions-4.1-py3-none-any.whl", hash = "sha256:0699a7af28f2523bf8db309a80278519362cd4b6e1fd0a8cd4bf063e1e023336", size = 232980, upload-time = "2025-04-11T01:15:37.701Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-filter"
|
name = "django-filter"
|
||||||
version = "24.3"
|
version = "25.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/50/bc/dc19ae39c235332926dd0efe0951f663fa1a9fc6be8430737ff7fd566b20/django_filter-24.3.tar.gz", hash = "sha256:d8ccaf6732afd21ca0542f6733b11591030fa98669f8d15599b358e24a2cd9c3", size = 144444, upload-time = "2024-08-02T13:27:58.132Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b5/40/c702a6fe8cccac9bf426b55724ebdf57d10a132bae80a17691d0cf0b9bac/django_filter-25.1.tar.gz", hash = "sha256:1ec9eef48fa8da1c0ac9b411744b16c3f4c31176c867886e4c48da369c407153", size = 143021, upload-time = "2025-02-14T16:30:53.238Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/09/b1/92f1c30b47c1ebf510c35a2ccad9448f73437e5891bbd2b4febe357cc3de/django_filter-24.3-py3-none-any.whl", hash = "sha256:c4852822928ce17fb699bcfccd644b3574f1a2d80aeb2b4ff4f16b02dd49dc64", size = 95011, upload-time = "2024-08-02T13:27:55.616Z" },
|
{ url = "https://files.pythonhosted.org/packages/07/a6/70dcd68537c434ba7cb9277d403c5c829caf04f35baf5eb9458be251e382/django_filter-25.1-py3-none-any.whl", hash = "sha256:4fa48677cf5857b9b1347fed23e355ea792464e0fe07244d1fdfb8a806215b80", size = 94114, upload-time = "2025-02-14T16:30:50.435Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-modelcluster"
|
name = "django-modelcluster"
|
||||||
version = "6.3"
|
version = "6.5"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
{ name = "pytz" },
|
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/3d/dd/db1ab2e256f9fda6796ac312686561cbe2740d960c920f74410559ef7739/django-modelcluster-6.3.tar.gz", hash = "sha256:0caed8a0e889f3abb92f144670878a466ef954ffa6c4c7b9c80e6426b720a49d", size = 28903, upload-time = "2024-02-26T19:26:53.185Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/34/0f/c5fd0c280a10224d619325783bfaab5a54903f82340e41ae21ab3127ebc9/django_modelcluster-6.5.tar.gz", hash = "sha256:459cbf0fb3bbc8c15ea9a2a062abc0d1ef935a38e04aa8ac3cb241c826bbc6dd", size = 29707, upload-time = "2026-05-01T12:25:33.524Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/ee/6d/8deb2dfde2e1177412b759a1c32713be19b1d3af1da686e44e64c165e8c0/django_modelcluster-6.3-py2.py3-none-any.whl", hash = "sha256:a8783d6565a0663f41cd6003ea361c3a5711e8a2a326160f1ec1eceb3e973d4f", size = 29039, upload-time = "2024-02-26T19:26:50.903Z" },
|
{ url = "https://files.pythonhosted.org/packages/cd/57/cdcdea9a56d114e9217d52ce9a8af19ad7fe8a0996faf8480d3f286c02e0/django_modelcluster-6.5-py3-none-any.whl", hash = "sha256:469b380a294027d5211d0e5d5746e0381f445b058d2229977a58fe0f1c58a596", size = 29865, upload-time = "2026-05-01T12:25:31.886Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -146,40 +181,67 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/e6/5b/216157ff053f955b15b9dcdc13bfb6e406666445164fee9e332e141be96d/django_permissionedforms-0.1-py2.py3-none-any.whl", hash = "sha256:d341a961a27cc77fde8cc42141c6ab55cc1f0cb886963cc2d6967b9674fa47d6", size = 5744, upload-time = "2022-02-28T19:40:26.337Z" },
|
{ url = "https://files.pythonhosted.org/packages/e6/5b/216157ff053f955b15b9dcdc13bfb6e406666445164fee9e332e141be96d/django_permissionedforms-0.1-py2.py3-none-any.whl", hash = "sha256:d341a961a27cc77fde8cc42141c6ab55cc1f0cb886963cc2d6967b9674fa47d6", size = 5744, upload-time = "2022-02-28T19:40:26.337Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-stubs-ext"
|
||||||
|
version = "5.2.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/fc/06/5e94715d103e6cc72380cb0d0b6682a7d5ad2c366cee478c94d77aad777d/django_stubs_ext-5.2.2.tar.gz", hash = "sha256:d9d151b919fe2438760f5bd938f03e1cb08c84d0651f9e5917f1313907e42683", size = 6244, upload-time = "2025-07-17T08:34:35.054Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4e/38/2903676f97f7902ee31984a06756b0e8836e897f4b617e1a03be4a43eb4f/django_stubs_ext-5.2.2-py3-none-any.whl", hash = "sha256:8833bbe32405a2a0ce168d3f75a87168f61bd16939caf0e8bf173bccbd8a44c5", size = 8816, upload-time = "2025-07-17T08:34:33.715Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-taggit"
|
name = "django-taggit"
|
||||||
version = "5.0.1"
|
version = "6.1.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/10/eb/269501702e231b552f68d813d0a07ac1ea81fd25a3c92fb0f249818f0f39/django-taggit-5.0.1.tar.gz", hash = "sha256:edcd7db1e0f35c304e082a2f631ddac2e16ef5296029524eb792af7430cab4cc", size = 60372, upload-time = "2023-10-29T11:57:20.124Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/34/a6/f1beaf8f552fe90c153cc039316ebab942c23dfbc88588dde081fefca816/django_taggit-6.1.0.tar.gz", hash = "sha256:c4d1199e6df34125dd36db5eb0efe545b254dec3980ce5dd80e6bab3e78757c3", size = 38151, upload-time = "2024-09-29T08:07:39.477Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/07/18/94a45c8c50592d5cf7d73a9111053917c3684fda031c988041396bb3e588/django_taggit-5.0.1-py3-none-any.whl", hash = "sha256:a0ca8a28b03c4b26c2630fd762cb76ec39b5e41abf727a7b66f897a625c5e647", size = 61141, upload-time = "2023-10-29T11:56:57.954Z" },
|
{ url = "https://files.pythonhosted.org/packages/6b/34/4185c345530b91d05cb82e05d07148f481a5eb5dc2ac44e092b3daa6f206/django_taggit-6.1.0-py3-none-any.whl", hash = "sha256:ab776264bbc76cb3d7e49e1bf9054962457831bd21c3a42db9138b41956e4cf0", size = 75749, upload-time = "2024-09-29T08:07:14.612Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-tasks"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "django-stubs-ext" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/60/b1/064645bf246a1f5b46d9638755b1869ea44a6d05e7c7c12841fddebb71f6/django_tasks-0.9.0.tar.gz", hash = "sha256:971b3829efeee68147f7deced8d21b907131b11ec7953af83eb94b11f128a24d", size = 32343, upload-time = "2025-10-17T16:21:08.58Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/e1/ce539ce1e21be71696649f84b6dbd508b2c0b89b559a6e620478bb126f7c/django_tasks-0.9.0-py3-none-any.whl", hash = "sha256:fddc344934a605d9eafa08ac8ba32c0cde9da23ef534e03a41f09fa0417b535a", size = 44057, upload-time = "2025-10-17T16:21:07.283Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-treebeard"
|
name = "django-treebeard"
|
||||||
version = "4.7.1"
|
version = "5.1.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/bb/24/eaccbce17355380cb3a8fe6ad92a85b76453dc1f0ecd04f48bfe8929065b/django-treebeard-4.7.1.tar.gz", hash = "sha256:846e462904b437155f76e04907ba4e48480716855f88b898df4122bdcfbd6e98", size = 294139, upload-time = "2024-01-31T16:35:19.751Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/f7/16/aa732ea1033586e4f946d8e47be9116ffb22b050485b179a7cafb82dfc34/django_treebeard-5.1.0.tar.gz", hash = "sha256:8ac2ba41307469a679c98188124933bc3baade36b02480343d1a301a1fca0700", size = 302339, upload-time = "2026-05-12T02:06:37.002Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/87/79/259966820614746cc4d81762edf97a53bf1e3b8e74797c010d310c6f4a8f/django_treebeard-4.7.1-py3-none-any.whl", hash = "sha256:995c7120153ab999898fe3043bbdcd8a0fc77cc106eb94de7350e9d02c885135", size = 93210, upload-time = "2024-01-31T16:35:17.843Z" },
|
{ url = "https://files.pythonhosted.org/packages/42/d3/311b9c43950238744f946540c48ea7068bf9e55bea976908a7be13b73082/django_treebeard-5.1.0-py3-none-any.whl", hash = "sha256:d0f68fcf1b49158e38d2322aa62a37bc0b89452d66a16f88e58dbe076d043c28", size = 78591, upload-time = "2026-05-12T02:06:35.62Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "djangorestframework"
|
name = "djangorestframework"
|
||||||
version = "3.15.2"
|
version = "3.16.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/2c/ce/31482eb688bdb4e271027076199e1aa8d02507e530b6d272ab8b4481557c/djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad", size = 1067420, upload-time = "2024-06-19T07:59:32.891Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/8a/95/5376fe618646fde6899b3cdc85fd959716bb67542e273a76a80d9f326f27/djangorestframework-3.16.1.tar.gz", hash = "sha256:166809528b1aced0a17dc66c24492af18049f2c9420dbd0be29422029cfc3ff7", size = 1089735, upload-time = "2025-08-06T17:50:53.251Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/7c/b6/fa99d8f05eff3a9310286ae84c4059b08c301ae4ab33ae32e46e8ef76491/djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20", size = 1071235, upload-time = "2024-06-19T07:59:26.106Z" },
|
{ url = "https://files.pythonhosted.org/packages/b0/ce/bf8b9d3f415be4ac5588545b5fcdbbb841977db1c1d923f7568eeabe1689/djangorestframework-3.16.1-py3-none-any.whl", hash = "sha256:33a59f47fb9c85ede792cbf88bde71893bcda0667bc573f784649521f1102cec", size = 1080442, upload-time = "2025-08-06T17:50:50.667Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -193,44 +255,80 @@ dependencies = [
|
|||||||
{ name = "psycopg2-binary" },
|
{ name = "psycopg2-binary" },
|
||||||
{ name = "wagtail" },
|
{ name = "wagtail" },
|
||||||
{ name = "wagtail-grapple" },
|
{ name = "wagtail-grapple" },
|
||||||
|
{ name = "wagtail-headless-preview" },
|
||||||
{ name = "whitenoise" },
|
{ name = "whitenoise" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dev-dependencies]
|
[package.dev-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
|
{ name = "pytest" },
|
||||||
|
{ name = "pytest-cov" },
|
||||||
|
{ name = "pytest-django" },
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
|
{ name = "wagtail-factories" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "django", specifier = ">=5.0.7,<6" },
|
{ name = "django", specifier = ">=6.0.5,<7" },
|
||||||
{ name = "django-extensions", specifier = ">=3.2.3,<4" },
|
{ name = "django-extensions", specifier = ">=4.1,<5" },
|
||||||
{ name = "gunicorn", specifier = ">=23.0.0" },
|
{ name = "gunicorn", specifier = ">=26.0.0,<27" },
|
||||||
{ name = "psycopg2-binary", specifier = ">=2.9.10,<3" },
|
{ name = "psycopg2-binary", specifier = ">=2.9.12,<3" },
|
||||||
{ name = "wagtail", specifier = ">=6.1.3,<7" },
|
{ name = "wagtail", specifier = ">=7.4.1,<8" },
|
||||||
{ name = "wagtail-grapple", specifier = ">=0.26.0,<0.27" },
|
{ name = "wagtail-grapple", specifier = ">=0.31.0,<0.32" },
|
||||||
{ name = "whitenoise", specifier = ">=6.7.0,<7" },
|
{ name = "wagtail-headless-preview", specifier = ">=0.8,<0.9" },
|
||||||
|
{ name = "whitenoise", specifier = ">=6.12.0,<7" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
dev = [{ name = "ruff" }]
|
dev = [
|
||||||
|
{ name = "pytest", specifier = ">=9.0.3,<10" },
|
||||||
|
{ name = "pytest-cov", specifier = ">=7.0.0,<8" },
|
||||||
|
{ name = "pytest-django", specifier = ">=4.12.0,<5" },
|
||||||
|
{ name = "ruff", specifier = ">=0.15.13,<0.16" },
|
||||||
|
{ name = "wagtail-factories", specifier = ">=4.4.0,<5" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "draftjs-exporter"
|
name = "draftjs-exporter"
|
||||||
version = "5.0.0"
|
version = "5.1.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/29/5b/7729b1a95d48d2f51b03feacda1d5b17058e1caec524592e59d0df801a8c/draftjs_exporter-5.0.0.tar.gz", hash = "sha256:2efee45d4bb4c0aaacc3e5ea2983a29a29381e02037f3f92a6b12706d7b87e1e", size = 33271, upload-time = "2022-04-03T22:00:47.094Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/d1/52/8b98525ab5477410bdbaf279c5fe0a99108211d40818bb769460784c1c41/draftjs_exporter-5.1.0.tar.gz", hash = "sha256:9f44b8dcecb702540e3aab24af2fad8683aec910fe0034c12cfab5d716ac5f84", size = 33500, upload-time = "2025-02-21T17:32:57.175Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/58/f4/95ad9b91194156f0e87877d640428b1c7be70a90979796ecdcb77adcfb4f/draftjs_exporter-5.0.0-py3-none-any.whl", hash = "sha256:8cb9d2d51284233decfe274802f1c53e257158c62b9f53ed2399de3fa80ac561", size = 26320, upload-time = "2022-04-03T22:00:44.647Z" },
|
{ url = "https://files.pythonhosted.org/packages/dc/99/26d5524aaa3e89266e0af19332053aa9f8a61b1c39b29c0dc709f43fbb29/draftjs_exporter-5.1.0-py3-none-any.whl", hash = "sha256:c32932b7933b994fd5ea74c1decf47b5c41e13ba06363f5b69b76ef10137d4e9", size = 26302, upload-time = "2025-02-21T17:32:54.4Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "et-xmlfile"
|
name = "et-xmlfile"
|
||||||
version = "1.1.0"
|
version = "2.0.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/3d/5d/0413a31d184a20c763ad741cc7852a659bf15094c24840c5bdd1754765cd/et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c", size = 3218, upload-time = "2021-04-26T13:26:05.068Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/96/c2/3dd434b0108730014f1b96fd286040dc3bcb70066346f7e01ec2ac95865f/et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada", size = 4688, upload-time = "2021-04-26T13:26:03.429Z" },
|
{ url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "factory-boy"
|
||||||
|
version = "3.3.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "faker" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146, upload-time = "2025-02-03T09:49:04.433Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036, upload-time = "2025-02-03T09:49:01.659Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "faker"
|
||||||
|
version = "40.18.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/18/06/70886e82d8f1d2b73454f3a7c1b7405300128df22e70d85a828951366932/faker-40.18.0.tar.gz", hash = "sha256:2207575c0e8f90e6ccd6dbef764de875c614d16d3db4eee9712d9a00087f2e70", size = 1968243, upload-time = "2026-05-14T16:43:04.834Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/84/0b/5c0b2d3a4b7a715f1835dd3f963bfbe841a02ae5cad1df8ee0325dfad235/faker-40.18.0-py3-none-any.whl", hash = "sha256:61a6b94b74605ddb090a065deb197a1c585ae7a874c094cf6693671d271e6083", size = 2006355, upload-time = "2026-05-14T16:43:02.489Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -244,21 +342,22 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphene"
|
name = "graphene"
|
||||||
version = "3.3"
|
version = "3.4.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aniso8601" },
|
|
||||||
{ name = "graphql-core" },
|
{ name = "graphql-core" },
|
||||||
{ name = "graphql-relay" },
|
{ name = "graphql-relay" },
|
||||||
|
{ name = "python-dateutil" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/82/75/02875858c7c09fc156840181cdee27b23408fac75720a2e1e9128f3d48a5/graphene-3.3.tar.gz", hash = "sha256:529bf40c2a698954217d3713c6041d69d3f719ad0080857d7ee31327112446b0", size = 57893, upload-time = "2023-07-26T06:48:39.083Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/cc/f6/bf62ff950c317ed03e77f3f6ddd7e34aaa98fe89d79ebd660c55343d8054/graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa", size = 44739, upload-time = "2024-11-09T20:44:25.757Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/24/70/96f6027cdfc9bb89fc07627b615cb43fb1c443c93498412beaeaf157e9f1/graphene-3.3-py2.py3-none-any.whl", hash = "sha256:bb3810be33b54cb3e6969506671eb72319e8d7ba0d5ca9c8066472f75bf35a38", size = 128227, upload-time = "2023-07-26T06:48:37.766Z" },
|
{ url = "https://files.pythonhosted.org/packages/66/e0/61d8e98007182e6b2aca7cf65904721fb2e4bce0192272ab9cb6f69d8812/graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71", size = 114894, upload-time = "2024-11-09T20:44:23.851Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphene-django"
|
name = "graphene-django"
|
||||||
version = "3.2.2"
|
version = "3.2.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
@@ -268,18 +367,18 @@ dependencies = [
|
|||||||
{ name = "promise" },
|
{ name = "promise" },
|
||||||
{ name = "text-unidecode" },
|
{ name = "text-unidecode" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/09/f1/4eb1c0ae79814aba03e2347f664cb0bef72816fbe99d4399378bb22bef47/graphene-django-3.2.2.tar.gz", hash = "sha256:059ccf25d9a5159f28d7ebf1a648c993ab34deb064e80b70ca096aa22a609556", size = 87591, upload-time = "2024-06-12T02:58:09.439Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/6f/7a/8aef131349329dcd167578720f6364412f1728bfda14bba22c1e7b5d8365/graphene-django-3.2.3.tar.gz", hash = "sha256:d831bfe8e9a6e77e477b7854faef4addb318f386119a69ee4c57b74560f3e07d", size = 88393, upload-time = "2025-03-13T08:33:03.949Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/da/71/6800d05144ee4fb57f471571dd80fc9c6b2196e6243d39ed3d3654082824/graphene_django-3.2.2-py2.py3-none-any.whl", hash = "sha256:0fd95c8c1cbe77ae2a5940045ce276803c3acbf200a156731e0c730f2776ae2c", size = 114204, upload-time = "2024-06-12T02:58:07.897Z" },
|
{ url = "https://files.pythonhosted.org/packages/18/35/ab9c668222f6271a0b71efb147c46816229dbb9fa5d15ee6b025eb08d4b2/graphene_django-3.2.3-py2.py3-none-any.whl", hash = "sha256:0c673a4dad315b26b4d18eb379ad0c7027fd6a36d23a1848b7c7c09a14a9271e", size = 114959, upload-time = "2025-03-13T08:33:02.453Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphql-core"
|
name = "graphql-core"
|
||||||
version = "3.2.3"
|
version = "3.2.6"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/a6/94df9045ca1bac404c7b394094cd06713f63f49c7a4d54d99b773ae81737/graphql-core-3.2.3.tar.gz", hash = "sha256:06d2aad0ac723e35b1cb47885d3e5c45e956a53bc1b209a9fc5369007fe46676", size = 529552, upload-time = "2022-09-23T08:37:13.684Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/c4/16/7574029da84834349b60ed71614d66ca3afe46e9bf9c7b9562102acb7d4f/graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab", size = 505353, upload-time = "2025-01-26T16:36:27.374Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f8/39/e5143e7ec70939d2076c1165ae9d4a3815597019c4d797b7f959cf778600/graphql_core-3.2.3-py3-none-any.whl", hash = "sha256:5766780452bd5ec8ba133f8bf287dc92713e3868ddd83aee4faab9fc3e303dc3", size = 202921, upload-time = "2022-09-23T08:37:11.825Z" },
|
{ url = "https://files.pythonhosted.org/packages/ae/4f/7297663840621022bc73c22d7d9d80dbc78b4db6297f764b545cd5dd462d/graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f", size = 203416, upload-time = "2025-01-26T16:36:24.868Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -296,48 +395,57 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gunicorn"
|
name = "gunicorn"
|
||||||
version = "23.0.0"
|
version = "26.0.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "packaging" },
|
{ name = "packaging" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/6d/b7/a4a3f632f823e432ce6bc65f62961b7980c898c77f075a2f7118cb3846fe/gunicorn-26.0.0.tar.gz", hash = "sha256:ca9346f85e3a4aeeb64d491045c16b9a35647abd37ea15efe53080eb8b090baf", size = 727286, upload-time = "2026-05-05T06:38:25.529Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" },
|
{ url = "https://files.pythonhosted.org/packages/e6/40/9c2384fc2be4ad25dd4a49decd5ad9ea5a3639814c11bd40ab77cb9f0a14/gunicorn-26.0.0-py3-none-any.whl", hash = "sha256:40233d26a5f0d1872916188c276e21641155111c2853f0c2cd55260aec0d24fc", size = 212009, upload-time = "2026-05-05T06:38:23.007Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.8"
|
version = "3.10"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/e349c5e6d4543326c6883ee9491e3921e0d07b55fdf3cce184b40d63e72a/idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603", size = 189467, upload-time = "2024-08-23T16:01:51.339Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/22/7e/d71db821f177828df9dea8c42ac46473366f191be53080e552e628aad991/idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", size = 66894, upload-time = "2024-08-23T16:01:49.963Z" },
|
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "l18n"
|
name = "iniconfig"
|
||||||
version = "2021.3"
|
version = "2.3.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||||
{ name = "pytz" },
|
|
||||||
{ name = "six" },
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ff/9c/13d732e16e8fbdef7e68f4f339f3b86618430d4003c7ffe82e15bf925d7c/l18n-2021.3.tar.gz", hash = "sha256:1956e890d673d17135cc20913253c154f6bc1c00266c22b7d503cc1a5a42d848", size = 50712, upload-time = "2021-11-12T09:32:36.255Z" }
|
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/6c/e7/dfa82d0bb2b314950e457a755463b429090127ffc0ab9d8d14ef4563ad44/l18n-2021.3-py3-none-any.whl", hash = "sha256:78495d1df95b6f7dcc694d1ba8994df709c463a1cbac1bf016e1b9a5ce7280b9", size = 51534, upload-time = "2021-11-12T09:32:34.296Z" },
|
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "laces"
|
name = "laces"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/cb/d69f2cbb248bd6baceb7597d8c6c229797689b546ac2a446fac15b8f455f/laces-0.1.1.tar.gz", hash = "sha256:e45159c46f6adca33010d34e9af869e57201b70675c6dc088e919b16c89456a4", size = 26889, upload-time = "2024-02-10T23:34:22.055Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/74/9a/9192d6a74e2c6db4f705dd98f56be488e47373172c13f4916aeabc4d68b8/laces-0.1.2.tar.gz", hash = "sha256:3218e09c1889ae5cf3fc7a82f5bb63ec0c7879889b6a9760bfc42323c694b84d", size = 29264, upload-time = "2025-01-14T04:37:34.805Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/e0/dc/ceadbdc5e14aec7bd01bdd6ef8d5bf704d6a12d1b5a25bcaaa066bdb820d/laces-0.1.1-py3-none-any.whl", hash = "sha256:ae2c575b9aaa46154e5518c61c9f86f5a9478f753a51e9c5547c7d275d361242", size = 21349, upload-time = "2024-02-10T23:34:20.37Z" },
|
{ url = "https://files.pythonhosted.org/packages/60/fe/31f76f5cb2579bdda208aa257ce5482653f22ab1bad3e128fe2f803fa2f1/laces-0.1.2-py3-none-any.whl", hash = "sha256:980cdaf9a31e883a2b8198132e2388931a4eb8814f5bfa5d8bba13ff9f657b7c", size = 22462, upload-time = "2025-01-14T04:37:30.636Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "modelsearch"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "django-tasks" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/dd/25/81121175512af4a4b152f6db3ea749535659f5890f0987cd481646527dff/modelsearch-1.3.1.tar.gz", hash = "sha256:f3b2e304dbef9f7c838423f591cfe0c60f4cef584bae8793d2aa8ac35a3c46e0", size = 103935, upload-time = "2026-04-27T23:37:08.841Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/f1/63cf88da72bc557594a543dd19a2c7e219acccc70cb2b1e2639204580fe6/modelsearch-1.3.1-py3-none-any.whl", hash = "sha256:a92847f01788d0d615e8715fabb8e823029288840297fb9e7235ee03ad49b6a8", size = 127463, upload-time = "2026-04-27T23:37:07.651Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -363,39 +471,59 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pillow"
|
name = "pillow"
|
||||||
version = "10.4.0"
|
version = "11.3.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload-time = "2024-07-01T09:48:43.583Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350, upload-time = "2024-07-01T09:46:17.177Z" },
|
{ url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980, upload-time = "2024-07-01T09:46:19.169Z" },
|
{ url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799, upload-time = "2024-07-01T09:46:21.883Z" },
|
{ url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973, upload-time = "2024-07-01T09:46:24.321Z" },
|
{ url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054, upload-time = "2024-07-01T09:46:26.825Z" },
|
{ url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484, upload-time = "2024-07-01T09:46:29.355Z" },
|
{ url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375, upload-time = "2024-07-01T09:46:31.756Z" },
|
{ url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773, upload-time = "2024-07-01T09:46:33.73Z" },
|
{ url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690, upload-time = "2024-07-01T09:46:36.587Z" },
|
{ url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951, upload-time = "2024-07-01T09:46:38.777Z" },
|
{ url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427, upload-time = "2024-07-01T09:46:43.15Z" },
|
{ url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pillow-heif"
|
name = "pillow-heif"
|
||||||
version = "0.18.0"
|
version = "1.1.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pillow" },
|
{ name = "pillow" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/bb/e7797fe7f5cad447bb470f916ead38f0929e8d28bdf6bd5d9f31dfe1ac26/pillow_heif-0.18.0.tar.gz", hash = "sha256:70318dad9faa76121c6592ac0ab59881ff0dac6ab791a922e70d82c7706cce88", size = 16172675, upload-time = "2024-07-27T16:21:21.889Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/6f/d4/597cf8c54d1ed494a46cc12e358d2f73a993b5f139402b35681f56beffde/pillow_heif-1.1.0.tar.gz", hash = "sha256:6c0c5f81a780185bbddc56e0d5537c53aa6cb5fb6018f5a60534a47c53f5455d", size = 18271020, upload-time = "2025-08-02T09:58:32.54Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/5a/48/4bdce48d77c307b50bba0c77d6485e156f0fefcca273ce007351ad1deb40/pillow_heif-0.18.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:c795e7ccceea33e01e49ce536139f94cabb1bf017393666f76c05a9daebae2da", size = 5279030, upload-time = "2024-07-27T16:20:00.876Z" },
|
{ url = "https://files.pythonhosted.org/packages/cf/93/e32cd695bca9750f6fa166f28a4c84fd8a23209f99d56712359c0b42f2e6/pillow_heif-1.1.0-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:c47f12a30a86c43f714353d0fb8478dc4da7915d678642e0b6cf6f0fd53d711e", size = 3384304, upload-time = "2025-08-02T09:57:55.997Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/3c/d8/8f2f8f44b6fc3689ace680b21655a0eabb1393f8e66b8851b0f95b8aac1c/pillow_heif-0.18.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:4dd5b3ec09be45c1ef63be31773df90e18ee08e5e950018b0a349924b54a24ac", size = 3732529, upload-time = "2024-07-27T16:20:02.849Z" },
|
{ url = "https://files.pythonhosted.org/packages/62/88/3e7cda3383c165a5eebf9a0193450de2b810b7654dbc363c8c26a87be6f2/pillow_heif-1.1.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3cc4a7a808b659f657c5144aeec8152d261d8f3c4a859eba66ef31dab26982d2", size = 2261529, upload-time = "2025-08-02T09:57:57.6Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f8/61/f0aac50a7ad051e1cb71ed7a8d3a94b5e54ea8ed1acb03586f0fcdebd730/pillow_heif-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb2eade59c2654c2643a3b637de37c19e75a77c66a3e9a5e0ae26210e4f48aee", size = 6759746, upload-time = "2024-07-27T16:20:04.776Z" },
|
{ url = "https://files.pythonhosted.org/packages/bf/f2/469d9ef5c64efecbc0559aa56ebe6539007f478d8aee83253037dc203567/pillow_heif-1.1.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f3b0df4a03f9c7f150d801f3644961df5af0739503d6fc5d3e0aeb540d16c3d", size = 5780880, upload-time = "2025-08-02T09:57:59.408Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4b/71/a8b3684f64307b96ea0ae14594564b41fdf4d4a6f7df5ef73d180a71db5d/pillow_heif-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35b59d599bfdb8454739db6b92f0841ecadbe887babb5ed5abd5299587843eef", size = 7586203, upload-time = "2024-07-27T16:20:06.811Z" },
|
{ url = "https://files.pythonhosted.org/packages/13/74/a5c13f96ba06f4ec7d85a6d343b91c2767bd5a3fd90c146cf3c59fbcb12c/pillow_heif-1.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44fcd3a171337788bf672882484725f124143d2f290cfdbf029c22c113a15c79", size = 5502822, upload-time = "2025-08-02T09:58:00.841Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d8/59/3b1c90f7d366fb653f5f072803ae6350e608bea0d0d0ea65d6e7764c470f/pillow_heif-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:26a0b8b8e899e7bcc876ee61fcadb0f0b849bd6a0d5c20f0e969c77a43b40568", size = 8119771, upload-time = "2024-07-27T16:20:08.491Z" },
|
{ url = "https://files.pythonhosted.org/packages/57/04/1a6a88647dbb0eec95fa8b9d5cac501f690cdd83479e7cacf56772f1189f/pillow_heif-1.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f07f28e99ae74dffca936e24d8881f3e7e572233cf1f7bc16fe42fb08a795be", size = 6822779, upload-time = "2025-08-02T09:58:02.238Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/48/20/a72297dc260d0bf8fce8f209d36ceb498b2f1e8beb478a0ab3f565c9a3c9/pillow_heif-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0276a3e0c667677ed0c67f4512cdf2f674065018049307ba4de5cb4648b0a33e", size = 8850943, upload-time = "2024-07-27T16:20:10.262Z" },
|
{ url = "https://files.pythonhosted.org/packages/ae/37/8873625e681505940381cff6a4a191301f98d9a5c1499f60a2e892ad8c89/pillow_heif-1.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7a6de39cfba4037479aa5deb65a6bb0b27da0a11b95f740202f03b578ee8932f", size = 6429866, upload-time = "2025-08-02T09:58:03.637Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/12/98/2bcb2790618cfb8ce2054256a4d3bc2597288bf82444cc87fdc815484c38/pillow_heif-0.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:5916fa31f2015626dd2372d14e24521ea6caed11b25be14faa9b9c67731087ce", size = 8547707, upload-time = "2024-07-27T16:20:12.594Z" },
|
{ url = "https://files.pythonhosted.org/packages/6b/3c/63cbbe3a6a54e3ec925d2433bb431a4bf61695f34b5f1e689db642fb20c1/pillow_heif-1.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:514c856230995dc2f918fb12d83906885d42829e911868e23035e2d916c4c7c5", size = 5573890, upload-time = "2025-08-02T09:58:05.049Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pluggy"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -409,36 +537,89 @@ sdist = { url = "https://files.pythonhosted.org/packages/cf/9c/fb5d48abfe5d791cd
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psycopg2-binary"
|
name = "psycopg2-binary"
|
||||||
version = "2.9.10"
|
version = "2.9.12"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764, upload-time = "2024-10-16T11:24:58.126Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/2a/60/a3624f79acea344c16fbef3a94d28b89a8042ddfb8f3e4ca83f538671409/psycopg2_binary-2.9.12.tar.gz", hash = "sha256:5ac9444edc768c02a6b6a591f070b8aae28ff3a99be57560ac996001580f294c", size = 379686, upload-time = "2026-04-21T09:40:34.304Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771, upload-time = "2024-10-16T11:20:35.234Z" },
|
{ url = "https://files.pythonhosted.org/packages/13/1b/708c0dca874acfad6d65314271859899a79007686f3a1f74e82a2ed4b645/psycopg2_binary-2.9.12-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f3b3de8a74ef8db215f22edffb19e32dc6fa41340456de7ec99efdc8a7b3ec2", size = 3712428, upload-time = "2026-04-20T23:35:23.453Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336, upload-time = "2024-10-16T11:20:38.742Z" },
|
{ url = "https://files.pythonhosted.org/packages/d6/39/ddbea9d4b4de6aca9431b6ed253f530f8a02d3b8f9bcfd0dbfe2b3de6fe4/psycopg2_binary-2.9.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1006fb62f0f0bc5ce256a832356c6262e91be43f5e4eb15b5eaf38079464caf2", size = 3823184, upload-time = "2026-04-20T23:35:25.92Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637, upload-time = "2024-10-16T11:20:42.145Z" },
|
{ url = "https://files.pythonhosted.org/packages/bf/a0/bc2fef74b106fa345567122a0659e6d94512ed7dc0131ec44c9e5aba3725/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:840066105706cd2eb29b9a1c2329620056582a4bf3e8169dec5c447042d0869f", size = 4579157, upload-time = "2026-04-20T23:35:28.542Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097, upload-time = "2024-10-16T11:20:46.185Z" },
|
{ url = "https://files.pythonhosted.org/packages/57/d7/d4e3b2005d3de607ca4fbb0e8742e248056e52184a6b94ebda3c1c2c329b/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:863f5d12241ebe1c76a72a04c2113b6dc905f90b9cef0e9be0efd994affd9354", size = 4274970, upload-time = "2026-04-20T23:35:30.418Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776, upload-time = "2024-10-16T11:20:50.879Z" },
|
{ url = "https://files.pythonhosted.org/packages/2e/42/c9853f8db3967fe08bcde11f53d53b85d351750cae726ce001cb68afa9c1/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a99eaab34a9010f1a086b126de467466620a750634d114d20455f3a824aae033", size = 5895175, upload-time = "2026-04-20T23:35:33.584Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968, upload-time = "2024-10-16T11:20:56.819Z" },
|
{ url = "https://files.pythonhosted.org/packages/eb/fd/b82b5601a97630308bef079f545ffec481bbbc795c2ba5ec416a01d03f60/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ffdd7dc5463ccd61845ac37b7012d0f35a1548df9febe14f8dd549be4a0bc81e", size = 4110658, upload-time = "2026-04-20T23:35:35.638Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334, upload-time = "2024-10-16T11:21:02.411Z" },
|
{ url = "https://files.pythonhosted.org/packages/62/8c/32ca69b0389ef25dd22937bf9e8fbe2ce27aea20b05ded48c4ce4cb42475/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:54a0dfecab1b48731f934e06139dfe11e24219fb6d0ceb32177cf0375f14c7b5", size = 3656251, upload-time = "2026-04-20T23:35:37.854Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722, upload-time = "2024-10-16T11:21:09.01Z" },
|
{ url = "https://files.pythonhosted.org/packages/c4/29/96992a2b59e3b9d730fcf9612d0a387305025dc867a9fc490a9e496e074e/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:96937c9c5d891f772430f418a7a8b4691a90c3e6b93cf72b5bd7cad8cbca32a5", size = 3301810, upload-time = "2026-04-20T23:35:39.927Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132, upload-time = "2024-10-16T11:21:16.339Z" },
|
{ url = "https://files.pythonhosted.org/packages/56/ad/44b06659949b243ae10112cd3b20a197f9bf3e81d5651379b9eb889bfaad/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:77b348775efd4cdab410ec6609d81ccecd1139c90265fa583a7255c8064bc03d", size = 3048977, upload-time = "2026-04-20T23:35:41.806Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312, upload-time = "2024-10-16T11:21:25.584Z" },
|
{ url = "https://files.pythonhosted.org/packages/1d/f2/10a1bcebadb6aa55e280e1f58975c36a7b560ea525184c7aa4064c466633/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:527e6342b3e44c2f0544f6b8e927d60de7f163f5723b8f1dfa7d2a84298738cd", size = 3351466, upload-time = "2026-04-20T23:35:43.993Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191, upload-time = "2024-10-16T11:21:29.912Z" },
|
{ url = "https://files.pythonhosted.org/packages/20/be/b732c8418ffa5bcfda002890f5dc4c869fc17db66ff11f53b17cfe44afc0/psycopg2_binary-2.9.12-cp314-cp314-win_amd64.whl", hash = "sha256:f12ae41fcafadb39b2785e64a40f9db05d6de2ac114077457e0e7c597f3af980", size = 2848762, upload-time = "2026-04-20T23:35:46.421Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031, upload-time = "2024-10-16T11:21:34.211Z" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytz"
|
name = "pygments"
|
||||||
version = "2024.1"
|
version = "2.20.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/90/26/9f1f00a5d021fff16dee3de13d43e5e978f3d58928e129c3a62cf7eb9738/pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", size = 316214, upload-time = "2024-02-02T01:18:41.693Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/9c/3d/a121f284241f08268b21359bd425f7d4825cffc5ac5cd0e1b3d82ffd2b10/pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319", size = 505474, upload-time = "2024-02-02T01:18:37.283Z" },
|
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest"
|
||||||
|
version = "9.0.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "iniconfig" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pluggy" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest-cov"
|
||||||
|
version = "7.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "coverage" },
|
||||||
|
{ name = "pluggy" },
|
||||||
|
{ name = "pytest" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest-django"
|
||||||
|
version = "4.12.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pytest" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/13/2b/db9a193df89e5660137f5428063bcc2ced7ad790003b26974adf5c5ceb3b/pytest_django-4.12.0.tar.gz", hash = "sha256:df94ec819a83c8979c8f6de13d9cdfbe76e8c21d39473cfe2b40c9fc9be3c758", size = 91156, upload-time = "2026-02-14T18:40:49.235Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/a5/41d091f697c09609e7ef1d5d61925494e0454ebf51de7de05f0f0a728f1d/pytest_django-4.12.0-py3-none-any.whl", hash = "sha256:3ff300c49f8350ba2953b90297d23bf5f589db69545f56f1ec5f8cff5da83e85", size = 26123, upload-time = "2026-02-14T18:40:47.381Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dateutil"
|
||||||
|
version = "2.9.0.post0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "six" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
version = "2.32.3"
|
version = "2.32.4"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "certifi" },
|
{ name = "certifi" },
|
||||||
@@ -446,61 +627,61 @@ dependencies = [
|
|||||||
{ name = "idna" },
|
{ name = "idna" },
|
||||||
{ name = "urllib3" },
|
{ name = "urllib3" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" },
|
{ url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.12.4"
|
version = "0.15.13"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/24/21/a7d5c126d5b557715ef81098f3db2fe20f622a039ff2e626af28d674ab80/ruff-0.15.13.tar.gz", hash = "sha256:f9d89f17f7ba7fb2ed42921f0df75da797a9a5d71bc39049e2c687cf2baf44b7", size = 4678180, upload-time = "2026-05-14T13:44:37.869Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" },
|
{ url = "https://files.pythonhosted.org/packages/c6/61/11d458dc6ac22504fd8e237b29dfd40504c7fbbcc8930402cfe51a8e63ed/ruff-0.15.13-py3-none-linux_armv6l.whl", hash = "sha256:444b580fc72fd6887e650acd3e575e18cdc79dbcf42fb4030b491057921f61f8", size = 10738279, upload-time = "2026-05-14T13:44:18.7Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" },
|
{ url = "https://files.pythonhosted.org/packages/86/ca/caa871ee7be718c45256fada4e16a218ee3e33f0c4a46b729a60a24912e6/ruff-0.15.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6590d009e7cb7ebf36f83dbdd44a3fa48a0994ff6f1cdc1b08006abe58f98dc7", size = 11124798, upload-time = "2026-05-14T13:44:06.427Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" },
|
{ url = "https://files.pythonhosted.org/packages/d3/19/43f5f2e568dddde567fc41f8471f9432c09563e19d3e617a48cfa52f8f0a/ruff-0.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1c26d2f66163deeb6e08d8b39fbbe983ce3c71cea06a6d7591cfd1421793c629", size = 10460761, upload-time = "2026-05-14T13:44:04.375Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" },
|
{ url = "https://files.pythonhosted.org/packages/99/df/cf938cd6de3003178f03ad7c1ea2a6c099468c03a35037985070b37e76be/ruff-0.15.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbd6f94b434f896308e4d57fb7bfde0d02b99f7a64b3bdab0fdfa6a864203a5", size = 10804451, upload-time = "2026-05-14T13:44:25.221Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" },
|
{ url = "https://files.pythonhosted.org/packages/c7/7d/5d0973129b154ded2225729169d7068f26b467760b146493fde138415f23/ruff-0.15.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3259f3be4d181bda591da5db2571aed6853c6a048157756448020bc6c5cd22", size = 10534285, upload-time = "2026-05-14T13:44:08.888Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" },
|
{ url = "https://files.pythonhosted.org/packages/1f/e3/6b999bbc66cd51e5f073842bc2a3995e99c5e0e72e16b15e7261f7abf57a/ruff-0.15.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae9c17e5eb4430c154e76abc25d79a318190f5a997f38fb6b114416c5319ffc9", size = 11312063, upload-time = "2026-05-14T13:44:11.274Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" },
|
{ url = "https://files.pythonhosted.org/packages/af/5a/642639e9f5db04f1e97fbd6e091c6fd20725bdf072fb114d00eefb9e6eb8/ruff-0.15.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e2e39bff6c341f4b577a21b801326fab0b11847f48fcaa83f00a113c9b3cb55", size = 12183079, upload-time = "2026-05-14T13:44:01.634Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" },
|
{ url = "https://files.pythonhosted.org/packages/19/4c/7585735f6b53b0f12de13618b2f7d250a844f018822efc899df2e7b8295f/ruff-0.15.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8d9a8e08013542e94d3220bc5b62cc3e5ef87c5f74bff367d3fac14fab013e6", size = 11440833, upload-time = "2026-05-14T13:43:59.043Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" },
|
{ url = "https://files.pythonhosted.org/packages/e8/31/bf1a0803d077e679cfeee5f2f67290a0fa79c7385b5d9a8c17b9db2c48f0/ruff-0.15.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc411dfebe5eebe55ce041c6ae080eb7668955e866daa2fbb16692a784f1c4ca", size = 11434486, upload-time = "2026-05-14T13:44:27.761Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" },
|
{ url = "https://files.pythonhosted.org/packages/e1/4e/62c9b999875d4f14db80f277c030578f5e249c9852d65b7ac7ad0b43c041/ruff-0.15.13-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:768494eb08b9cee54e2fd27969966f74db5a57f6eaa7a90fcb3306af34dfc4bd", size = 11385189, upload-time = "2026-05-14T13:44:13.704Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" },
|
{ url = "https://files.pythonhosted.org/packages/fc/89/7e959047a104df3eb12863447c110140191fc5b6c4f379ea2e803fcdb0e4/ruff-0.15.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fb75f9a3a7e42ffe117d734494e6c5e5cb3565d66e12612cb63d0e572a41a5b6", size = 10781380, upload-time = "2026-05-14T13:43:56.734Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" },
|
{ url = "https://files.pythonhosted.org/packages/ff/52/5fd18f3b88cab63e88aa11516b3b4e1e5f720e5c330f8dbe5c26210f41f8/ruff-0.15.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8cb74dd33bb2f6613faf7fc03b660053b5ac4f80e706d5788c6335e2a8048d51", size = 10540605, upload-time = "2026-05-14T13:44:20.748Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" },
|
{ url = "https://files.pythonhosted.org/packages/e8/e0/9e35f338990d3e41a82875ff7053ffe97541dae81c9d02143177f381d572/ruff-0.15.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7ef823f817fcd191dc934e984be9cf4094f808effa16f2542ad8e821ba02bbf2", size = 11036554, upload-time = "2026-05-14T13:44:16.256Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" },
|
{ url = "https://files.pythonhosted.org/packages/c2/13/070fb048c24080fba188f66371e2a92785be257ad02242066dc7255ac6e9/ruff-0.15.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f345a13937bd7f09f6f5d19fa0721b0c103e00e7f62bc67089a8e5e037719e0b", size = 11528133, upload-time = "2026-05-14T13:44:22.808Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" },
|
{ url = "https://files.pythonhosted.org/packages/6b/8c/b1e1666aef7fc6555094d73ae6cd981701781ae85b97ceefc0eebd0b4668/ruff-0.15.13-py3-none-win32.whl", hash = "sha256:4044f94208b3b05ba0fc4a4abd0558cf4d6459bd18325eead7fd8cc66f909b41", size = 10721455, upload-time = "2026-05-14T13:44:35.697Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" },
|
{ url = "https://files.pythonhosted.org/packages/ab/a6/870a3e8a50590bb92be184ad928c2922f088b00d9dc5c5ec7b924ee08c22/ruff-0.15.13-py3-none-win_amd64.whl", hash = "sha256:7064884d442b7d477b4e7473d12da7f08851d2b1982763c5d3f388a19468a1a4", size = 11900409, upload-time = "2026-05-14T13:44:30.389Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" },
|
{ url = "https://files.pythonhosted.org/packages/9b/36/9c015cd052fca743dae8cb2aeb16b551444787467db42ceab0fc968865af/ruff-0.15.13-py3-none-win_arm64.whl", hash = "sha256:2471da9bd1068c8c064b5fd9c0c4b6dddffd6369cb1cd68b29993b1709ff1b21", size = 11179336, upload-time = "2026-05-14T13:44:33.026Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.16.0"
|
version = "1.17.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041, upload-time = "2021-05-05T14:18:18.379Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053, upload-time = "2021-05-05T14:18:17.237Z" },
|
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "soupsieve"
|
name = "soupsieve"
|
||||||
version = "2.6"
|
version = "2.7"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569, upload-time = "2024-08-13T13:39:12.166Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186, upload-time = "2024-08-13T13:39:10.986Z" },
|
{ url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlparse"
|
name = "sqlparse"
|
||||||
version = "0.5.1"
|
version = "0.5.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/73/82/dfa23ec2cbed08a801deab02fe7c904bfb00765256b155941d789a338c68/sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e", size = 84502, upload-time = "2024-07-15T19:30:27.085Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload-time = "2024-12-10T12:05:30.728Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/5d/a5/b2860373aa8de1e626b2bdfdd6df4355f0565b47e51f7d0c54fe70faf8fe/sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4", size = 44156, upload-time = "2024-07-15T19:30:25.033Z" },
|
{ url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -522,26 +703,35 @@ wheels = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tzdata"
|
name = "typing-extensions"
|
||||||
version = "2024.1"
|
version = "4.14.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559, upload-time = "2024-02-11T23:22:40.2Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370, upload-time = "2024-02-11T23:22:38.223Z" },
|
{ url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tzdata"
|
||||||
|
version = "2025.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "2.2.2"
|
version = "2.5.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/43/6d/fa469ae21497ddc8bc93e5877702dca7cb8f911e337aca7452b5724f1bb6/urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168", size = 292266, upload-time = "2024-06-17T13:40:11.401Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/ca/1c/89ffc63a9605b583d5df2be791a27bc1a42b7c32bab68d3c8f2f73a98cd4/urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", size = 121444, upload-time = "2024-06-17T13:40:07.795Z" },
|
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wagtail"
|
name = "wagtail"
|
||||||
version = "6.2.1"
|
version = "7.4.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyascii" },
|
{ name = "anyascii" },
|
||||||
@@ -551,34 +741,48 @@ dependencies = [
|
|||||||
{ name = "django-modelcluster" },
|
{ name = "django-modelcluster" },
|
||||||
{ name = "django-permissionedforms" },
|
{ name = "django-permissionedforms" },
|
||||||
{ name = "django-taggit" },
|
{ name = "django-taggit" },
|
||||||
|
{ name = "django-tasks" },
|
||||||
{ name = "django-treebeard" },
|
{ name = "django-treebeard" },
|
||||||
{ name = "djangorestframework" },
|
{ name = "djangorestframework" },
|
||||||
{ name = "draftjs-exporter" },
|
{ name = "draftjs-exporter" },
|
||||||
{ name = "l18n" },
|
|
||||||
{ name = "laces" },
|
{ name = "laces" },
|
||||||
|
{ name = "modelsearch" },
|
||||||
{ name = "openpyxl" },
|
{ name = "openpyxl" },
|
||||||
{ name = "pillow" },
|
{ name = "pillow" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
{ name = "telepath" },
|
{ name = "telepath" },
|
||||||
{ name = "willow", extra = ["heif"] },
|
{ name = "willow", extra = ["heif"] },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/6d/97/97b59c6bcd913f394e9f475dd952466954e9959ca9e10a45d4b251141eae/wagtail-6.2.1.tar.gz", hash = "sha256:0f136ef23b157997a44fa46543a320a31437350951cf13add8ea8b69cc5e8385", size = 6516190, upload-time = "2024-08-20T15:40:59.778Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/81/da/c060bd9ec00335336e04a610cd1d6762e459ade9c4983f7ecddae15af36d/wagtail-7.4.1.tar.gz", hash = "sha256:c7232cbe16afa5842c236e069b2baead3b3e87e9dc90a7493330b54b69e18b83", size = 6944979, upload-time = "2026-05-19T17:26:18.762Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/ca/b0/942f6bca14bfc2fbe786ebbaa90a831b71cbe3592e3da3f34d6be9f775b1/wagtail-6.2.1-py3-none-any.whl", hash = "sha256:31d073ea8acdc973ef45c5719977a1bb122ad0fc3f01348f37e922128200b42a", size = 8976424, upload-time = "2024-08-20T15:40:54.365Z" },
|
{ url = "https://files.pythonhosted.org/packages/53/c8/162fff3c5ef9e158127c9f4aa1f0d38edd8d1fa242629ae1d8af3b6306c7/wagtail-7.4.1-py3-none-any.whl", hash = "sha256:a42dc18b0fb818649c0d704ea50a2dea06d1b75614abdf837e90766263645c76", size = 9597601, upload-time = "2026-05-19T17:26:12.53Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wagtail-factories"
|
||||||
|
version = "4.4.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "factory-boy" },
|
||||||
|
{ name = "wagtail" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/42/8c/2ece22c2757bb3f6ef34f36cf185246b136134cf594b7b1dde92cb0a331f/wagtail_factories-4.4.0.tar.gz", hash = "sha256:c77c13d438a2e999a9220ff1829f060116013aa519a81944577353f460ba8cba", size = 9939, upload-time = "2026-02-10T15:14:13.7Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4c/85/bd5aeaa54a10b6defca927f75bc3890acb7c3a9a23df8e10f3aa42e75807/wagtail_factories-4.4.0-py2.py3-none-any.whl", hash = "sha256:be0abb96d36bf0e3c733d7520fc1944441a379ab06d93af447fc4e71b67e2a01", size = 10754, upload-time = "2026-02-10T15:14:12.773Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wagtail-grapple"
|
name = "wagtail-grapple"
|
||||||
version = "0.26.0"
|
version = "0.31.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "graphene-django" },
|
{ name = "graphene-django" },
|
||||||
{ name = "wagtail" },
|
{ name = "wagtail" },
|
||||||
{ name = "wagtail-headless-preview" },
|
{ name = "wagtail-headless-preview" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/36/2ad789a09d78b786eccf359ff88a596bcacfff4b9dd6505b68ad3c7f62f5/wagtail_grapple-0.26.0.tar.gz", hash = "sha256:68be091f7feb95bfbe0377bd03c1814a64a6150ae964ec593670d995f11780e5", size = 38362, upload-time = "2024-06-26T10:39:30.511Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/9c/29/8cab682abde7f3a8d78a29aac6e441b7a9a20d9baf850c7fc438ad086c48/wagtail_grapple-0.31.0.tar.gz", hash = "sha256:5a70e88dca5188181e42306b207b1e38cf5df4452aa62e764b9e997e2ea19797", size = 39240, upload-time = "2026-04-21T09:24:28.195Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/7a/81/189560b651d61d71c81841f56249dcea3ab441bfcb2adb5afe7b5e516085/wagtail_grapple-0.26.0-py3-none-any.whl", hash = "sha256:de9d3c336d151279976d67f781c758ac5efc7d2bb003c2e311e6fbeea1554c41", size = 47759, upload-time = "2024-06-26T10:39:28.849Z" },
|
{ url = "https://files.pythonhosted.org/packages/08/05/d80a7675e359fa62d45b92f14e42850fcb5f42c4fec3950951585b0a0975/wagtail_grapple-0.31.0-py3-none-any.whl", hash = "sha256:fed9205df68861d0ec6c5be67ac5aeb00775ebabde6baa38d582e723076befc0", size = 49251, upload-time = "2026-04-21T09:24:26.18Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -595,24 +799,24 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whitenoise"
|
name = "whitenoise"
|
||||||
version = "6.7.0"
|
version = "6.12.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/64/b8/86451d63ef5e1a9c480b52759d9db25ba85c3420ebdaf039057ed152a4c1/whitenoise-6.7.0.tar.gz", hash = "sha256:58c7a6cd811e275a6c91af22e96e87da0b1109e9a53bb7464116ef4c963bf636", size = 24973, upload-time = "2024-06-19T16:20:09.11Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/cb/2a/55b3f3a4ec326cd077c1c3defeee656b9298372a69229134d930151acd01/whitenoise-6.12.0.tar.gz", hash = "sha256:f723ebb76a112e98816ff80fcea0a6c9b8ecde835f8ddda25df7a30a3c2db6ad", size = 26841, upload-time = "2026-02-27T00:05:42.028Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/b8/42/68400d8ad59f67a1f7e12c2f39089ce005f08f73333f3e215f3d5ed6453c/whitenoise-6.7.0-py3-none-any.whl", hash = "sha256:a1ae85e01fdc9815d12fa33f17765bc132ed2c54fa76daf9e39e879dd93566f6", size = 19905, upload-time = "2024-06-19T16:20:03.269Z" },
|
{ url = "https://files.pythonhosted.org/packages/db/eb/d5583a11486211f3ebd4b385545ae787f32363d453c19fffd81106c9c138/whitenoise-6.12.0-py3-none-any.whl", hash = "sha256:fc5e8c572e33ebf24795b47b6a7da8da3c00cff2349f5b04c02f28d0cc5a3cc2", size = 20302, upload-time = "2026-02-27T00:05:40.086Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "willow"
|
name = "willow"
|
||||||
version = "1.8.0"
|
version = "1.11.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "defusedxml" },
|
{ name = "defusedxml" },
|
||||||
{ name = "filetype" },
|
{ name = "filetype" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/77/c3/9d19db05d3abe0375e8d6bf2d3f7ea6f8df266d5ddabcc5a114c8a7cce1f/willow-1.8.0.tar.gz", hash = "sha256:ef3df6cde80d4914e719188147bef1d71c240edb118340e0c5957ecc8fe08315", size = 113313, upload-time = "2024-01-17T19:11:58.712Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/a3/bd/2a383be24c3e47423aa9b0aa5b4ca818ef193506b58800dd51e1b89d7bb3/willow-1.11.0.tar.gz", hash = "sha256:70292b2d0cd2d5bb4076f0b3d61308aeaa0b225f3970d00752f08a8fd386c3d1", size = 113827, upload-time = "2025-07-16T08:46:26.939Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/95/7a/a48cf90bbf226f793d830c3bbdb7f0c4aa8087c08ce84cc864a1c882a457/willow-1.8.0-py3-none-any.whl", hash = "sha256:48ccf5ce48ccd29c37a32497cd7af50983f8570543c4de2988b15d583efc66be", size = 119223, upload-time = "2024-01-17T19:11:56.316Z" },
|
{ url = "https://files.pythonhosted.org/packages/c1/05/b3f1b443c31ad871c48e19ea2be189681c2df4ccf594b1dd83d6775c032b/willow-1.11.0-py3-none-any.whl", hash = "sha256:0a4388dbf18726eef8f27449659047689c39b7023045ca5a8a75410d3864ee6f", size = 119459, upload-time = "2025-07-16T08:46:25.596Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from wagtail.admin.panels import FieldPanel, FieldRowPanel, MultiFieldPanel
|
|||||||
from wagtail.fields import RichTextField, StreamField
|
from wagtail.fields import RichTextField, StreamField
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
from wagtail.search import index
|
from wagtail.search import index
|
||||||
|
from wagtail_headless_preview.models import HeadlessMixin
|
||||||
|
|
||||||
from dnscms.blocks import ImageSliderBlock
|
from dnscms.blocks import ImageSliderBlock
|
||||||
from dnscms.fields import CommonStreamField
|
from dnscms.fields import CommonStreamField
|
||||||
@@ -18,7 +19,7 @@ from dnscms.wordpress.models import WPImportedPageMixin
|
|||||||
|
|
||||||
|
|
||||||
@register_singular_query_field("venueIndex")
|
@register_singular_query_field("venueIndex")
|
||||||
class VenueIndex(Page):
|
class VenueIndex(HeadlessMixin, Page):
|
||||||
# there can only be one venue index page
|
# there can only be one venue index page
|
||||||
max_count = 1
|
max_count = 1
|
||||||
subpage_types = ["venues.VenuePage"]
|
subpage_types = ["venues.VenuePage"]
|
||||||
@@ -35,7 +36,7 @@ class VenueIndex(Page):
|
|||||||
|
|
||||||
|
|
||||||
@register_singular_query_field("venueRentalIndex")
|
@register_singular_query_field("venueRentalIndex")
|
||||||
class VenueRentalIndex(Page):
|
class VenueRentalIndex(HeadlessMixin, Page):
|
||||||
# there can only be one venue index page
|
# there can only be one venue index page
|
||||||
max_count = 1
|
max_count = 1
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
@@ -51,7 +52,7 @@ class VenueRentalIndex(Page):
|
|||||||
graphql_fields = [GraphQLRichText("lead"), GraphQLStreamfield("body")]
|
graphql_fields = [GraphQLRichText("lead"), GraphQLStreamfield("body")]
|
||||||
|
|
||||||
|
|
||||||
class VenuePage(WPImportedPageMixin, Page):
|
class VenuePage(HeadlessMixin, WPImportedPageMixin, Page):
|
||||||
# no children
|
# no children
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
parent_page_types = ["venues.VenueIndex"]
|
parent_page_types = ["venues.VenueIndex"]
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[tools]
|
||||||
|
python = "3.14"
|
||||||
|
uv = "latest"
|
||||||
|
node = "24"
|
||||||
|
prek = "latest"
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
exclude = '^web/src/gql/'
|
||||||
|
|
||||||
|
[[repos]]
|
||||||
|
repo = "https://github.com/pre-commit/pre-commit-hooks"
|
||||||
|
rev = "v6.0.0"
|
||||||
|
hooks = [
|
||||||
|
{ id = "end-of-file-fixer" },
|
||||||
|
{ id = "trailing-whitespace" },
|
||||||
|
{ id = "check-yaml" },
|
||||||
|
{ id = "check-toml" },
|
||||||
|
{ id = "check-merge-conflict" },
|
||||||
|
{ id = "check-added-large-files" },
|
||||||
|
{ id = "debug-statements" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[repos]]
|
||||||
|
repo = "https://github.com/astral-sh/ruff-pre-commit"
|
||||||
|
rev = "v0.15.13"
|
||||||
|
|
||||||
|
[[repos.hooks]]
|
||||||
|
id = "ruff-check"
|
||||||
|
args = ["--fix"]
|
||||||
|
files = '^dnscms/.*\.py$'
|
||||||
|
exclude = '/migrations/'
|
||||||
|
|
||||||
|
[[repos.hooks]]
|
||||||
|
id = "ruff-format"
|
||||||
|
files = '^dnscms/.*\.py$'
|
||||||
|
exclude = '/migrations/'
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
GRAPHQL_ENDPOINT=https://cms.neuf.no/api/graphql/
|
WAGTAIL_BASE_URL=https://cms.neuf.no
|
||||||
URL=http://localhost:3000
|
URL=http://localhost:3000
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# Based on https://github.com/vercel/next.js/tree/canary/examples/with-docker
|
# Based on https://github.com/vercel/next.js/tree/canary/examples/with-docker
|
||||||
|
|
||||||
FROM node:22-alpine AS base
|
FROM node:24-alpine AS base
|
||||||
|
|
||||||
# Install dependencies only when needed
|
# Install dependencies only when needed
|
||||||
FROM base AS deps
|
FROM base AS deps
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
Run the development server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Update GraphQL definitions from `http://127.0.0.1:8000/api/graphql/`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run codegen
|
|
||||||
```
|
|
||||||
+19
-1
@@ -3,13 +3,31 @@ import { CodegenConfig } from "@graphql-codegen/cli";
|
|||||||
import { loadEnvConfig } from "@next/env";
|
import { loadEnvConfig } from "@next/env";
|
||||||
loadEnvConfig(process.cwd());
|
loadEnvConfig(process.cwd());
|
||||||
|
|
||||||
|
const wagtailBaseUrl = process.env.WAGTAIL_BASE_URL;
|
||||||
|
if (!wagtailBaseUrl) {
|
||||||
|
throw new Error("WAGTAIL_BASE_URL is not set");
|
||||||
|
}
|
||||||
|
const graphqlEndpoint = `${wagtailBaseUrl.replace(/\/$/, "")}/api/graphql/`;
|
||||||
|
|
||||||
const config: CodegenConfig = {
|
const config: CodegenConfig = {
|
||||||
schema: process.env.GRAPHQL_ENDPOINT,
|
schema: graphqlEndpoint,
|
||||||
documents: ["src/**/*.tsx", "src/**/*.ts"],
|
documents: ["src/**/*.tsx", "src/**/*.ts"],
|
||||||
ignoreNoDocuments: true, // for better experience with the watcher
|
ignoreNoDocuments: true, // for better experience with the watcher
|
||||||
generates: {
|
generates: {
|
||||||
"./src/gql/": {
|
"./src/gql/": {
|
||||||
preset: "client",
|
preset: "client",
|
||||||
|
presetConfig: {
|
||||||
|
fragmentMasking: { unmaskFunctionName: "unmaskFragment" },
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
scalars: {
|
||||||
|
DateTime: "string",
|
||||||
|
JSONString: "string",
|
||||||
|
PositiveInt: "number",
|
||||||
|
RichText: "string",
|
||||||
|
UUID: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
[tools]
|
|
||||||
node = "22"
|
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
images: {
|
images: {
|
||||||
@@ -12,6 +16,10 @@ const nextConfig = {
|
|||||||
hostname: "**",
|
hostname: "**",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
dangerouslyAllowLocalIP: process.env.NODE_ENV === "development",
|
||||||
|
},
|
||||||
|
turbopack: {
|
||||||
|
root: __dirname,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Generated
+2517
-2590
File diff suppressed because it is too large
Load Diff
+26
-21
@@ -10,31 +10,36 @@
|
|||||||
"codegen": "graphql-codegen"
|
"codegen": "graphql-codegen"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@graphql-codegen/cli": "^5.0.7",
|
"@graphql-codegen/cli": "^7.0.0",
|
||||||
"@graphql-codegen/client-preset": "^4.8.3",
|
"@graphql-codegen/client-preset": "^6.0.0",
|
||||||
"@parcel/watcher": "^2.5.1",
|
"@parcel/watcher": "^2.5.6",
|
||||||
"@sindresorhus/slugify": "^2.2.1",
|
"@sindresorhus/slugify": "^3.0.0",
|
||||||
"@urql/next": "^1.1.5",
|
"@urql/next": "^2.0.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"date-fns-tz": "^3.2.0",
|
"date-fns-tz": "^3.2.0",
|
||||||
"graphql": "^16.11.0",
|
"graphql": "^16.14.0",
|
||||||
"next": "^15.4.6",
|
"next": "^16.2.6",
|
||||||
"nuqs": "^2.4.3",
|
"nuqs": "^2.8.9",
|
||||||
"react": "^19.1.1",
|
"react": "19.2.6",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "19.2.6",
|
||||||
"react-intersection-observer": "^9.16.0",
|
"react-intersection-observer": "^10.0.3",
|
||||||
"sass": "^1.90.0",
|
"sass": "^1.99.0",
|
||||||
"sharp": "^0.34.3",
|
"sharp": "^0.34.5",
|
||||||
"swiper": "^11.2.10",
|
"swiper": "^12.1.4",
|
||||||
"urql": "^4.2.2",
|
"urql": "^5.0.2",
|
||||||
"use-debounce": "^10.0.5"
|
"use-debounce": "^10.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22",
|
"@types/node": "^24",
|
||||||
"@types/react": "^19.1.9",
|
"@types/react": "19.2.14",
|
||||||
"@types/react-dom": "^19.1.7",
|
"@types/react-dom": "19.2.3",
|
||||||
|
"baseline-browser-mapping": "^2.10.29",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.4.6",
|
"eslint-config-next": "16.2.6",
|
||||||
"typescript": "^5"
|
"typescript": "^6"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"@types/react": "19.2.14",
|
||||||
|
"@types/react-dom": "19.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 32 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 17 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
@@ -1,41 +1,15 @@
|
|||||||
import { graphql } from "@/gql";
|
import { graphql } from "@/gql";
|
||||||
import { GenericFragment } from "@/gql/graphql";
|
|
||||||
import { getClient } from "@/app/client";
|
import { getClient } from "@/app/client";
|
||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { PageHeader } from "@/components/general/PageHeader";
|
import {
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
GenericPageView,
|
||||||
import { BgPig } from "@/components/general/BgPig";
|
loadGenericPageProps,
|
||||||
|
} from "@/components/general/GenericPageView";
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
export const dynamicParams = false;
|
export const dynamicParams = false;
|
||||||
|
|
||||||
const GenericFragmentDefinition = graphql(`
|
|
||||||
fragment Generic on GenericPage {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
urlPath
|
|
||||||
seoTitle
|
|
||||||
searchDescription
|
|
||||||
title
|
|
||||||
lead
|
|
||||||
pig
|
|
||||||
body {
|
|
||||||
...Blocks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const genericPageByUrlPathQuery = graphql(`
|
|
||||||
query genericPageByUrl($urlPath: String!) {
|
|
||||||
page: page(contentType: "generic.GenericPage", urlPath: $urlPath) {
|
|
||||||
... on GenericPage {
|
|
||||||
...Generic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
function getWagtailUrlPath(url: string[]): string {
|
function getWagtailUrlPath(url: string[]): string {
|
||||||
// for the page /foo/bar we need to look for `/home/foo/bar/`
|
// for the page /foo/bar we need to look for `/home/foo/bar/`
|
||||||
return `/home/${url.join("/")}/`;
|
return `/home/${url.join("/")}/`;
|
||||||
@@ -78,46 +52,14 @@ export async function generateMetadata(
|
|||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { url } = await params;
|
const { url } = await params;
|
||||||
const urlPath = getWagtailUrlPath(url);
|
const props = await loadGenericPageProps({ urlPath: getWagtailUrlPath(url) });
|
||||||
const { data, error } = await getClient().query(genericPageByUrlPathQuery, {
|
if (!props) return null;
|
||||||
urlPath: urlPath,
|
return getSeoMetadata(props.page, parent);
|
||||||
});
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.page) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const page = data.page as GenericFragment;
|
|
||||||
const metadata = await getSeoMetadata(page, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({ params }: { params: Params }) {
|
export default async function Page({ params }: { params: Params }) {
|
||||||
const { url } = await params;
|
const { url } = await params;
|
||||||
const urlPath = getWagtailUrlPath(url);
|
const props = await loadGenericPageProps({ urlPath: getWagtailUrlPath(url) });
|
||||||
const { data, error } = await getClient().query(genericPageByUrlPathQuery, {
|
if (!props) return notFound();
|
||||||
urlPath: urlPath,
|
return <GenericPageView {...props} />;
|
||||||
});
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data?.page) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const page = data?.page as GenericFragment;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<PageHeader heading={page.title} lead={page.lead} />
|
|
||||||
<PageContent blocks={page.body} />
|
|
||||||
</main>
|
|
||||||
{page.pig && <BgPig type={page.pig} color="white" />}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,13 @@
|
|||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { getClient } from "@/app/client";
|
import { getClient } from "@/app/client";
|
||||||
import { Breadcrumb } from "@/components/general/Breadcrumb";
|
import {
|
||||||
import { ImageFigure } from "@/components/general/Image";
|
NewsPageView,
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
loadNewsPageProps,
|
||||||
|
} from "@/components/news/NewsPageView";
|
||||||
import { graphql } from "@/gql";
|
import { graphql } from "@/gql";
|
||||||
import { NewsFragment } from "@/gql/graphql";
|
|
||||||
import { formatDate } from "@/lib/date";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
const newsBySlugQuery = graphql(`
|
|
||||||
query newsBySlug($slug: String!) {
|
|
||||||
news: page(contentType: "news.NewsPage", slug: $slug) {
|
|
||||||
... on NewsPage {
|
|
||||||
...News
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
const allNewsSlugsQuery = graphql(`
|
const allNewsSlugsQuery = graphql(`
|
||||||
query allNewsSlugs {
|
query allNewsSlugs {
|
||||||
@@ -51,62 +40,14 @@ export async function generateMetadata(
|
|||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const { data, error } = await getClient().query(newsBySlugQuery, {
|
const props = await loadNewsPageProps({ slug });
|
||||||
slug,
|
if (!props) return null;
|
||||||
});
|
return getSeoMetadata(props.news, parent);
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.news) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const news = data.news as NewsFragment;
|
|
||||||
const metadata = await getSeoMetadata(news, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({ params }: { params: Params }) {
|
export default async function Page({ params }: { params: Params }) {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const { data, error } = await getClient().query(newsBySlugQuery, {
|
const props = await loadNewsPageProps({ slug });
|
||||||
slug,
|
if (!props) return notFound();
|
||||||
});
|
return <NewsPageView {...props} />;
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.news) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const news = data?.news as NewsFragment;
|
|
||||||
const featuredImage: any = news.featuredImage;
|
|
||||||
return (
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<section className="news-header">
|
|
||||||
<Breadcrumb
|
|
||||||
link="/aktuelt"
|
|
||||||
text="Nyhet"
|
|
||||||
date={formatDate(news.firstPublishedAt, "d. MMMM yyyy")}
|
|
||||||
/>
|
|
||||||
<h1 className="news-title">{news.title}</h1>
|
|
||||||
{news.lead && (
|
|
||||||
<div
|
|
||||||
className="lead"
|
|
||||||
dangerouslySetInnerHTML={{ __html: news.lead }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{featuredImage && (
|
|
||||||
<ImageFigure
|
|
||||||
src={featuredImage.url}
|
|
||||||
alt={featuredImage.alt ?? ""}
|
|
||||||
width={featuredImage.width}
|
|
||||||
height={featuredImage.height}
|
|
||||||
attribution={featuredImage.attribution}
|
|
||||||
sizes="100vw"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
<PageContent blocks={news.body} />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,19 @@
|
|||||||
import { getClient } from "@/app/client";
|
|
||||||
import { NewsList } from "@/components/news/NewsList";
|
|
||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { PageHeader } from "@/components/general/PageHeader";
|
import {
|
||||||
import { newsQuery, NewsFragment, NewsIndexFragment } from "@/lib/news";
|
NewsIndexView,
|
||||||
|
loadNewsIndexProps,
|
||||||
|
} from "@/components/news/NewsIndexView";
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: Promise<{}> },
|
_: unknown,
|
||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { data, error } = await getClient().query(newsQuery, {});
|
const { index } = await loadNewsIndexProps();
|
||||||
if (error) {
|
return getSeoMetadata(index, parent);
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.index) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data.index as NewsIndexFragment;
|
|
||||||
const metadata = await getSeoMetadata(index, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const { data, error } = await getClient().query(newsQuery, {});
|
const props = await loadNewsIndexProps();
|
||||||
if (error) {
|
return <NewsIndexView {...props} />;
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
const news = (data?.news ?? []) as NewsFragment[];
|
|
||||||
const index = data?.index as NewsIndexFragment;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<PageHeader heading={index.title} lead={index.lead} align="left" />
|
|
||||||
<NewsList news={news} />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { cookies, draftMode } from "next/headers";
|
||||||
|
|
||||||
|
export async function POST() {
|
||||||
|
(await draftMode()).disable();
|
||||||
|
(await cookies()).delete("preview-token");
|
||||||
|
return new Response(null, { status: 204 });
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { cookies, draftMode } from "next/headers";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { NextRequest } from "next/server";
|
||||||
|
|
||||||
|
// Wagtail-headless-preview directs the editor's preview iframe here with
|
||||||
|
// ?content_type=app.Model&token=<signed>. We stash the token in a cookie,
|
||||||
|
// enable Next.js draft mode, and redirect to the type-dispatching renderer.
|
||||||
|
export async function GET(req: NextRequest) {
|
||||||
|
const token = req.nextUrl.searchParams.get("token");
|
||||||
|
const contentType = req.nextUrl.searchParams.get("content_type");
|
||||||
|
if (!token || !contentType) {
|
||||||
|
return new Response("missing token/content_type", { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
(await draftMode()).enable();
|
||||||
|
(await cookies()).set("preview-token", token, {
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: "lax",
|
||||||
|
secure: process.env.NODE_ENV === "production",
|
||||||
|
maxAge: 60 * 60 * 24,
|
||||||
|
path: "/",
|
||||||
|
});
|
||||||
|
|
||||||
|
redirect("/preview/render");
|
||||||
|
}
|
||||||
@@ -1,25 +1,13 @@
|
|||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { getClient } from "@/app/client";
|
import { getClient } from "@/app/client";
|
||||||
import { EventDetails } from "@/components/events/EventDetails";
|
import {
|
||||||
import { EventHeader } from "@/components/events/EventHeader";
|
EventPageView,
|
||||||
import { BgPig } from "@/components/general/BgPig";
|
loadEventPageProps,
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
} from "@/components/events/EventPageView";
|
||||||
import { graphql } from "@/gql";
|
import { graphql } from "@/gql";
|
||||||
import { EventFragment } from "@/gql/graphql";
|
|
||||||
import { getEventPig } from "@/lib/event";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
const eventBySlugQuery = graphql(`
|
|
||||||
query eventBySlug($slug: String!) {
|
|
||||||
event: page(contentType: "events.EventPage", slug: $slug) {
|
|
||||||
... on EventPage {
|
|
||||||
...Event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
const allEventSlugsQuery = graphql(`
|
const allEventSlugsQuery = graphql(`
|
||||||
query allEventSlugs {
|
query allEventSlugs {
|
||||||
@@ -52,52 +40,14 @@ export async function generateMetadata(
|
|||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const { data, error } = await getClient().query(eventBySlugQuery, {
|
const props = await loadEventPageProps({ slug });
|
||||||
slug,
|
if (!props) return null;
|
||||||
});
|
return getSeoMetadata(props.event, parent);
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.event) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const event = data.event as EventFragment;
|
|
||||||
const metadata = await getSeoMetadata(event, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({ params }: { params: Params }) {
|
export default async function Page({ params }: { params: Params }) {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const { data, error } = await getClient().query(eventBySlugQuery, {
|
const props = await loadEventPageProps({ slug });
|
||||||
slug,
|
if (!props) return notFound();
|
||||||
});
|
return <EventPageView {...props} />;
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.event) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const event = data.event as EventFragment;
|
|
||||||
const eventPig = getEventPig(event);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<EventHeader event={event} />
|
|
||||||
<EventDetails event={event} />
|
|
||||||
{event.lead && (
|
|
||||||
<div
|
|
||||||
className="lead event-lead"
|
|
||||||
dangerouslySetInnerHTML={{ __html: event.lead }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<PageContent blocks={event.body} />
|
|
||||||
</main>
|
|
||||||
{eventPig && <BgPig type={eventPig} color="white" />}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,24 @@
|
|||||||
import { Suspense } from "react";
|
|
||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { getClient } from "@/app/client";
|
import { getClient } from "@/app/client";
|
||||||
import { EventContainer } from "@/components/events/EventContainer";
|
|
||||||
import {
|
import {
|
||||||
eventsOverviewQuery,
|
EventIndexView,
|
||||||
eventIndexMetadataQuery,
|
loadEventIndexProps,
|
||||||
EventFragment,
|
} from "@/components/events/EventIndexView";
|
||||||
EventCategory,
|
import { EventIndexFragment } from "@/gql/graphql";
|
||||||
EventOrganizer,
|
import { eventIndexMetadataQuery } from "@/lib/event";
|
||||||
} from "@/lib/event";
|
|
||||||
import { PageHeader } from "@/components/general/PageHeader";
|
|
||||||
import { EventIndexFragment, VenueFragment } from "@/gql/graphql";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: Promise<{}> },
|
_: unknown,
|
||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { data, error } = await getClient().query(eventIndexMetadataQuery, {});
|
const { data, error } = await getClient().query(eventIndexMetadataQuery, {});
|
||||||
|
if (error) throw new Error(error.message);
|
||||||
if (error) {
|
if (!data?.index) return null;
|
||||||
throw new Error(error.message);
|
return getSeoMetadata(data.index as EventIndexFragment, parent);
|
||||||
}
|
|
||||||
if (!data?.index) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data.index as EventIndexFragment;
|
|
||||||
const metadata = await getSeoMetadata(index, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const { data, error } = await getClient().query(eventsOverviewQuery, {});
|
const props = await loadEventIndexProps();
|
||||||
if (error) {
|
return <EventIndexView {...props} />;
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!data?.index ||
|
|
||||||
!data?.events?.futureEvents ||
|
|
||||||
!data?.eventCategories ||
|
|
||||||
!data?.eventOrganizers ||
|
|
||||||
!data?.venues
|
|
||||||
) {
|
|
||||||
throw new Error("Failed to render /arrangementer");
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data?.index as EventIndexFragment;
|
|
||||||
const events = (data?.events?.futureEvents ?? []) as EventFragment[];
|
|
||||||
const eventCategories = (data?.eventCategories ?? []) as EventCategory[];
|
|
||||||
const eventOrganizers = (data?.eventOrganizers ?? []) as EventOrganizer[];
|
|
||||||
const venues = (data?.venues ?? []) as VenueFragment[];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<PageHeader heading="Dette skjer på Chateau Neuf" align="left" />
|
|
||||||
<Suspense>
|
|
||||||
<EventContainer
|
|
||||||
events={events}
|
|
||||||
eventCategories={eventCategories}
|
|
||||||
eventOrganizers={eventOrganizers}
|
|
||||||
venues={venues}
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,15 @@ import "server-only";
|
|||||||
import { cacheExchange, createClient, fetchExchange } from "@urql/core";
|
import { cacheExchange, createClient, fetchExchange } from "@urql/core";
|
||||||
import { registerUrql } from "@urql/next/rsc";
|
import { registerUrql } from "@urql/next/rsc";
|
||||||
|
|
||||||
|
const wagtailBaseUrl = process.env.WAGTAIL_BASE_URL;
|
||||||
|
if (!wagtailBaseUrl) {
|
||||||
|
throw new Error("WAGTAIL_BASE_URL is not set");
|
||||||
|
}
|
||||||
|
const graphqlEndpoint = `${wagtailBaseUrl.replace(/\/$/, "")}/api/graphql/`;
|
||||||
|
|
||||||
const makeClient = () => {
|
const makeClient = () => {
|
||||||
return createClient({
|
return createClient({
|
||||||
url: process.env.GRAPHQL_ENDPOINT ?? "",
|
url: graphqlEndpoint,
|
||||||
exchanges: [cacheExchange, fetchExchange],
|
exchanges: [cacheExchange, fetchExchange],
|
||||||
// requestPolicy: "network-only",
|
// requestPolicy: "network-only",
|
||||||
fetchOptions: { next: { revalidate: 0 } },
|
fetchOptions: { next: { revalidate: 0 } },
|
||||||
|
|||||||
@@ -1,25 +1,13 @@
|
|||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { getClient } from "@/app/client";
|
import { getClient } from "@/app/client";
|
||||||
import { AssociationHeader } from "@/components/associations/AssociationHeader";
|
import {
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
AssociationPageView,
|
||||||
|
loadAssociationPageProps,
|
||||||
|
} from "@/components/associations/AssociationPageView";
|
||||||
import { graphql } from "@/gql";
|
import { graphql } from "@/gql";
|
||||||
import { AssociationFragment } from "@/gql/graphql";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
const associationBySlugQuery = graphql(`
|
|
||||||
query associationBySlug($slug: String!) {
|
|
||||||
association: page(
|
|
||||||
contentType: "associations.AssociationPage"
|
|
||||||
slug: $slug
|
|
||||||
) {
|
|
||||||
... on AssociationPage {
|
|
||||||
...Association
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
type Params = Promise<{ slug: string }>;
|
type Params = Promise<{ slug: string }>;
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
@@ -27,20 +15,9 @@ export async function generateMetadata(
|
|||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const { data, error } = await getClient().query(associationBySlugQuery, {
|
const props = await loadAssociationPageProps({ slug });
|
||||||
slug,
|
if (!props) return null;
|
||||||
});
|
return getSeoMetadata(props.association, parent);
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.association) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const association = data.association as AssociationFragment;
|
|
||||||
const metadata = await getSeoMetadata(association, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
@@ -70,23 +47,7 @@ export async function generateStaticParams() {
|
|||||||
|
|
||||||
export default async function Page({ params }: { params: Params }) {
|
export default async function Page({ params }: { params: Params }) {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const { data, error } = await getClient().query(associationBySlugQuery, {
|
const props = await loadAssociationPageProps({ slug });
|
||||||
slug,
|
if (!props) return notFound();
|
||||||
});
|
return <AssociationPageView {...props} />;
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.association) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const association = data.association as AssociationFragment;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<AssociationHeader association={association} />
|
|
||||||
<PageContent blocks={association.body} />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,106 +1,19 @@
|
|||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { graphql } from "@/gql";
|
import {
|
||||||
import { AssociationFragment, AssociationIndexFragment } from "@/gql/graphql";
|
AssociationIndexView,
|
||||||
import { getClient } from "@/app/client";
|
loadAssociationIndexProps,
|
||||||
import { AssociationList } from "@/components/associations/AssociationList";
|
} from "@/components/associations/AssociationIndexView";
|
||||||
import { PageHeader } from "@/components/general/PageHeader";
|
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
const allAssociationsQuery = graphql(`
|
|
||||||
query allAssociations {
|
|
||||||
index: associationIndex {
|
|
||||||
... on AssociationIndex {
|
|
||||||
...AssociationIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
associations: pages(
|
|
||||||
contentType: "associations.AssociationPage"
|
|
||||||
limit: 1000
|
|
||||||
) {
|
|
||||||
... on AssociationPage {
|
|
||||||
...Association
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: Promise<{}> },
|
_: unknown,
|
||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { data, error } = await getClient().query(allAssociationsQuery, {});
|
const { index } = await loadAssociationIndexProps();
|
||||||
|
return getSeoMetadata(index, parent);
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.index) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data.index as AssociationIndexFragment;
|
|
||||||
const metadata = await getSeoMetadata(index, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssociationIndexDefinition = graphql(`
|
|
||||||
fragment AssociationIndex on AssociationIndex {
|
|
||||||
... on AssociationIndex {
|
|
||||||
title
|
|
||||||
seoTitle
|
|
||||||
searchDescription
|
|
||||||
lead
|
|
||||||
body {
|
|
||||||
...Blocks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const AssociationFragmentDefinition = graphql(`
|
|
||||||
fragment Association on AssociationPage {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
slug
|
|
||||||
title
|
|
||||||
seoTitle
|
|
||||||
searchDescription
|
|
||||||
excerpt
|
|
||||||
lead
|
|
||||||
body {
|
|
||||||
...Blocks
|
|
||||||
}
|
|
||||||
logo {
|
|
||||||
url
|
|
||||||
width
|
|
||||||
height
|
|
||||||
}
|
|
||||||
associationType
|
|
||||||
websiteUrl
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const { data, error } = await getClient().query(allAssociationsQuery, {});
|
const props = await loadAssociationIndexProps();
|
||||||
|
return <AssociationIndexView {...props} />;
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.associations || !data.index) {
|
|
||||||
throw new Error("Failed to render /foreninger");
|
|
||||||
}
|
|
||||||
|
|
||||||
const associations = data.associations as AssociationFragment[];
|
|
||||||
const index = data.index as AssociationIndexFragment;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<PageHeader heading={index.title} lead={index.lead} />
|
|
||||||
{index.body && <PageContent blocks={index.body} />}
|
|
||||||
<AssociationList
|
|
||||||
associations={associations}
|
|
||||||
heading="Foreninger og utvalg"
|
|
||||||
/>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,72 +1,19 @@
|
|||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import {
|
||||||
import { graphql } from "@/gql";
|
ContactIndexView,
|
||||||
import { ContactIndexFragment } from "@/gql/graphql";
|
loadContactIndexProps,
|
||||||
import { getClient } from "@/app/client";
|
} from "@/components/contact/ContactIndexView";
|
||||||
import { PageHeader } from "@/components/general/PageHeader";
|
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
|
||||||
import { GeneralContactBlock } from "@/components/blocks/GeneralContactBlock";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
const contactQuery = graphql(`
|
|
||||||
query contacts {
|
|
||||||
index: contactIndex {
|
|
||||||
... on ContactIndex {
|
|
||||||
...ContactIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const ContactIndexDefinition = graphql(`
|
|
||||||
fragment ContactIndex on ContactIndex {
|
|
||||||
... on ContactIndex {
|
|
||||||
title
|
|
||||||
seoTitle
|
|
||||||
searchDescription
|
|
||||||
lead
|
|
||||||
body {
|
|
||||||
...Blocks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: Promise<{}> },
|
_: unknown,
|
||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { data, error } = await getClient().query(contactQuery, {});
|
const { index } = await loadContactIndexProps();
|
||||||
|
return getSeoMetadata(index, parent);
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.index) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data.index as ContactIndexFragment;
|
|
||||||
const metadata = await getSeoMetadata(index, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const { data, error } = await getClient().query(contactQuery, {});
|
const props = await loadContactIndexProps();
|
||||||
|
return <ContactIndexView {...props} />;
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.index) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data.index as ContactIndexFragment;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<PageHeader heading={index.title} lead={index.lead} />
|
|
||||||
<GeneralContactBlock />
|
|
||||||
{index.body && <PageContent blocks={index.body} />}
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,13 @@
|
|||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { getClient } from "@/app/client";
|
import { getClient } from "@/app/client";
|
||||||
import { ImageSliderBlock } from "@/components/blocks/ImageSliderBlock";
|
import {
|
||||||
import { Breadcrumb } from "@/components/general/Breadcrumb";
|
VenuePageView,
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
loadVenuePageProps,
|
||||||
import { NeufMap } from "@/components/venues/NeufMap";
|
} from "@/components/venues/VenuePageView";
|
||||||
import { VenueInfo } from "@/components/venues/VenueInfo";
|
|
||||||
import { graphql } from "@/gql";
|
import { graphql } from "@/gql";
|
||||||
import { VenueFragment } from "@/gql/graphql";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
const venueBySlugQuery = graphql(`
|
|
||||||
query venueBySlug($slug: String!) {
|
|
||||||
venue: page(contentType: "venues.VenuePage", slug: $slug) {
|
|
||||||
... on VenuePage {
|
|
||||||
...Venue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
type Params = Promise<{ slug: string }>;
|
type Params = Promise<{ slug: string }>;
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
@@ -27,20 +15,9 @@ export async function generateMetadata(
|
|||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const { data, error } = await getClient().query(venueBySlugQuery, {
|
const props = await loadVenuePageProps({ slug });
|
||||||
slug,
|
if (!props) return null;
|
||||||
});
|
return getSeoMetadata(props.venue, parent);
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.venue) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const venue = data.venue as VenueFragment;
|
|
||||||
const metadata = await getSeoMetadata(venue, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
@@ -70,32 +47,7 @@ export async function generateStaticParams() {
|
|||||||
|
|
||||||
export default async function Page({ params }: { params: Params }) {
|
export default async function Page({ params }: { params: Params }) {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const { data, error } = await getClient().query(venueBySlugQuery, {
|
const props = await loadVenuePageProps({ slug });
|
||||||
slug,
|
if (!props) return notFound();
|
||||||
});
|
return <VenuePageView {...props} />;
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.venue) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const venue = data.venue as VenueFragment;
|
|
||||||
const featuredImage: any = venue.featuredImage;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
{venue.images && venue.images.length !== 0 && (
|
|
||||||
<ImageSliderBlock block={venue.images[0]} hero />
|
|
||||||
)}
|
|
||||||
<div className="page-header-small">
|
|
||||||
<Breadcrumb link="/utleie" text="Lokale" />
|
|
||||||
<h1 className="page-title">{venue.title}</h1>
|
|
||||||
</div>
|
|
||||||
<PageContent blocks={venue.body} />
|
|
||||||
<VenueInfo venue={venue} />
|
|
||||||
<NeufMap venueSlug={venue.slug} />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,111 +1,19 @@
|
|||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { graphql } from "@/gql";
|
import {
|
||||||
import { VenueFragment, VenueIndexFragment } from "@/gql/graphql";
|
VenueIndexView,
|
||||||
import { getClient } from "@/app/client";
|
loadVenueIndexProps,
|
||||||
import { VenueList } from "@/components/venues/VenueList";
|
} from "@/components/venues/VenueIndexView";
|
||||||
import { PageHeader } from "@/components/general/PageHeader";
|
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
const venueIndexQuery = graphql(`
|
|
||||||
query venueIndex {
|
|
||||||
index: venueIndex {
|
|
||||||
... on VenueIndex {
|
|
||||||
...VenueIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
venues: pages(contentType: "venues.VenuePage", limit: 100) {
|
|
||||||
... on VenuePage {
|
|
||||||
...Venue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const VenueIndexDefinition = graphql(`
|
|
||||||
fragment VenueIndex on VenueIndex {
|
|
||||||
... on VenueIndex {
|
|
||||||
title
|
|
||||||
seoTitle
|
|
||||||
searchDescription
|
|
||||||
lead
|
|
||||||
body {
|
|
||||||
...Blocks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const VenueFragmentDefinition = graphql(`
|
|
||||||
fragment Venue on VenuePage {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
slug
|
|
||||||
title
|
|
||||||
seoTitle
|
|
||||||
searchDescription
|
|
||||||
images {
|
|
||||||
...Blocks
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
...Blocks
|
|
||||||
}
|
|
||||||
featuredImage {
|
|
||||||
...Image
|
|
||||||
}
|
|
||||||
showAsBookable
|
|
||||||
showInOverview
|
|
||||||
floor
|
|
||||||
preposition
|
|
||||||
usedFor
|
|
||||||
techSpecsUrl
|
|
||||||
capabilityAudio
|
|
||||||
capabilityAudioVideo
|
|
||||||
capabilityBar
|
|
||||||
capabilityLighting
|
|
||||||
capacityLegal
|
|
||||||
capacityStanding
|
|
||||||
capacitySitting
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: Promise<{}> },
|
_: unknown,
|
||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { data, error } = await getClient().query(venueIndexQuery, {});
|
const { index } = await loadVenueIndexProps();
|
||||||
|
return getSeoMetadata(index, parent);
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.index) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = (data?.index ?? []) as VenueIndexFragment;
|
|
||||||
const metadata = await getSeoMetadata(index, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const { data, error } = await getClient().query(venueIndexQuery, {});
|
const props = await loadVenueIndexProps();
|
||||||
|
return <VenueIndexView {...props} />;
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.index || !data?.venues) {
|
|
||||||
throw new Error("Failed to render /lokaler");
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data.index as VenueIndexFragment;
|
|
||||||
const venues = (data?.venues ?? []) as VenueFragment[];
|
|
||||||
const visibleVenues = venues.filter((x) => x.showInOverview);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<PageHeader heading={index.title} lead={index.lead} />
|
|
||||||
<PageContent blocks={index.body} />
|
|
||||||
<VenueList venues={visibleVenues} />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-94
@@ -1,97 +1,9 @@
|
|||||||
import { graphql } from "@/gql";
|
import {
|
||||||
import { EventFragment } from "@/lib/event";
|
HomePageView,
|
||||||
import { NewsFragment } from "@/lib/news";
|
loadHomePageProps,
|
||||||
import { HomeFragment } from "@/gql/graphql";
|
} from "@/components/home/HomePageView";
|
||||||
import { getClient } from "@/app/client";
|
|
||||||
import { FeaturedEvents } from "@/components/events/FeaturedEvents";
|
|
||||||
import { NewsList } from "@/components/news/NewsList";
|
|
||||||
import { Newsletter } from "@/components/general/Newsletter";
|
|
||||||
import { UpcomingEvents } from "@/components/events/UpcomingEvents";
|
|
||||||
import { Pig } from "@/components/general/Pig";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { Icon } from "@/components/general/Icon";
|
|
||||||
import { SectionHeader } from "@/components/general/SectionHeader";
|
|
||||||
import { SectionFooter } from "@/components/general/SectionFooter";
|
|
||||||
|
|
||||||
const HomeFragmentDefinition = graphql(`
|
|
||||||
fragment Home on HomePage {
|
|
||||||
... on HomePage {
|
|
||||||
featuredEvents {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
const homeQuery = graphql(`
|
const props = await loadHomePageProps();
|
||||||
query home {
|
return <HomePageView {...props} />;
|
||||||
events: eventIndex {
|
|
||||||
... on EventIndex {
|
|
||||||
futureEvents {
|
|
||||||
... on EventPage {
|
|
||||||
...Event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
home: page(contentType: "home.HomePage", urlPath: "/home/") {
|
|
||||||
... on HomePage {
|
|
||||||
...Home
|
|
||||||
}
|
|
||||||
}
|
|
||||||
news: pages(contentType: "news.newsPage", order: "-first_published_at", limit: 4) {
|
|
||||||
... on NewsPage {
|
|
||||||
...News
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
const { data, error } = await getClient().query(homeQuery, {});
|
|
||||||
const home = (data?.home ?? []) as HomeFragment;
|
|
||||||
const events = (data?.events?.futureEvents ?? []) as EventFragment[];
|
|
||||||
const news = (data?.news ?? []) as NewsFragment[];
|
|
||||||
|
|
||||||
const featuredEventIds = home.featuredEvents.map((x) => x.id);
|
|
||||||
const featuredEvents = [
|
|
||||||
...events.filter((x) => featuredEventIds.includes(x.id)),
|
|
||||||
...events.filter((x) => !featuredEventIds.includes(x.id)),
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<main className="site-main index" id="main">
|
|
||||||
<FeaturedEvents events={featuredEvents} />
|
|
||||||
<UpcomingEvents events={events} />
|
|
||||||
<div className="infoBlock">
|
|
||||||
<SectionHeader heading="Besøk oss" link="/praktisk" linkText="Praktisk info" />
|
|
||||||
<div>
|
|
||||||
<h2 className="title">Skal du besøke Chateau Neuf?</h2>
|
|
||||||
<p>
|
|
||||||
Vi hjelper deg med å finne frem, og sørger for at du har en fin
|
|
||||||
opplevelse.
|
|
||||||
</p>
|
|
||||||
<Link href="/praktisk#adkomst" className="button">
|
|
||||||
<span>Adresse og adkomst</span>
|
|
||||||
<Icon type="arrowRight" />
|
|
||||||
</Link>
|
|
||||||
<Link href="/praktisk#billetter" className="button">
|
|
||||||
<span>Billetter</span>
|
|
||||||
<Icon type="arrowRight" />
|
|
||||||
</Link>
|
|
||||||
<Link href="/praktisk#apningstider" className="button">
|
|
||||||
<span>Åpningstider</span>
|
|
||||||
<Icon type="arrowRight" />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="pig">
|
|
||||||
<Pig type="point" />
|
|
||||||
</div>
|
|
||||||
<SectionFooter link="/praktisk" linkText="Praktisk info" />
|
|
||||||
</div>
|
|
||||||
<NewsList heading="Siste nytt" featured news={news} />
|
|
||||||
</main>
|
|
||||||
<Newsletter />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
import { getClient } from "@/app/client";
|
||||||
|
import { PreviewBanner } from "@/components/general/PreviewBanner";
|
||||||
|
import {
|
||||||
|
AssociationIndexView,
|
||||||
|
loadAssociationIndexProps,
|
||||||
|
} from "@/components/associations/AssociationIndexView";
|
||||||
|
import {
|
||||||
|
AssociationPageView,
|
||||||
|
loadAssociationPageProps,
|
||||||
|
} from "@/components/associations/AssociationPageView";
|
||||||
|
import {
|
||||||
|
ContactIndexView,
|
||||||
|
loadContactIndexProps,
|
||||||
|
} from "@/components/contact/ContactIndexView";
|
||||||
|
import {
|
||||||
|
EventIndexView,
|
||||||
|
loadEventIndexProps,
|
||||||
|
} from "@/components/events/EventIndexView";
|
||||||
|
import {
|
||||||
|
EventPageView,
|
||||||
|
loadEventPageProps,
|
||||||
|
} from "@/components/events/EventPageView";
|
||||||
|
import {
|
||||||
|
GenericPageView,
|
||||||
|
loadGenericPageProps,
|
||||||
|
} from "@/components/general/GenericPageView";
|
||||||
|
import {
|
||||||
|
HomePageView,
|
||||||
|
loadHomePageProps,
|
||||||
|
} from "@/components/home/HomePageView";
|
||||||
|
import {
|
||||||
|
NewsIndexView,
|
||||||
|
loadNewsIndexProps,
|
||||||
|
} from "@/components/news/NewsIndexView";
|
||||||
|
import {
|
||||||
|
NewsPageView,
|
||||||
|
loadNewsPageProps,
|
||||||
|
} from "@/components/news/NewsPageView";
|
||||||
|
import {
|
||||||
|
SponsorsPageView,
|
||||||
|
loadSponsorsPageProps,
|
||||||
|
} from "@/components/sponsor/SponsorsPageView";
|
||||||
|
import {
|
||||||
|
StudioPageView,
|
||||||
|
loadStudioPageProps,
|
||||||
|
} from "@/components/studio/StudioPageView";
|
||||||
|
import {
|
||||||
|
VenueIndexView,
|
||||||
|
loadVenueIndexProps,
|
||||||
|
} from "@/components/venues/VenueIndexView";
|
||||||
|
import {
|
||||||
|
VenuePageView,
|
||||||
|
loadVenuePageProps,
|
||||||
|
} from "@/components/venues/VenuePageView";
|
||||||
|
import {
|
||||||
|
VenueRentalIndexView,
|
||||||
|
loadVenueRentalIndexProps,
|
||||||
|
} from "@/components/venues/VenueRentalIndexView";
|
||||||
|
import { graphql } from "@/gql";
|
||||||
|
import {
|
||||||
|
AssociationFragment,
|
||||||
|
AssociationIndexFragment,
|
||||||
|
ContactIndexFragment,
|
||||||
|
EventFragment,
|
||||||
|
GenericFragment,
|
||||||
|
HomeFragment,
|
||||||
|
NewsFragment,
|
||||||
|
NewsIndexFragment,
|
||||||
|
SponsorsPageFragment,
|
||||||
|
StudioFragment,
|
||||||
|
VenueFragment,
|
||||||
|
VenueIndexFragment,
|
||||||
|
VenueRentalIndexFragment,
|
||||||
|
} from "@/gql/graphql";
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
export const revalidate = 0;
|
||||||
|
|
||||||
|
const previewPageQuery = graphql(`
|
||||||
|
query previewPage($token: String!) {
|
||||||
|
page: page(token: $token) {
|
||||||
|
__typename
|
||||||
|
... on GenericPage {
|
||||||
|
...Generic
|
||||||
|
}
|
||||||
|
... on StudioPage {
|
||||||
|
...Studio
|
||||||
|
}
|
||||||
|
... on SponsorsPage {
|
||||||
|
...SponsorsPage
|
||||||
|
}
|
||||||
|
... on HomePage {
|
||||||
|
...Home
|
||||||
|
}
|
||||||
|
... on EventPage {
|
||||||
|
...Event
|
||||||
|
}
|
||||||
|
... on NewsPage {
|
||||||
|
...News
|
||||||
|
}
|
||||||
|
... on AssociationPage {
|
||||||
|
...Association
|
||||||
|
}
|
||||||
|
... on VenuePage {
|
||||||
|
...Venue
|
||||||
|
}
|
||||||
|
... on NewsIndex {
|
||||||
|
...NewsIndex
|
||||||
|
}
|
||||||
|
... on AssociationIndex {
|
||||||
|
...AssociationIndex
|
||||||
|
}
|
||||||
|
... on VenueIndex {
|
||||||
|
...VenueIndex
|
||||||
|
}
|
||||||
|
... on VenueRentalIndex {
|
||||||
|
...VenueRentalIndex
|
||||||
|
}
|
||||||
|
... on ContactIndex {
|
||||||
|
...ContactIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
function ExpiredPreview() {
|
||||||
|
return (
|
||||||
|
<main className="site-main" id="main">
|
||||||
|
<h1>Preview session expired</h1>
|
||||||
|
<p>Click Preview again in the Wagtail admin to start a new session.</p>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function UnsupportedType({ typename }: { typename: string }) {
|
||||||
|
return (
|
||||||
|
<main className="site-main" id="main">
|
||||||
|
<h1>Preview not available</h1>
|
||||||
|
<p>
|
||||||
|
Type <code>{typename}</code> cannot be previewed.
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function PreviewRender() {
|
||||||
|
const token = (await cookies()).get("preview-token")?.value;
|
||||||
|
if (!token) {
|
||||||
|
return <ExpiredPreview />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await getClient().query(previewPageQuery, { token });
|
||||||
|
if (error) {
|
||||||
|
throw new Error(error.message);
|
||||||
|
}
|
||||||
|
if (!data?.page) {
|
||||||
|
return <ExpiredPreview />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = data.page;
|
||||||
|
const view = await (async () => {
|
||||||
|
switch (page.__typename) {
|
||||||
|
case "GenericPage": {
|
||||||
|
const props = await loadGenericPageProps({
|
||||||
|
pageOverride: page as GenericFragment,
|
||||||
|
});
|
||||||
|
return <GenericPageView {...props!} />;
|
||||||
|
}
|
||||||
|
case "StudioPage": {
|
||||||
|
const props = await loadStudioPageProps({
|
||||||
|
pageOverride: page as StudioFragment,
|
||||||
|
});
|
||||||
|
return <StudioPageView {...props} />;
|
||||||
|
}
|
||||||
|
case "SponsorsPage": {
|
||||||
|
const props = await loadSponsorsPageProps({
|
||||||
|
pageOverride: page as SponsorsPageFragment,
|
||||||
|
});
|
||||||
|
return <SponsorsPageView {...props} />;
|
||||||
|
}
|
||||||
|
case "EventPage": {
|
||||||
|
const props = await loadEventPageProps({
|
||||||
|
eventOverride: page as EventFragment,
|
||||||
|
});
|
||||||
|
return <EventPageView {...props!} />;
|
||||||
|
}
|
||||||
|
case "NewsPage": {
|
||||||
|
const props = await loadNewsPageProps({
|
||||||
|
newsOverride: page as NewsFragment,
|
||||||
|
});
|
||||||
|
return <NewsPageView {...props!} />;
|
||||||
|
}
|
||||||
|
case "AssociationPage": {
|
||||||
|
const props = await loadAssociationPageProps({
|
||||||
|
associationOverride: page as AssociationFragment,
|
||||||
|
});
|
||||||
|
return <AssociationPageView {...props!} />;
|
||||||
|
}
|
||||||
|
case "VenuePage": {
|
||||||
|
const props = await loadVenuePageProps({
|
||||||
|
venueOverride: page as VenueFragment,
|
||||||
|
});
|
||||||
|
return <VenuePageView {...props!} />;
|
||||||
|
}
|
||||||
|
case "HomePage": {
|
||||||
|
const props = await loadHomePageProps({
|
||||||
|
homeOverride: page as HomeFragment,
|
||||||
|
});
|
||||||
|
return <HomePageView {...props} />;
|
||||||
|
}
|
||||||
|
case "EventIndex": {
|
||||||
|
const props = await loadEventIndexProps();
|
||||||
|
return <EventIndexView {...props} />;
|
||||||
|
}
|
||||||
|
case "NewsIndex": {
|
||||||
|
const props = await loadNewsIndexProps({
|
||||||
|
indexOverride: page as NewsIndexFragment,
|
||||||
|
});
|
||||||
|
return <NewsIndexView {...props} />;
|
||||||
|
}
|
||||||
|
case "AssociationIndex": {
|
||||||
|
const props = await loadAssociationIndexProps({
|
||||||
|
indexOverride: page as AssociationIndexFragment,
|
||||||
|
});
|
||||||
|
return <AssociationIndexView {...props} />;
|
||||||
|
}
|
||||||
|
case "VenueIndex": {
|
||||||
|
const props = await loadVenueIndexProps({
|
||||||
|
indexOverride: page as VenueIndexFragment,
|
||||||
|
});
|
||||||
|
return <VenueIndexView {...props} />;
|
||||||
|
}
|
||||||
|
case "VenueRentalIndex": {
|
||||||
|
const props = await loadVenueRentalIndexProps({
|
||||||
|
indexOverride: page as VenueRentalIndexFragment,
|
||||||
|
});
|
||||||
|
return <VenueRentalIndexView {...props} />;
|
||||||
|
}
|
||||||
|
case "ContactIndex": {
|
||||||
|
const props = await loadContactIndexProps({
|
||||||
|
indexOverride: page as ContactIndexFragment,
|
||||||
|
});
|
||||||
|
return <ContactIndexView {...props} />;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return <UnsupportedType typename={page.__typename ?? "unknown"} />;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PreviewBanner />
|
||||||
|
{view}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,82 +1,19 @@
|
|||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { graphql } from "@/gql";
|
import {
|
||||||
import { SponsorsPage, SponsorBlock } from "@/gql/graphql";
|
SponsorsPageView,
|
||||||
import { getClient } from "@/app/client";
|
loadSponsorsPageProps,
|
||||||
import { PageHeader } from "@/components/general/PageHeader";
|
} from "@/components/sponsor/SponsorsPageView";
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
import { SponsorList } from "@/components/sponsor/SponsorList";
|
|
||||||
|
|
||||||
const sponsorsPageQuery = graphql(`
|
|
||||||
query sponsors {
|
|
||||||
page: sponsorsPage {
|
|
||||||
... on SponsorsPage {
|
|
||||||
...SponsorsPage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: Promise<{}> },
|
_: unknown,
|
||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { data, error } = await getClient().query(sponsorsPageQuery, {});
|
const { page } = await loadSponsorsPageProps();
|
||||||
|
return getSeoMetadata(page, parent);
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.page) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data.page as SponsorsPage;
|
|
||||||
const metadata = await getSeoMetadata(index, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SponsorsPageFragmentDefinition = graphql(`
|
|
||||||
fragment SponsorsPage on SponsorsPage {
|
|
||||||
... on SponsorsPage {
|
|
||||||
title
|
|
||||||
seoTitle
|
|
||||||
searchDescription
|
|
||||||
lead
|
|
||||||
body {
|
|
||||||
...Blocks
|
|
||||||
}
|
|
||||||
sponsors {
|
|
||||||
... on SponsorBlock {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
logo {
|
|
||||||
...Image
|
|
||||||
}
|
|
||||||
text
|
|
||||||
website
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const { data, error } = await getClient().query(sponsorsPageQuery, {});
|
const props = await loadSponsorsPageProps();
|
||||||
|
return <SponsorsPageView {...props} />;
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.page) {
|
|
||||||
throw new Error("Failed to render /sponsorer");
|
|
||||||
}
|
|
||||||
|
|
||||||
const page = data.page as SponsorsPage;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<PageHeader heading={page.title} lead={page.lead} />
|
|
||||||
{page.body && <PageContent blocks={page.body} />}
|
|
||||||
<SponsorList sponsors={page.sponsors as SponsorBlock[]} />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
|
import {
|
||||||
|
StudioPageView,
|
||||||
|
loadStudioPageProps,
|
||||||
|
} from "@/components/studio/StudioPageView";
|
||||||
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
|
export async function generateMetadata(
|
||||||
|
_: unknown,
|
||||||
|
parent: ResolvingMetadata
|
||||||
|
): Promise<Metadata | null> {
|
||||||
|
const { page } = await loadStudioPageProps();
|
||||||
|
return getSeoMetadata(page, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const props = await loadStudioPageProps();
|
||||||
|
return <StudioPageView {...props} />;
|
||||||
|
}
|
||||||
@@ -1,82 +1,19 @@
|
|||||||
import { Metadata, ResolvingMetadata } from "next";
|
import { Metadata, ResolvingMetadata } from "next";
|
||||||
import { graphql } from "@/gql";
|
import {
|
||||||
import { VenueFragment, VenueRentalIndexFragment } from "@/gql/graphql";
|
VenueRentalIndexView,
|
||||||
import { getClient } from "@/app/client";
|
loadVenueRentalIndexProps,
|
||||||
import { VenueList } from "@/components/venues/VenueList";
|
} from "@/components/venues/VenueRentalIndexView";
|
||||||
import { PageHeader } from "@/components/general/PageHeader";
|
|
||||||
import { BgPig } from "@/components/general/BgPig";
|
|
||||||
import { PageContent } from "@/components/general/PageContent";
|
|
||||||
import { getSeoMetadata } from "@/lib/seo";
|
import { getSeoMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
const venueRentalIndexQuery = graphql(`
|
|
||||||
query venueRentalIndex {
|
|
||||||
index: venueRentalIndex {
|
|
||||||
... on VenueRentalIndex {
|
|
||||||
...VenueRentalIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
venues: pages(contentType: "venues.VenuePage", limit: 100) {
|
|
||||||
... on VenuePage {
|
|
||||||
...Venue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const VenueRentalIndexDefinition = graphql(`
|
|
||||||
fragment VenueRentalIndex on VenueRentalIndex {
|
|
||||||
... on VenueRentalIndex {
|
|
||||||
title
|
|
||||||
seoTitle
|
|
||||||
searchDescription
|
|
||||||
lead
|
|
||||||
body {
|
|
||||||
...Blocks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: Promise<{}> },
|
_: unknown,
|
||||||
parent: ResolvingMetadata
|
parent: ResolvingMetadata
|
||||||
): Promise<Metadata | null> {
|
): Promise<Metadata | null> {
|
||||||
const { data, error } = await getClient().query(venueRentalIndexQuery, {});
|
const { index } = await loadVenueRentalIndexProps();
|
||||||
|
return getSeoMetadata(index, parent);
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.index) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data.index as VenueRentalIndexFragment;
|
|
||||||
const metadata = await getSeoMetadata(index, parent);
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const { data, error } = await getClient().query(venueRentalIndexQuery, {});
|
const props = await loadVenueRentalIndexProps();
|
||||||
|
return <VenueRentalIndexView {...props} />;
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
if (!data?.index || !data?.venues) {
|
|
||||||
throw new Error("Failed to render /utleie");
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = data.index as VenueRentalIndexFragment;
|
|
||||||
const venues = (data?.venues ?? []) as VenueFragment[];
|
|
||||||
const bookableVenues = venues.filter((venue) => venue.showAsBookable);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<main className="site-main" id="main">
|
|
||||||
<PageHeader heading={index.title} lead={index.lead} />
|
|
||||||
{index.body && <PageContent blocks={index.body} />}
|
|
||||||
<VenueList venues={bookableVenues} heading="Våre lokaler" />
|
|
||||||
</main>
|
|
||||||
<BgPig type="key" />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import { graphql } from "@/gql";
|
||||||
|
import { AssociationFragment, AssociationIndexFragment } from "@/gql/graphql";
|
||||||
|
import { getClient } from "@/app/client";
|
||||||
|
import { AssociationList } from "@/components/associations/AssociationList";
|
||||||
|
import { PageHeader } from "@/components/general/PageHeader";
|
||||||
|
import { PageContent } from "@/components/general/PageContent";
|
||||||
|
|
||||||
|
const AssociationIndexDefinition = graphql(`
|
||||||
|
fragment AssociationIndex on AssociationIndex {
|
||||||
|
__typename
|
||||||
|
title
|
||||||
|
seoTitle
|
||||||
|
searchDescription
|
||||||
|
lead
|
||||||
|
body {
|
||||||
|
...Blocks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const AssociationFragmentDefinition = graphql(`
|
||||||
|
fragment Association on AssociationPage {
|
||||||
|
__typename
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
title
|
||||||
|
seoTitle
|
||||||
|
searchDescription
|
||||||
|
excerpt
|
||||||
|
lead
|
||||||
|
body {
|
||||||
|
...Blocks
|
||||||
|
}
|
||||||
|
logo {
|
||||||
|
url
|
||||||
|
width
|
||||||
|
height
|
||||||
|
}
|
||||||
|
associationType
|
||||||
|
websiteUrl
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const allAssociationsQuery = graphql(`
|
||||||
|
query allAssociations {
|
||||||
|
index: associationIndex {
|
||||||
|
... on AssociationIndex {
|
||||||
|
...AssociationIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
associations: pages(
|
||||||
|
contentType: "associations.AssociationPage"
|
||||||
|
limit: 1000
|
||||||
|
) {
|
||||||
|
... on AssociationPage {
|
||||||
|
...Association
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export type AssociationIndexViewProps = {
|
||||||
|
index: AssociationIndexFragment;
|
||||||
|
associations: AssociationFragment[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function loadAssociationIndexProps(overrides?: {
|
||||||
|
indexOverride?: AssociationIndexFragment;
|
||||||
|
}): Promise<AssociationIndexViewProps> {
|
||||||
|
const { data, error } = await getClient().query(allAssociationsQuery, {});
|
||||||
|
if (error) throw new Error(error.message);
|
||||||
|
const index =
|
||||||
|
overrides?.indexOverride ?? (data?.index as AssociationIndexFragment | undefined);
|
||||||
|
if (!index) throw new Error("Failed to load /foreninger");
|
||||||
|
const associations = (data?.associations ?? []) as AssociationFragment[];
|
||||||
|
return { index, associations };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AssociationIndexView({
|
||||||
|
index,
|
||||||
|
associations,
|
||||||
|
}: AssociationIndexViewProps) {
|
||||||
|
return (
|
||||||
|
<main className="site-main" id="main">
|
||||||
|
<PageHeader heading={index.title} lead={index.lead} />
|
||||||
|
{index.body && <PageContent blocks={index.body} />}
|
||||||
|
<AssociationList
|
||||||
|
associations={associations}
|
||||||
|
heading="Foreninger og utvalg"
|
||||||
|
/>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { graphql } from "@/gql";
|
||||||
|
import { AssociationFragment } from "@/gql/graphql";
|
||||||
|
import { getClient } from "@/app/client";
|
||||||
|
import { AssociationHeader } from "@/components/associations/AssociationHeader";
|
||||||
|
import { PageContent } from "@/components/general/PageContent";
|
||||||
|
|
||||||
|
const associationBySlugQuery = graphql(`
|
||||||
|
query associationBySlug($slug: String!) {
|
||||||
|
association: page(
|
||||||
|
contentType: "associations.AssociationPage"
|
||||||
|
slug: $slug
|
||||||
|
) {
|
||||||
|
... on AssociationPage {
|
||||||
|
...Association
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export type AssociationPageViewProps = { association: AssociationFragment };
|
||||||
|
|
||||||
|
export async function loadAssociationPageProps(args: {
|
||||||
|
slug?: string;
|
||||||
|
associationOverride?: AssociationFragment;
|
||||||
|
}): Promise<AssociationPageViewProps | null> {
|
||||||
|
if (args.associationOverride) {
|
||||||
|
return { association: args.associationOverride };
|
||||||
|
}
|
||||||
|
if (!args.slug) throw new Error("loadAssociationPageProps needs slug or associationOverride");
|
||||||
|
const { data, error } = await getClient().query(associationBySlugQuery, {
|
||||||
|
slug: args.slug,
|
||||||
|
});
|
||||||
|
if (error) throw new Error(error.message);
|
||||||
|
const association = data?.association as AssociationFragment | undefined;
|
||||||
|
if (!association) return null;
|
||||||
|
return { association };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AssociationPageView({ association }: AssociationPageViewProps) {
|
||||||
|
return (
|
||||||
|
<main className="site-main" id="main">
|
||||||
|
<AssociationHeader association={association} />
|
||||||
|
<PageContent blocks={association.body} />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,15 +1,26 @@
|
|||||||
import { AccordionBlock as AccordionBlockType } from "@/gql/graphql";
|
import { graphql } from "@/gql";
|
||||||
|
import { type AccordionBlockFragment } from "@/gql/graphql";
|
||||||
import { Blocks } from "./Blocks";
|
import { Blocks } from "./Blocks";
|
||||||
import { Accordion } from "@/components/general/Accordion";
|
import { Accordion } from "@/components/general/Accordion";
|
||||||
|
|
||||||
|
const AccordionBlockFragmentDefinition = graphql(`
|
||||||
|
fragment AccordionBlock on AccordionBlock {
|
||||||
|
heading
|
||||||
|
body {
|
||||||
|
id
|
||||||
|
blockType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const AccordionBlock = ({
|
export const AccordionBlock = ({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: AccordionBlockType;
|
block: AccordionBlockFragment;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Accordion heading={block.heading}>
|
<Accordion heading={block.heading}>
|
||||||
<Blocks blocks={block.body} />
|
<Blocks blocks={block.body} />
|
||||||
</Accordion>
|
</Accordion>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,33 @@
|
|||||||
import { ContactEntityBlock as ContactEntityBlockType } from "@/gql/graphql";
|
import { graphql, unmaskFragment } from "@/gql";
|
||||||
|
import { type ContactEntityBlockFragment } from "@/gql/graphql";
|
||||||
import styles from "./contactEntityBlock.module.scss";
|
import styles from "./contactEntityBlock.module.scss";
|
||||||
import { formatNorwegianPhoneNumber, formatPhoneE164 } from "@/lib/common";
|
import {
|
||||||
|
ContactEntityFragmentDefinition,
|
||||||
|
ImageFragmentDefinition,
|
||||||
|
formatNorwegianPhoneNumber,
|
||||||
|
formatPhoneE164,
|
||||||
|
} from "@/lib/common";
|
||||||
import { Icon } from "../general/Icon";
|
import { Icon } from "../general/Icon";
|
||||||
import { Image } from "../general/Image";
|
import { Image } from "../general/Image";
|
||||||
|
|
||||||
|
const ContactEntityBlockFragmentDefinition = graphql(`
|
||||||
|
fragment ContactEntityBlock on ContactEntityBlock {
|
||||||
|
contactEntity {
|
||||||
|
...ContactEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const ContactEntityBlock = ({
|
export const ContactEntityBlock = ({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: ContactEntityBlockType;
|
block: ContactEntityBlockFragment;
|
||||||
}) => {
|
}) => {
|
||||||
const contact = block?.contactEntity;
|
const contact = unmaskFragment(
|
||||||
|
ContactEntityFragmentDefinition,
|
||||||
|
block?.contactEntity
|
||||||
|
);
|
||||||
|
const image = unmaskFragment(ImageFragmentDefinition, contact?.image);
|
||||||
|
|
||||||
if (!contact) {
|
if (!contact) {
|
||||||
return <></>;
|
return <></>;
|
||||||
@@ -21,18 +39,18 @@ export const ContactEntityBlock = ({
|
|||||||
return (
|
return (
|
||||||
<li className={styles.contactItem}>
|
<li className={styles.contactItem}>
|
||||||
<div className={styles.image}>
|
<div className={styles.image}>
|
||||||
{!contact.image && (
|
{!image && (
|
||||||
<img
|
<img
|
||||||
src="/assets/graphics/portrait-pig.svg"
|
src="/assets/graphics/portrait-pig.svg"
|
||||||
className={styles.portraitPlaceholder}
|
className={styles.portraitPlaceholder}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{contact.image && (
|
{image && (
|
||||||
<Image
|
<Image
|
||||||
src={contact.image.url}
|
src={image.url}
|
||||||
alt={contact.image.alt ?? ""}
|
alt={image.alt ?? ""}
|
||||||
width={contact.image.width}
|
width={image.width}
|
||||||
height={contact.image.height}
|
height={image.height}
|
||||||
sizes="25vw"
|
sizes="25vw"
|
||||||
className={styles.portrait}
|
className={styles.portrait}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
import { ContactListBlock as ContactListBlockType } from "@/gql/graphql";
|
import { graphql } from "@/gql";
|
||||||
|
import { type ContactListBlockFragment } from "@/gql/graphql";
|
||||||
import styles from "./contactListBlock.module.scss";
|
import styles from "./contactListBlock.module.scss";
|
||||||
import { Blocks } from "./Blocks";
|
import { Blocks } from "./Blocks";
|
||||||
|
|
||||||
|
const ContactListBlockFragmentDefinition = graphql(`
|
||||||
|
fragment ContactListBlock on ContactListBlock {
|
||||||
|
items {
|
||||||
|
blockType
|
||||||
|
... on ContactEntityBlock {
|
||||||
|
contactEntity {
|
||||||
|
...ContactEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const ContactListBlock = ({
|
export const ContactListBlock = ({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: ContactListBlockType;
|
block: ContactListBlockFragment;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<ul className={styles.contactList}>
|
<ul className={styles.contactList}>
|
||||||
|
|||||||
@@ -1,11 +1,37 @@
|
|||||||
import { ContactSectionBlock as ContactSectionBlockType } from "@/gql/graphql";
|
import { graphql } from "@/gql";
|
||||||
|
import {
|
||||||
|
type ContactSectionBlockFragment,
|
||||||
|
type ContactSubsectionBlockFragment,
|
||||||
|
} from "@/gql/graphql";
|
||||||
import styles from "./contactSection.module.scss";
|
import styles from "./contactSection.module.scss";
|
||||||
import { Blocks } from "./Blocks";
|
import { Blocks } from "./Blocks";
|
||||||
|
|
||||||
|
const ContactSectionBlockFragmentDefinition = graphql(`
|
||||||
|
fragment ContactSectionBlock on ContactSectionBlock {
|
||||||
|
title
|
||||||
|
text
|
||||||
|
blocks {
|
||||||
|
id
|
||||||
|
blockType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const ContactSubsectionBlockFragmentDefinition = graphql(`
|
||||||
|
fragment ContactSubsectionBlock on ContactSubsectionBlock {
|
||||||
|
title
|
||||||
|
text
|
||||||
|
blocks {
|
||||||
|
id
|
||||||
|
blockType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const ContactSectionBlock = ({
|
export const ContactSectionBlock = ({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: ContactSectionBlockType;
|
block: ContactSectionBlockFragment;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<section className={styles.contactSection}>
|
<section className={styles.contactSection}>
|
||||||
@@ -24,7 +50,7 @@ export const ContactSectionBlock = ({
|
|||||||
export const ContactSubsectionBlock = ({
|
export const ContactSubsectionBlock = ({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: ContactSectionBlockType;
|
block: ContactSubsectionBlockFragment;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<section className={styles.contactSubsection}>
|
<section className={styles.contactSubsection}>
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
import { EmbedBlock as EmbedBlockType } from "@/gql/graphql";
|
import { graphql } from "@/gql";
|
||||||
|
import { type EmbedBlockFragment } from "@/gql/graphql";
|
||||||
import styles from "./embedBlock.module.scss";
|
import styles from "./embedBlock.module.scss";
|
||||||
|
|
||||||
export const EmbedBlock = ({ block }: { block: EmbedBlockType }) => {
|
const EmbedBlockFragmentDefinition = graphql(`
|
||||||
|
fragment EmbedBlock on EmbedBlock {
|
||||||
|
url
|
||||||
|
embed
|
||||||
|
rawEmbed
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export const EmbedBlock = ({ block }: { block: EmbedBlockFragment }) => {
|
||||||
if (!block.embed) {
|
if (!block.embed) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
@@ -18,7 +27,7 @@ export const EmbedBlock = ({ block }: { block: EmbedBlockType }) => {
|
|||||||
*/
|
*/
|
||||||
let embedData: any = {};
|
let embedData: any = {};
|
||||||
try {
|
try {
|
||||||
embedData = JSON.parse(block.rawEmbed);
|
embedData = block.rawEmbed ? JSON.parse(block.rawEmbed) : {};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
embedData = {};
|
embedData = {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { FactBoxBlock as FactBoxBlockType } from "@/gql/graphql";
|
import { graphql } from "@/gql";
|
||||||
|
import { type FactBoxBlockFragment } from "@/gql/graphql";
|
||||||
import styles from "./factBoxBlock.module.scss";
|
import styles from "./factBoxBlock.module.scss";
|
||||||
|
|
||||||
type FactBoxBlockTypeWithAlias = FactBoxBlockType & {
|
const FactBoxBlockFragmentDefinition = graphql(`
|
||||||
factBoxBody?: string;
|
fragment FactBoxBlock on FactBoxBlock {
|
||||||
};
|
backgroundColor
|
||||||
|
factBoxBody: body
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const FactBoxBlock = ({
|
export const FactBoxBlock = ({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: FactBoxBlockTypeWithAlias;
|
block: FactBoxBlockFragment;
|
||||||
}) => {
|
}) => {
|
||||||
if (!block.factBoxBody) {
|
if (!block.factBoxBody) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@@ -1,22 +1,47 @@
|
|||||||
import { FeaturedBlock as FeaturedBlockType } from "@/gql/graphql";
|
import { graphql, unmaskFragment } from "@/gql";
|
||||||
|
import { type FeaturedBlockFragment } from "@/gql/graphql";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Image } from "@/components/general/Image";
|
import { Image } from "@/components/general/Image";
|
||||||
|
import { ImageFragmentDefinition } from "@/lib/common";
|
||||||
import styles from "./featuredBlock.module.scss";
|
import styles from "./featuredBlock.module.scss";
|
||||||
|
|
||||||
// the 'text' field has been aliased to 'featuredBlockText' and i'm
|
const FeaturedBlockFragmentDefinition = graphql(`
|
||||||
// using codegen the wrong way. let's specify the field here and move on
|
fragment FeaturedBlock on FeaturedBlock {
|
||||||
type FeaturedBlockTypeWithAlias = FeaturedBlockType & {
|
title
|
||||||
featuredBlockText: string;
|
featuredBlockText: text
|
||||||
};
|
linkText
|
||||||
|
imagePosition
|
||||||
|
backgroundColor
|
||||||
|
featuredPage {
|
||||||
|
contentType
|
||||||
|
pageType
|
||||||
|
url
|
||||||
|
... on EventPage {
|
||||||
|
featuredImage {
|
||||||
|
...Image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on NewsPage {
|
||||||
|
featuredImage {
|
||||||
|
...Image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
featuredImageOverride {
|
||||||
|
...Image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const FeaturedBlock = ({
|
export const FeaturedBlock = ({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: FeaturedBlockTypeWithAlias;
|
block: FeaturedBlockFragment;
|
||||||
}) => {
|
}) => {
|
||||||
const image = !!block.featuredImageOverride
|
const image = unmaskFragment(
|
||||||
? block.featuredImageOverride
|
ImageFragmentDefinition,
|
||||||
: null;
|
block.featuredImageOverride
|
||||||
|
);
|
||||||
// TODO: fetch image from target page
|
// TODO: fetch image from target page
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
import { HorizontalRuleBlock as HorizontalRuleBlockType } from "@/gql/graphql";
|
import { graphql } from "@/gql";
|
||||||
|
import { type HorizontalRuleBlockFragment } from "@/gql/graphql";
|
||||||
import { Image } from "@/components/general/Image";
|
import { Image } from "@/components/general/Image";
|
||||||
import styles from "./horizontalRuleBlock.module.scss";
|
import styles from "./horizontalRuleBlock.module.scss";
|
||||||
|
|
||||||
|
const HorizontalRuleBlockFragmentDefinition = graphql(`
|
||||||
|
fragment HorizontalRuleBlock on HorizontalRuleBlock {
|
||||||
|
color
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const HorizontalRuleBlock = ({
|
export const HorizontalRuleBlock = ({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: HorizontalRuleBlockType;
|
block: HorizontalRuleBlockFragment;
|
||||||
}) => {
|
}) => {
|
||||||
const knownColors = [
|
const knownColors = [
|
||||||
"deepBrick",
|
"deepBrick",
|
||||||
|
|||||||
@@ -1,8 +1,30 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { ImageSliderBlock as ImageSliderBlockType } from "@/gql/graphql";
|
import { graphql, unmaskFragment, type FragmentType } from "@/gql";
|
||||||
|
import { type ImageSliderBlockFragment } from "@/gql/graphql";
|
||||||
import { ImageFigure } from "@/components/general/Image";
|
import { ImageFigure } from "@/components/general/Image";
|
||||||
|
import { ImageFragmentDefinition } from "@/lib/common";
|
||||||
import styles from "./imageSliderBlock.module.scss";
|
import styles from "./imageSliderBlock.module.scss";
|
||||||
|
|
||||||
|
const ImageSliderItemFragmentDefinition = graphql(`
|
||||||
|
fragment ImageSliderItem on ImageSliderItemBlock {
|
||||||
|
image {
|
||||||
|
...Image
|
||||||
|
}
|
||||||
|
text
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export const ImageSliderBlockFragmentDefinition = graphql(`
|
||||||
|
fragment ImageSliderBlock on ImageSliderBlock {
|
||||||
|
images {
|
||||||
|
__typename
|
||||||
|
... on ImageSliderItemBlock {
|
||||||
|
...ImageSliderItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
// import swiper modules & styles
|
// import swiper modules & styles
|
||||||
import { Swiper, SwiperSlide } from "swiper/react";
|
import { Swiper, SwiperSlide } from "swiper/react";
|
||||||
import { Pagination, Navigation } from "swiper/modules";
|
import { Pagination, Navigation } from "swiper/modules";
|
||||||
@@ -11,17 +33,42 @@ import "swiper/css/pagination";
|
|||||||
import "swiper/css/navigation";
|
import "swiper/css/navigation";
|
||||||
import "./swiper.scss";
|
import "./swiper.scss";
|
||||||
|
|
||||||
|
const Slide = ({
|
||||||
|
item: maskedItem,
|
||||||
|
}: {
|
||||||
|
item: FragmentType<typeof ImageSliderItemFragmentDefinition>;
|
||||||
|
}) => {
|
||||||
|
const item = unmaskFragment(ImageSliderItemFragmentDefinition, maskedItem);
|
||||||
|
const image = unmaskFragment(ImageFragmentDefinition, item.image);
|
||||||
|
return (
|
||||||
|
<ImageFigure
|
||||||
|
key={image.id}
|
||||||
|
src={image.url}
|
||||||
|
alt={image.alt ?? ""}
|
||||||
|
width={image.width}
|
||||||
|
height={image.height}
|
||||||
|
attribution={image.attribution}
|
||||||
|
caption={item.text}
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const ImageSliderBlock = ({
|
export const ImageSliderBlock = ({
|
||||||
block,
|
block,
|
||||||
hero,
|
hero,
|
||||||
pageContent
|
pageContent,
|
||||||
}: {
|
}: {
|
||||||
block: ImageSliderBlockType | any;
|
block: ImageSliderBlockFragment;
|
||||||
hero?: boolean;
|
hero?: boolean;
|
||||||
pageContent?: boolean;
|
pageContent?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.imageSliderBlock} data-hero={hero} data-pagecontent={pageContent}>
|
<div
|
||||||
|
className={styles.imageSliderBlock}
|
||||||
|
data-hero={hero}
|
||||||
|
data-pagecontent={pageContent}
|
||||||
|
>
|
||||||
<Swiper
|
<Swiper
|
||||||
pagination={{
|
pagination={{
|
||||||
type: "fraction",
|
type: "fraction",
|
||||||
@@ -30,21 +77,16 @@ export const ImageSliderBlock = ({
|
|||||||
modules={[Pagination, Navigation]}
|
modules={[Pagination, Navigation]}
|
||||||
className="mySwiper"
|
className="mySwiper"
|
||||||
>
|
>
|
||||||
{block.images &&
|
{block.images?.map((item, index) => {
|
||||||
block.images.map((imageItem: any, index: number) => (
|
if (item?.__typename !== "ImageSliderItemBlock") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
<SwiperSlide key={index}>
|
<SwiperSlide key={index}>
|
||||||
<ImageFigure
|
<Slide item={item} />
|
||||||
key={imageItem.image.id}
|
|
||||||
src={imageItem.image.url}
|
|
||||||
alt={imageItem.image.alt ?? ""}
|
|
||||||
width={imageItem.image.width}
|
|
||||||
height={imageItem.image.height}
|
|
||||||
attribution={imageItem.image.attribution}
|
|
||||||
caption={imageItem.text}
|
|
||||||
sizes="100vw"
|
|
||||||
/>
|
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,31 @@
|
|||||||
import { ImageWithTextBlock as ImageWithTextBlockType } from "@/gql/graphql";
|
import { graphql, unmaskFragment } from "@/gql";
|
||||||
|
import { type ImageWithTextBlockFragment } from "@/gql/graphql";
|
||||||
import { ImageFigure } from "@/components/general/Image";
|
import { ImageFigure } from "@/components/general/Image";
|
||||||
|
import { ImageFragmentDefinition } from "@/lib/common";
|
||||||
|
|
||||||
|
const ImageWithTextBlockFragmentDefinition = graphql(`
|
||||||
|
fragment ImageWithTextBlock on ImageWithTextBlock {
|
||||||
|
image {
|
||||||
|
...Image
|
||||||
|
}
|
||||||
|
imageFormat
|
||||||
|
text
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export function ImageWithTextBlock({
|
export function ImageWithTextBlock({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: ImageWithTextBlockType;
|
block: ImageWithTextBlockFragment;
|
||||||
}) {
|
}) {
|
||||||
|
const image = unmaskFragment(ImageFragmentDefinition, block.image);
|
||||||
return (
|
return (
|
||||||
<ImageFigure
|
<ImageFigure
|
||||||
src={block.image.url}
|
src={image.url}
|
||||||
alt={block.image.alt ?? ""}
|
alt={image.alt ?? ""}
|
||||||
width={block.image.width}
|
width={image.width}
|
||||||
height={block.image.height}
|
height={image.height}
|
||||||
attribution={block.image.attribution}
|
attribution={image.attribution}
|
||||||
caption={block.text}
|
caption={block.text}
|
||||||
imageFormat={block.imageFormat}
|
imageFormat={block.imageFormat}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,13 +1,26 @@
|
|||||||
import { PageSectionBlock as PageSectionBlockType } from "@/gql/graphql";
|
import { graphql } from "@/gql";
|
||||||
|
import { type PageSectionBlockFragment } from "@/gql/graphql";
|
||||||
import styles from "./pageSection.module.scss";
|
import styles from "./pageSection.module.scss";
|
||||||
import { Blocks } from "./Blocks";
|
import { Blocks } from "./Blocks";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import { DecorativeIcon } from "../general/Icon";
|
import { DecorativeIcon } from "../general/Icon";
|
||||||
|
|
||||||
|
const PageSectionBlockFragmentDefinition = graphql(`
|
||||||
|
fragment PageSectionBlock on PageSectionBlock {
|
||||||
|
title
|
||||||
|
backgroundColor
|
||||||
|
icon
|
||||||
|
body {
|
||||||
|
id
|
||||||
|
blockType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const PageSectionBlock = ({
|
export const PageSectionBlock = ({
|
||||||
block,
|
block,
|
||||||
}: {
|
}: {
|
||||||
block: PageSectionBlockType;
|
block: PageSectionBlockFragment;
|
||||||
}) => {
|
}) => {
|
||||||
const anchor = slugify(block.title);
|
const anchor = slugify(block.title);
|
||||||
|
|
||||||
@@ -31,7 +44,7 @@ export const PageSectionBlock = ({
|
|||||||
export const PageSectionNavigationBlock = ({
|
export const PageSectionNavigationBlock = ({
|
||||||
sections,
|
sections,
|
||||||
}: {
|
}: {
|
||||||
sections: PageSectionBlockType[];
|
sections: PageSectionBlockFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
if (!sections.length) {
|
if (!sections.length) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
|
import { graphql } from "@/gql";
|
||||||
|
import { type RichTextBlockFragment } from "@/gql/graphql";
|
||||||
import styles from "./richTextBlock.module.scss";
|
import styles from "./richTextBlock.module.scss";
|
||||||
|
|
||||||
export const RichTextBlock = ({ block }: any) => {
|
const RichTextBlockFragmentDefinition = graphql(`
|
||||||
|
fragment RichTextBlock on RichTextBlock {
|
||||||
|
rawValue
|
||||||
|
value
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export const RichTextBlock = ({ block }: { block: RichTextBlockFragment }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.richTextBlock}
|
className={styles.richTextBlock}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { graphql } from "@/gql";
|
||||||
|
import { ContactIndexFragment } from "@/gql/graphql";
|
||||||
|
import { getClient } from "@/app/client";
|
||||||
|
import { GeneralContactBlock } from "@/components/blocks/GeneralContactBlock";
|
||||||
|
import { PageContent } from "@/components/general/PageContent";
|
||||||
|
import { PageHeader } from "@/components/general/PageHeader";
|
||||||
|
|
||||||
|
const ContactIndexDefinition = graphql(`
|
||||||
|
fragment ContactIndex on ContactIndex {
|
||||||
|
__typename
|
||||||
|
title
|
||||||
|
seoTitle
|
||||||
|
searchDescription
|
||||||
|
lead
|
||||||
|
body {
|
||||||
|
...Blocks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const contactQuery = graphql(`
|
||||||
|
query contacts {
|
||||||
|
index: contactIndex {
|
||||||
|
... on ContactIndex {
|
||||||
|
...ContactIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export type ContactIndexViewProps = { index: ContactIndexFragment };
|
||||||
|
|
||||||
|
export async function loadContactIndexProps(overrides?: {
|
||||||
|
indexOverride?: ContactIndexFragment;
|
||||||
|
}): Promise<ContactIndexViewProps> {
|
||||||
|
if (overrides?.indexOverride) {
|
||||||
|
return { index: overrides.indexOverride };
|
||||||
|
}
|
||||||
|
const { data, error } = await getClient().query(contactQuery, {});
|
||||||
|
if (error) throw new Error(error.message);
|
||||||
|
const index = data?.index as ContactIndexFragment | undefined;
|
||||||
|
if (!index) throw new Error("Failed to load /kontakt");
|
||||||
|
return { index };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ContactIndexView({ index }: ContactIndexViewProps) {
|
||||||
|
return (
|
||||||
|
<main className="site-main" id="main">
|
||||||
|
<PageHeader heading={index.title} lead={index.lead} />
|
||||||
|
<GeneralContactBlock />
|
||||||
|
{index.body && <PageContent blocks={index.body} />}
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
useQueryState,
|
useQueryState,
|
||||||
parseAsStringLiteral,
|
parseAsStringLiteral,
|
||||||
@@ -8,7 +9,10 @@ import {
|
|||||||
} from "nuqs";
|
} from "nuqs";
|
||||||
import { EventItem } from "./EventItem";
|
import { EventItem } from "./EventItem";
|
||||||
import { EventFilter, EventFilterExplained } from "./EventFilter";
|
import { EventFilter, EventFilterExplained } from "./EventFilter";
|
||||||
|
import { unmaskFragment } from "@/gql";
|
||||||
import {
|
import {
|
||||||
|
EventCategoryFragmentDefinition,
|
||||||
|
EventOrganizerFragmentDefinition,
|
||||||
EventFragment,
|
EventFragment,
|
||||||
EventCategory,
|
EventCategory,
|
||||||
SingularEvent,
|
SingularEvent,
|
||||||
@@ -47,11 +51,11 @@ export const EventContainer = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [mode, setMode] = useQueryState(
|
const [mode, setMode] = useQueryState(
|
||||||
"mode",
|
"mode",
|
||||||
parseAsStringLiteral(["list", "calendar"]).withDefault("list")
|
parseAsStringLiteral(["list", "calendar"]).withDefault("list"),
|
||||||
);
|
);
|
||||||
const [categories, setCategories] = useQueryState(
|
const [categories, setCategories] = useQueryState(
|
||||||
"category",
|
"category",
|
||||||
parseAsArrayOf(parseAsString, ",")
|
parseAsArrayOf(parseAsString, ","),
|
||||||
);
|
);
|
||||||
const [organizer, setOrganizer] = useQueryState("organizer", parseAsString);
|
const [organizer, setOrganizer] = useQueryState("organizer", parseAsString);
|
||||||
const [venue, setVenue] = useQueryState("venue", parseAsString);
|
const [venue, setVenue] = useQueryState("venue", parseAsString);
|
||||||
@@ -71,20 +75,43 @@ export const EventContainer = ({
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Allow filtering on all organizers that have upcoming events
|
Allow filtering on all organizers that have upcoming events
|
||||||
Filtering on an organizer with no upcoming events will work, but be hidden from dropdown
|
Filtering on an organizer with no upcoming events will work,
|
||||||
|
and in that case it's included in the dropdown
|
||||||
*/
|
*/
|
||||||
|
const allOrganizers = unmaskFragment(
|
||||||
|
EventOrganizerFragmentDefinition,
|
||||||
|
events.flatMap((x) => x.organizers),
|
||||||
|
);
|
||||||
const uniqueOrganizers: string[] = unique(
|
const uniqueOrganizers: string[] = unique(
|
||||||
events
|
allOrganizers
|
||||||
.map((x) => x.organizers)
|
|
||||||
.flat()
|
|
||||||
.filter((x) => x.__typename === "EventOrganizer")
|
.filter((x) => x.__typename === "EventOrganizer")
|
||||||
.map((x) => x.slug)
|
.map((x) => x.slug)
|
||||||
.filter((x) => typeof x === "string")
|
.filter((x) => typeof x === "string" && x !== ""),
|
||||||
);
|
);
|
||||||
const filterableOrganizers = uniqueOrganizers
|
const filterableOrganizers = uniqueOrganizers
|
||||||
.map((slug) => eventOrganizers.find((haystack) => haystack.slug === slug))
|
.map((slug) => eventOrganizers.find((haystack) => haystack.slug === slug))
|
||||||
.filter((x) => x !== undefined) as EventOrganizer[];
|
.filter((x) => x !== undefined) as EventOrganizer[];
|
||||||
|
|
||||||
|
// If an organizer is selected that's not in the upcoming list, add it
|
||||||
|
if (
|
||||||
|
organizer &&
|
||||||
|
!filterableOrganizers.some((org) => org.slug === organizer)
|
||||||
|
) {
|
||||||
|
const selectedOrganizer = eventOrganizers.find(
|
||||||
|
(org) => org.slug === organizer,
|
||||||
|
);
|
||||||
|
if (selectedOrganizer) {
|
||||||
|
filterableOrganizers.push(selectedOrganizer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset organizer if slug is invalid */
|
||||||
|
useEffect(() => {
|
||||||
|
if (!eventOrganizers.find((haystack) => haystack.slug === organizer)) {
|
||||||
|
setOrganizer(null);
|
||||||
|
}
|
||||||
|
}, [eventOrganizers, organizer]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Allow filtering on all venues that have upcoming events
|
Allow filtering on all venues that have upcoming events
|
||||||
Filtering on a venue with no upcoming events will work,
|
Filtering on a venue with no upcoming events will work,
|
||||||
@@ -96,36 +123,48 @@ export const EventContainer = ({
|
|||||||
.flat()
|
.flat()
|
||||||
.filter((x) => x.venue?.__typename === "VenuePage")
|
.filter((x) => x.venue?.__typename === "VenuePage")
|
||||||
.map((x) => x.venue?.slug)
|
.map((x) => x.venue?.slug)
|
||||||
.filter((x) => typeof x === "string")
|
.filter((x) => typeof x === "string"),
|
||||||
);
|
);
|
||||||
const filterableVenues = venues
|
const filterableVenues = venues
|
||||||
.filter(
|
.filter(
|
||||||
(x) => venueSlugsWithUpcomingEvents.includes(x.slug) || x.slug === venue
|
(x) => venueSlugsWithUpcomingEvents.includes(x.slug) || x.slug === venue,
|
||||||
)
|
)
|
||||||
.map((x) => venues.find((haystack) => haystack.slug === x.slug))
|
.map((x) => venues.find((haystack) => haystack.slug === x.slug))
|
||||||
.filter((x) => x !== undefined) as VenueFragment[];
|
.filter((x) => x !== undefined) as VenueFragment[];
|
||||||
|
|
||||||
const filteredEvents = events
|
/* Reset venue if slug is invalid */
|
||||||
.filter(
|
useEffect(() => {
|
||||||
(x) =>
|
if (!venues.find((haystack) => haystack.slug === venue)) {
|
||||||
!organizer ||
|
setVenue(null);
|
||||||
x.organizers.map((organizer) => organizer.slug).includes(organizer)
|
}
|
||||||
)
|
}, [venues, venue]);
|
||||||
.filter(
|
|
||||||
(x) =>
|
const filteredEvents = events.filter((event) => {
|
||||||
!categories ||
|
if (organizer) {
|
||||||
x.categories
|
const organizers = unmaskFragment(
|
||||||
.map((eventCategory) => eventCategory.slug)
|
EventOrganizerFragmentDefinition,
|
||||||
.filter((x) => categories.includes(x)).length !== 0
|
event.organizers,
|
||||||
)
|
);
|
||||||
.filter(
|
if (!organizers.some((o) => o.slug === organizer)) {
|
||||||
(x) =>
|
return false;
|
||||||
!venue ||
|
}
|
||||||
x.occurrences
|
}
|
||||||
.map((occurrence) => occurrence.venue?.slug)
|
if (categories) {
|
||||||
.filter((x) => typeof x === "string")
|
const eventCategories = unmaskFragment(
|
||||||
.includes(venue)
|
EventCategoryFragmentDefinition,
|
||||||
);
|
event.categories,
|
||||||
|
);
|
||||||
|
if (!eventCategories.some((c) => categories.includes(c.slug))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (venue) {
|
||||||
|
if (!event.occurrences.some((occ) => occ.venue?.slug === venue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
const [showFilter, setShowFilter] = useState(false);
|
const [showFilter, setShowFilter] = useState(false);
|
||||||
function toggleFilter() {
|
function toggleFilter() {
|
||||||
@@ -273,12 +312,12 @@ function maybeYear(yearMonthString: string) {
|
|||||||
|
|
||||||
const EventCalendar = ({ events }: { events: EventFragment[] }) => {
|
const EventCalendar = ({ events }: { events: EventFragment[] }) => {
|
||||||
const futureSingularEvents = getSingularEvents(events).filter(
|
const futureSingularEvents = getSingularEvents(events).filter(
|
||||||
(x) => x.occurrence?.start && isTodayOrFuture(x.occurrence.start)
|
(x) => x.occurrence?.start && isTodayOrFuture(x.occurrence.start),
|
||||||
);
|
);
|
||||||
const eventsByDate = organizeEventsInCalendar(futureSingularEvents);
|
const eventsByDate = organizeEventsInCalendar(futureSingularEvents);
|
||||||
const yearMonths = Object.keys(eventsByDate);
|
const yearMonths = Object.keys(eventsByDate);
|
||||||
const [visibleYearMonths, setVisibleYearMonths] = useState(
|
const [visibleYearMonths, setVisibleYearMonths] = useState(
|
||||||
yearMonths.slice(0, 2)
|
yearMonths.slice(0, 2),
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleYearMonth = (yearMonth: string) => {
|
const toggleYearMonth = (yearMonth: string) => {
|
||||||
@@ -298,9 +337,9 @@ const EventCalendar = ({ events }: { events: EventFragment[] }) => {
|
|||||||
yearMonthSum +
|
yearMonthSum +
|
||||||
Object.values(week).reduce(
|
Object.values(week).reduce(
|
||||||
(weekSum, day) => weekSum + day.length,
|
(weekSum, day) => weekSum + day.length,
|
||||||
0
|
0,
|
||||||
),
|
),
|
||||||
0
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
import { EventFragment } from "@/lib/event";
|
import { unmaskFragment } from "@/gql";
|
||||||
|
import {
|
||||||
|
EventCategoryFragmentDefinition,
|
||||||
|
EventFragment,
|
||||||
|
} from "@/lib/event";
|
||||||
|
import { ImageFragmentDefinition } from "@/lib/common";
|
||||||
import styles from "./eventHeader.module.scss";
|
import styles from "./eventHeader.module.scss";
|
||||||
import { ImageFigure } from "@/components/general/Image";
|
import { ImageFigure } from "@/components/general/Image";
|
||||||
import { Breadcrumb } from "../general/Breadcrumb";
|
|
||||||
import { Icon } from "../general/Icon";
|
import { Icon } from "../general/Icon";
|
||||||
|
|
||||||
export const EventHeader = ({ event }: { event: EventFragment }) => {
|
export const EventHeader = ({ event }: { event: EventFragment }) => {
|
||||||
const featuredImage: any = event.featuredImage;
|
const featuredImage = unmaskFragment(
|
||||||
|
ImageFragmentDefinition,
|
||||||
|
event.featuredImage
|
||||||
|
);
|
||||||
|
const categories = unmaskFragment(
|
||||||
|
EventCategoryFragmentDefinition,
|
||||||
|
event.categories
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.eventHeader}>
|
<div className={styles.eventHeader}>
|
||||||
<div className={styles.heading}>
|
<div className={styles.heading}>
|
||||||
{/*<Breadcrumb link="/arrangementer" text="Arrangement" />*/}
|
{/*<Breadcrumb link="/arrangementer" text="Arrangement" />*/}
|
||||||
{event.categories.length > 0 && (
|
{categories.length > 0 && (
|
||||||
<div className={styles.categories}>
|
<div className={styles.categories}>
|
||||||
{event.categories.map((category) => (
|
{categories.map((category) => (
|
||||||
<div key={category.name} className="tag">
|
<div key={category.name} className="tag">
|
||||||
{category.name}
|
{category.name}
|
||||||
</div>
|
</div>
|
||||||
@@ -33,7 +44,7 @@ export const EventHeader = ({ event }: { event: EventFragment }) => {
|
|||||||
{featuredImage && (
|
{featuredImage && (
|
||||||
<ImageFigure
|
<ImageFigure
|
||||||
src={featuredImage.url}
|
src={featuredImage.url}
|
||||||
alt={featuredImage.alt}
|
alt={featuredImage.alt ?? ""}
|
||||||
width={featuredImage.width}
|
width={featuredImage.width}
|
||||||
height={featuredImage.height}
|
height={featuredImage.height}
|
||||||
attribution={featuredImage.attribution}
|
attribution={featuredImage.attribution}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { Suspense } from "react";
|
||||||
|
import { VenueFragment } from "@/gql/graphql";
|
||||||
|
import { getClient } from "@/app/client";
|
||||||
|
import { EventContainer } from "@/components/events/EventContainer";
|
||||||
|
import { PageHeader } from "@/components/general/PageHeader";
|
||||||
|
import {
|
||||||
|
EventCategory,
|
||||||
|
EventFragment,
|
||||||
|
EventOrganizer,
|
||||||
|
eventsOverviewQuery,
|
||||||
|
} from "@/lib/event";
|
||||||
|
|
||||||
|
export type EventIndexViewProps = {
|
||||||
|
events: EventFragment[];
|
||||||
|
eventCategories: EventCategory[];
|
||||||
|
eventOrganizers: EventOrganizer[];
|
||||||
|
venues: VenueFragment[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function loadEventIndexProps(): Promise<EventIndexViewProps> {
|
||||||
|
const { data, error } = await getClient().query(eventsOverviewQuery, {});
|
||||||
|
if (error) throw new Error(error.message);
|
||||||
|
if (
|
||||||
|
!data?.index ||
|
||||||
|
!data?.events?.futureEvents ||
|
||||||
|
!data?.eventCategories ||
|
||||||
|
!data?.eventOrganizers ||
|
||||||
|
!data?.venues
|
||||||
|
) {
|
||||||
|
throw new Error("Failed to load /arrangementer");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
events: data.events.futureEvents as EventFragment[],
|
||||||
|
eventCategories: data.eventCategories as EventCategory[],
|
||||||
|
eventOrganizers: data.eventOrganizers as EventOrganizer[],
|
||||||
|
venues: data.venues as VenueFragment[],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EventIndexView({
|
||||||
|
events,
|
||||||
|
eventCategories,
|
||||||
|
eventOrganizers,
|
||||||
|
venues,
|
||||||
|
}: EventIndexViewProps) {
|
||||||
|
return (
|
||||||
|
<main className="site-main" id="main">
|
||||||
|
<PageHeader heading="Dette skjer på Chateau Neuf" align="left" />
|
||||||
|
<Suspense>
|
||||||
|
<EventContainer
|
||||||
|
events={events}
|
||||||
|
eventCategories={eventCategories}
|
||||||
|
eventOrganizers={eventOrganizers}
|
||||||
|
venues={venues}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { graphql } from "@/gql";
|
||||||
|
import { EventFragment } from "@/gql/graphql";
|
||||||
|
import { getClient } from "@/app/client";
|
||||||
|
import { EventDetails } from "@/components/events/EventDetails";
|
||||||
|
import { EventHeader } from "@/components/events/EventHeader";
|
||||||
|
import { BgPig } from "@/components/general/BgPig";
|
||||||
|
import { PageContent } from "@/components/general/PageContent";
|
||||||
|
import { getEventPig } from "@/lib/event";
|
||||||
|
|
||||||
|
const eventBySlugQuery = graphql(`
|
||||||
|
query eventBySlug($slug: String!) {
|
||||||
|
event: page(contentType: "events.EventPage", slug: $slug) {
|
||||||
|
... on EventPage {
|
||||||
|
...Event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export type EventPageViewProps = { event: EventFragment };
|
||||||
|
|
||||||
|
export async function loadEventPageProps(args: {
|
||||||
|
slug?: string;
|
||||||
|
eventOverride?: EventFragment;
|
||||||
|
}): Promise<EventPageViewProps | null> {
|
||||||
|
if (args.eventOverride) {
|
||||||
|
return { event: args.eventOverride };
|
||||||
|
}
|
||||||
|
if (!args.slug) throw new Error("loadEventPageProps needs slug or eventOverride");
|
||||||
|
const { data, error } = await getClient().query(eventBySlugQuery, {
|
||||||
|
slug: args.slug,
|
||||||
|
});
|
||||||
|
if (error) throw new Error(error.message);
|
||||||
|
const event = data?.event as EventFragment | undefined;
|
||||||
|
if (!event) return null;
|
||||||
|
return { event };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EventPageView({ event }: EventPageViewProps) {
|
||||||
|
const eventPig = getEventPig(event);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<main className="site-main" id="main">
|
||||||
|
<EventHeader event={event} />
|
||||||
|
<EventDetails event={event} />
|
||||||
|
{event.lead && (
|
||||||
|
<div
|
||||||
|
className="lead event-lead"
|
||||||
|
dangerouslySetInnerHTML={{ __html: event.lead }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<PageContent blocks={event.body} />
|
||||||
|
</main>
|
||||||
|
{eventPig && <BgPig type={eventPig} color="white" />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { EventFragment, EventOrganizer } from "@/lib/event";
|
import { unmaskFragment } from "@/gql";
|
||||||
|
import {
|
||||||
|
EventFragment,
|
||||||
|
EventOrganizerFragmentDefinition,
|
||||||
|
} from "@/lib/event";
|
||||||
import styles from "./organizerList.module.scss";
|
import styles from "./organizerList.module.scss";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
|
|
||||||
export const OrganizerList = ({ event }: { event: EventFragment }) => {
|
export const OrganizerList = ({ event }: { event: EventFragment }) => {
|
||||||
const total = event.organizers.length;
|
const organizers = unmaskFragment(
|
||||||
|
EventOrganizerFragmentDefinition,
|
||||||
|
event.organizers
|
||||||
|
);
|
||||||
|
const total = organizers.length;
|
||||||
return (
|
return (
|
||||||
<div className={styles.organizerList}>
|
<div className={styles.organizerList}>
|
||||||
{event.organizers.map((organizer, index) => {
|
{organizers.map((organizer, index) => {
|
||||||
const url = organizer.association?.url ?? organizer.externalUrl ?? null;
|
const url = organizer.association?.url ?? organizer.externalUrl ?? null;
|
||||||
const hasValidUrl =
|
const hasValidUrl =
|
||||||
typeof url === "string" &&
|
typeof url === "string" &&
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { graphql } from "@/gql";
|
||||||
|
import { GenericFragment } from "@/gql/graphql";
|
||||||
|
import { getClient } from "@/app/client";
|
||||||
|
import { PageHeader } from "@/components/general/PageHeader";
|
||||||
|
import { PageContent } from "@/components/general/PageContent";
|
||||||
|
import { BgPig } from "@/components/general/BgPig";
|
||||||
|
|
||||||
|
const GenericFragmentDefinition = graphql(`
|
||||||
|
fragment Generic on GenericPage {
|
||||||
|
__typename
|
||||||
|
id
|
||||||
|
urlPath
|
||||||
|
seoTitle
|
||||||
|
searchDescription
|
||||||
|
title
|
||||||
|
lead
|
||||||
|
pig
|
||||||
|
body {
|
||||||
|
...Blocks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const genericPageByUrlPathQuery = graphql(`
|
||||||
|
query genericPageByUrl($urlPath: String!) {
|
||||||
|
page: page(contentType: "generic.GenericPage", urlPath: $urlPath) {
|
||||||
|
... on GenericPage {
|
||||||
|
...Generic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export type GenericPageViewProps = { page: GenericFragment };
|
||||||
|
|
||||||
|
export async function loadGenericPageProps(args: {
|
||||||
|
urlPath?: string;
|
||||||
|
pageOverride?: GenericFragment;
|
||||||
|
}): Promise<GenericPageViewProps | null> {
|
||||||
|
if (args.pageOverride) {
|
||||||
|
return { page: args.pageOverride };
|
||||||
|
}
|
||||||
|
if (!args.urlPath) throw new Error("loadGenericPageProps needs urlPath or pageOverride");
|
||||||
|
const { data, error } = await getClient().query(genericPageByUrlPathQuery, {
|
||||||
|
urlPath: args.urlPath,
|
||||||
|
});
|
||||||
|
if (error) throw new Error(error.message);
|
||||||
|
const page = data?.page as GenericFragment | undefined;
|
||||||
|
if (!page) return null;
|
||||||
|
return { page };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GenericPageView({ page }: GenericPageViewProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<main className="site-main" id="main">
|
||||||
|
<PageHeader heading={page.title} lead={page.lead} />
|
||||||
|
<PageContent blocks={page.body} />
|
||||||
|
</main>
|
||||||
|
{page.pig && <BgPig type={page.pig} color="white" />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import NextImage, { ImageProps as NextImageProps } from "next/image";
|
import NextImage, { type ImageProps as NextImageProps } from "next/image";
|
||||||
import styles from "./image.module.scss";
|
import styles from "./image.module.scss";
|
||||||
|
|
||||||
type ImageProps = NextImageProps & {
|
type ImageProps = NextImageProps & {
|
||||||
@@ -7,14 +7,35 @@ type ImageProps = NextImageProps & {
|
|||||||
imageFormat?: string | null; // "original" | "bleed" | "fullWidth"
|
imageFormat?: string | null; // "original" | "bleed" | "fullWidth"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isSvgSrc(src: NextImageProps["src"]): src is string {
|
||||||
|
return typeof src === "string" && /\.svg(\?|#|$)/i.test(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MaybeNextImage(props: NextImageProps) {
|
||||||
|
if (isSvgSrc(props.src)) {
|
||||||
|
const { src, alt, width, height, className, style } = props;
|
||||||
|
return (
|
||||||
|
<img // eslint-disable-line @next/next/no-img-element
|
||||||
|
src={src}
|
||||||
|
alt={alt}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
className={className}
|
||||||
|
style={style}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <NextImage {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
export function ImageFigure(props: ImageProps) {
|
export function ImageFigure(props: ImageProps) {
|
||||||
const { attribution, caption, imageFormat, ...nextImageProps } = props;
|
const { attribution, caption, imageFormat, ...imageProps } = props;
|
||||||
return (
|
return (
|
||||||
<figure
|
<figure
|
||||||
className={`${styles.image} ${imageFormat ? styles[imageFormat] : ""}`}
|
className={`${styles.image} ${imageFormat ? styles[imageFormat] : ""}`}
|
||||||
>
|
>
|
||||||
<div className={styles.imageWrapper}>
|
<div className={styles.imageWrapper}>
|
||||||
<NextImage {...nextImageProps} />
|
<MaybeNextImage {...imageProps} />
|
||||||
{attribution && <div className={styles.attribution}>{attribution}</div>}
|
{attribution && <div className={styles.attribution}>{attribution}</div>}
|
||||||
</div>
|
</div>
|
||||||
{caption && <figcaption>{caption}</figcaption>}
|
{caption && <figcaption>{caption}</figcaption>}
|
||||||
@@ -23,5 +44,5 @@ export function ImageFigure(props: ImageProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Image(props: NextImageProps) {
|
export function Image(props: NextImageProps) {
|
||||||
return <NextImage {...props} />;
|
return <MaybeNextImage {...props} />;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@ export const Newsletter = ({ url }: { url?: string }) => {
|
|||||||
<DecorativeIcon type="email" />
|
<DecorativeIcon type="email" />
|
||||||
</div>
|
</div>
|
||||||
<h2>Nyhetsbrev</h2>
|
<h2>Nyhetsbrev</h2>
|
||||||
<p>Meld deg på vårt nyhetsbrev og hold deg oppdatert på program og nyheter!</p>
|
<p>Meld deg på vårt nyhetsbrev og hold deg oppdatert på arrangementer og siste nytt!</p>
|
||||||
</div>
|
</div>
|
||||||
<a href={link} target="_blank" className="button primary">
|
<a href={link} target="_blank" className="button primary">
|
||||||
Meld deg på
|
Meld deg på
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user