diff --git a/src/open_inwoner/cms/objects/cms_plugins.py b/src/open_inwoner/cms/objects/cms_plugins.py index 2fc06260cb..d136659684 100644 --- a/src/open_inwoner/cms/objects/cms_plugins.py +++ b/src/open_inwoner/cms/objects/cms_plugins.py @@ -1,9 +1,10 @@ +import json from django.utils.translation import ugettext_lazy as _ from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool -from .models import ObjectsList +from .models import ComponentChoices, ObjectsList @plugin_pool.register_plugin @@ -12,3 +13,28 @@ class ObjectsListPlugin(CMSPluginBase): name = _("Object List Plugin") render_template = "cms/objects/objects_list.html" cache = False + + def _get_render_template(self, context, instance, placeholder): + default_template = super()._get_render_template(context, instance, placeholder) + if component := instance.component: + match component: + case ComponentChoices.link: + return "cms/objects/objects_list.html" + case ComponentChoices.map: + return "cms/objects/objects_map.html" + + return default_template + + def render(self, context, instance, placeholder): + request = context["request"] + context["instance"] = instance + context["objects"] = [] + + if request.user.is_authenticated and (bsn := request.user.bsn): + objects = instance.get_objects_by_bsn(bsn) + match instance.component: + case ComponentChoices.link: + context["objects"] = instance.convert_objects_to_actiondata(objects) + case ComponentChoices.map: + context["objects"] = instance.convert_objects_to_geodata(objects) + return context diff --git a/src/open_inwoner/cms/objects/models.py b/src/open_inwoner/cms/objects/models.py index d2da14c207..554fff3b7b 100644 --- a/src/open_inwoner/cms/objects/models.py +++ b/src/open_inwoner/cms/objects/models.py @@ -1,20 +1,130 @@ -import os - +import json +from glom import glom +from glom.core import PathAccessError, PathAssignError from django.db import models from django.utils.translation import ugettext_lazy as _ +from djchoices import ChoiceItem, DjangoChoices + from cms.models import CMSPlugin from objectsapiclient.models import Configuration, ObjectTypeField +class StatusChoices(DjangoChoices): + open = ChoiceItem("open", _("Open")) + submitted = ChoiceItem("ingediend", _("Submitted")) + processed = ChoiceItem("verwerkt", _("Processed")) + closed = ChoiceItem("gesloten", _("Closed")) + + +class DateOrderChoices(DjangoChoices): + ascend = ChoiceItem("+", _("Ascend")) + descent = ChoiceItem("-", _("Descent")) + + +class ComponentChoices(DjangoChoices): + link = ChoiceItem("link", _("Link")) + map = ChoiceItem("map", _("Map")) + + class ObjectsList(CMSPlugin): title = models.CharField(_("Title"), max_length=250) object_type = ObjectTypeField() # Stores the UUID of the selected object_type + status = models.CharField( + _("Status"), + max_length=250, + choices=StatusChoices.choices, + blank=True, + ) + component = models.CharField( + _("Component"), + max_length=30, + choices=ComponentChoices.choices, + default=ComponentChoices.link, + ) + date_order = models.CharField( + _("Date Order"), + max_length=1, + choices=DateOrderChoices.choices, + default=DateOrderChoices.descent, + ) + bsn_path = models.CharField( + _("BSN Path"), + max_length=250, + help_text=_("The python path to the bsn value"), + blank=True, + ) + object_title = models.CharField( + _("Object Title"), max_length=250, default="data.title" + ) + object_date = models.CharField(_("Object Date"), max_length=250, default="start_at") + object_link = models.CharField( + _("Object Link"), max_length=250, default="data.formulier.value" + ) + + def _return_self(self): + return f"{self.title}, {self.object_type}, {self.bsn_path}" def get_objects(self): return Configuration.get_solo().client.get_objects( object_type_uuid=self.object_type ) + def get_objects_by_bsn(self, bsn): + data_attrs = [] + + if self.object_type and self.bsn_path: + data_attrs.append(self.bsn_path + "__exact__" + str(bsn)) + if self.status: + data_attrs.append("status__exact__" + self.status) + + return Configuration.get_solo().client.get_objects_by_bsn( + object_type_uuid=self.object_type, + ordering=self.date_order + "record__startAt", + data_attrs=",".join(data_attrs), + ) + + def convert_objects_to_actiondata(self, objects): + object_list = [] + for object in objects: + try: + object_list.append( + { + "title": glom(object.record, self.object_title), + "date": glom(object.record, self.object_date), + "link": glom(object.record, self.object_link), + } + ) + except (PathAccessError, PathAssignError): + pass + + return object_list + + def convert_objects_to_geodata(self, objects): + features = [] + for object in objects: + if geometry := object.record.get("geometry"): + try: + features.append( + { + "type": "Feature", + "geometry": geometry, + "properties": { + "name": glom(object.record, self.object_title), + "date": glom(object.record, self.object_date), + "link": glom(object.record, self.object_link), + }, + } + ) + except (PathAccessError, PathAssignError): + pass + + return json.dumps( + { + "type": "FeatureCollection", + "features": features, + } + ) + def __str__(self): return self.title diff --git a/src/open_inwoner/components/templates/components/DenhaagAction/DenhaagAction.html b/src/open_inwoner/components/templates/components/DenhaagAction/DenhaagAction.html new file mode 100644 index 0000000000..bd5d5972d3 --- /dev/null +++ b/src/open_inwoner/components/templates/components/DenhaagAction/DenhaagAction.html @@ -0,0 +1,15 @@ +{% load l10n i18n %} + + +
{{title}}
+
+
+ +
+
+ +
+
+
\ No newline at end of file diff --git a/src/open_inwoner/components/templatetags/denhaag_action_tags.py b/src/open_inwoner/components/templatetags/denhaag_action_tags.py new file mode 100644 index 0000000000..11a68535e7 --- /dev/null +++ b/src/open_inwoner/components/templatetags/denhaag_action_tags.py @@ -0,0 +1,11 @@ +from django import template + +register = template.Library() + + +@register.inclusion_tag("components/DenhaagAction/DenhaagAction.html") +def denhaag_action(title, date, link, **kwargs): + kwargs["title"] = title + kwargs["date"] = date + kwargs["link"] = link + return kwargs diff --git a/src/open_inwoner/js/components/map/index.js b/src/open_inwoner/js/components/map/index.js index c6be8d11b2..65c52b261a 100644 --- a/src/open_inwoner/js/components/map/index.js +++ b/src/open_inwoner/js/components/map/index.js @@ -94,6 +94,8 @@ class Map { address_line_2, phonenumber, email, + date, + link, ...properties } = feature.properties @@ -103,6 +105,8 @@ class Map { const displayAddress2 = escapeVariableText(address_line_2) const displayPhonenumber = escapeVariableText(phonenumber) const displayEmail = escapeVariableText(email) + const displayDate = escapeVariableText(date) + const buttonLink = escapeVariableText(link) let title = '' if (locationDetailView) { @@ -122,6 +126,7 @@ class Map {
+

${displayDate}

${displayAddress1}

${displayAddress2}

@@ -130,6 +135,9 @@ class Map { ${displayEmail} + + Action +
` } diff --git a/src/open_inwoner/scss/components/DenhaagAction/DenhaagAction.scss b/src/open_inwoner/scss/components/DenhaagAction/DenhaagAction.scss new file mode 100644 index 0000000000..13a45e8e3f --- /dev/null +++ b/src/open_inwoner/scss/components/DenhaagAction/DenhaagAction.scss @@ -0,0 +1,115 @@ +.denhaag-action { + display: flex; + justify-content: space-between; + flex-direction: column; + gap: .5rem; + color: hsla(0, 0%, 18%, 1); + background-color: hsl(0 0% 100%); + padding-inline-start: 1rem; + padding-inline-end: 1rem; + padding-block-start: 1rem; + padding-block-end: 1rem; + border-style: solid; + border-width: 1px; + border-color: hsl(0 0% 82%); + + @media (width >= 768px) { + flex-direction: row; + align-items: center; + gap: 1.5rem; + padding-block-start: .8rem; + padding-block-end: .8rem; + } + + &__details { + display: flex; + flex-shrink: 0; + flex-direction: column; + gap: 1rem; + + @media (width >= 768px) { + flex-direction: row; + align-items: center; + gap: 1.5rem; + } + } + + &__content { + font-weight: 600; + } + + &__date { + display: flex; + align-items: center; + gap: .8rem; + color: hsl(0 0% 29%); + font-weight: 500 + + &--warning { + display: flex; + align-items: center; + gap: .5rem; + color: hsl(23 100% 27%); + font-weight: 600; + } + } + + &__actions { + display: flex; + align-items: center; + gap: .5rem; + + & > a { + width: 100%; + + @media (width >= 768px) { + width: auto; + } + } + } + + &__link-icon { + flex-shrink: 0; + width: 20px; + color: hsl(207 80% 35%); + } + + &__warning-icon { + flex-shrink: 0; + width: 20px; + color: hsl(23 99% 44%); + } + + &--single { + padding-inline-start: 1rem; + padding-inline-end: 1rem; + padding-block-start: 1rem; + padding-block-end: 1rem; + border-top: none; + border-left: none; + border-right: none; + text-decoration: none; + + &:hover, + &.denhaag-action--hover { + background-color: hsl(0 0% 95%); + } + + &:focus, + &.denhaag-action--focus { + outline-color: hsl(47 100% 25%);; + outline-width: 2px; + outline-style: dashed; + } + + .denhaag-action__details { + flex-direction: row; + justify-content: space-between; + gap: .5rem; + + @media (width >= 768px) { + gap: .8rem + } + } + } + } \ No newline at end of file diff --git a/src/open_inwoner/scss/components/_index.scss b/src/open_inwoner/scss/components/_index.scss index 050955c39b..2b5cc5fb31 100644 --- a/src/open_inwoner/scss/components/_index.scss +++ b/src/open_inwoner/scss/components/_index.scss @@ -14,6 +14,7 @@ @import './Container/Container.scss'; @import './CookieConsent/CookieBanner.scss'; @import './Dashboard/Dashboard.scss'; +@import './DenhaagAction/DenhaagAction.scss'; @import './Divider/Divider.scss'; @import './Dropdown/Dropdown.scss'; @import './Faq/Faq.scss'; diff --git a/src/open_inwoner/templates/cms/objects/objects_list.html b/src/open_inwoner/templates/cms/objects/objects_list.html index 6e34aa1c31..a5976d2e74 100644 --- a/src/open_inwoner/templates/cms/objects/objects_list.html +++ b/src/open_inwoner/templates/cms/objects/objects_list.html @@ -1,17 +1,16 @@ -{% load i18n sekizai_tags %} +{% load i18n sekizai_tags utils denhaag_action_tags %} -{% load i18n button_tags card_tags utils icon_tags %}

{{ instance.title }}

-
- {% for object in instance.get_objects %} - {% render_card image_object_fit="cover" %} -
-

{{ object.record.index }}

- {{ object.uuid }} -
- {% endrender_card %} + +
+ {% if objects %} + {% for object in objects %} + {% denhaag_action title=object.title date=object.date link=object.link %} {% endfor %} + {% else %} +

Er staan geen taken gevonden

+ {% endif %}
diff --git a/src/open_inwoner/templates/cms/objects/objects_map.html b/src/open_inwoner/templates/cms/objects/objects_map.html new file mode 100644 index 0000000000..350fa7affe --- /dev/null +++ b/src/open_inwoner/templates/cms/objects/objects_map.html @@ -0,0 +1,13 @@ +{% load i18n sekizai_tags utils map_tags %} + +

+ {{ instance.title }} +

+
+ {% if objects %} + {% map 52.1326 5.2913 zoom=3 geojson_feature_collection=objects %} + {% else %} +

Er staan geen taken gevonden

+ {% endif %} +
+ diff --git a/src/open_inwoner/utils/templatetags/utils.py b/src/open_inwoner/utils/templatetags/utils.py index 832d8ab7a9..1154b42ace 100644 --- a/src/open_inwoner/utils/templatetags/utils.py +++ b/src/open_inwoner/utils/templatetags/utils.py @@ -5,8 +5,6 @@ from humanfriendly import format_size -from open_inwoner.configurations.models import SiteConfiguration - register = template.Library()