Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mijn Berichten demo (VNG Hackathon 2024) #1442

Draft
wants to merge 44 commits into
base: develop
Choose a base branch
from
Draft
Changes from 10 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5a732e5
[#2798] Updated attempt at MijnTaken / TasksPlugin for OIP
alextreme Oct 6, 2024
29126f7
[#2798] Flake8 cleanup
alextreme Oct 6, 2024
1df2ce8
Setup basic Mijn Berichten urls and views
swrichards Oct 7, 2024
8c38c70
Add a mock Bericht object and model
swrichards Oct 7, 2024
4b4b752
[#2799] First set-up for front-end services-berichten
jiromaykin Oct 7, 2024
4116f90
[#2798] Only showing block if there are tasks
alextreme Oct 7, 2024
7a1eefc
Merge pull request #1425 from maykinmedia/swr/mijn-services-hackathon…
swrichards Oct 8, 2024
243695e
[#2799] Rebase
jiromaykin Oct 7, 2024
6f2239a
[#2799] First version of some styling for berichtenlist and detail
jiromaykin Oct 8, 2024
6cab617
[#2799] iSorted
jiromaykin Oct 8, 2024
56c9a16
Merge pull request #1426 from maykinmedia/feature/2799-frontend-mijnb…
swrichards Oct 8, 2024
22f3abe
Add objects-api-client-django pinned to Hackathon branch
swrichards Oct 8, 2024
61abd02
Connect Berichten views to Objects API
swrichards Oct 8, 2024
7179da6
Merge pull request #1424 from maykinmedia/issue/2798-tasks-cms-plugin…
alextreme Oct 8, 2024
e7e91b5
[#2798] Only show open tasks
alextreme Oct 8, 2024
7b6c2a1
Add objects-api-client-django pinned to Hackathon branch
swrichards Oct 8, 2024
3af2ab8
Connect Berichten views to Objects API
swrichards Oct 8, 2024
663fe21
Merge pull request #1429 from maykinmedia/swr/hackathon-fetch-objects
swrichards Oct 8, 2024
6117076
Merge pull request #1430 from maykinmedia/issue/mijn-taken-filter-on-…
alextreme Oct 8, 2024
2c52058
Update bericht opened status upon accessing detail page
swrichards Oct 9, 2024
5b7c7da
Add a URL to mark a bericht as unread
swrichards Oct 9, 2024
a6bd263
[#2799] Improved template for detailbericht + added menu items
jiromaykin Oct 8, 2024
d0826c2
Merge branch 'mijn-services-hackathon-2024' into issue/2799-design-mi…
jiromaykin Oct 9, 2024
0f97264
Merge pull request #1433 from maykinmedia/swr/hackathon-update-berich…
alextreme Oct 9, 2024
df3bdce
[#2799] Improved list template
jiromaykin Oct 9, 2024
a426754
[#2799] Improved template for detailbericht + added menu items
jiromaykin Oct 8, 2024
9db01c0
[#2799] Adjusted breadcrumb
jiromaykin Oct 9, 2024
dbfc2e3
Merge pull request #1432 from maykinmedia/issue/2799-design-mijnberic…
swrichards Oct 9, 2024
4c6f3fb
[#2799] Cleaned-up code and margins
jiromaykin Oct 9, 2024
c717882
Update bericht opened status upon accessing detail page
swrichards Oct 9, 2024
e466df9
Add a URL to mark a bericht as unread
swrichards Oct 9, 2024
ade2fc3
[#2799] Improved template for detailbericht + added menu items
jiromaykin Oct 8, 2024
f4b3beb
Merge branch 'mijn-services-hackathon-2024' into issue/2799-final-hac…
jiromaykin Oct 10, 2024
5cb9706
Merge pull request #1434 from maykinmedia/issue/2799-final-hackathon-…
swrichards Oct 10, 2024
5989412
Fix serialization of bericht dates to Javascript code
swrichards Oct 10, 2024
a30eab2
Add mark unread button to Bericht detail
swrichards Oct 10, 2024
f3c5134
Update Mijn Berichten link with template tag
swrichards Oct 10, 2024
ea0e446
Remove handeling block from Bericht detail
swrichards Oct 10, 2024
f04cecf
Merge pull request #1435 from maykinmedia/swr/hackathon-ui-tweaks
alextreme Oct 10, 2024
91c82fb
Improved acess checks for Bericht views
swrichards Oct 10, 2024
707ecf5
Add bericht download functionality
swrichards Oct 11, 2024
9072cdd
Better UI logic for handling missing einddatum handelingstermijn
swrichards Oct 11, 2024
cedfcdb
Merge pull request #1436 from maykinmedia/swr/better-access-checks
alextreme Oct 11, 2024
d289785
Merge pull request #1437 from maykinmedia/swr/download-bericht-attach…
alextreme Oct 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions requirements/base.in
Original file line number Diff line number Diff line change
@@ -96,9 +96,6 @@ zgw-consumers-oas
notifications-api-common
git+https://github.com/maykinmedia/objects-api-client-django.git@f499caf#egg=objects-api-client-django




# 2FA SMS verification
oath
messagebird
10 changes: 4 additions & 6 deletions requirements/base.txt
Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@ asgiref==3.7.2
# via django
asn1crypto==1.5.1
# via webauthn
async-timeout==4.0.3
# via redis
attrs==21.2.0
# via
# glom
@@ -342,9 +344,7 @@ faker==27.0.0
fontawesomefree==6.4.2
# via -r requirements/base.in
fonttools[woff]==4.43.0
# via
# fonttools
# weasyprint
# via weasyprint
furl==2.1.3
# via
# -r requirements/base.in
@@ -537,9 +537,7 @@ sqlparse==0.4.4
svglib==1.5.1
# via easy-thumbnails
tablib[html,ods,xls,xlsx,yaml]==3.1.0
# via
# django-import-export
# tablib
# via django-import-export
tinycss2==1.1.1
# via
# -r requirements/base.in
10 changes: 5 additions & 5 deletions requirements/ci.txt
Original file line number Diff line number Diff line change
@@ -37,6 +37,11 @@ asn1crypto==1.5.1
# webauthn
astroid==2.15.8
# via pylint
async-timeout==4.0.3
# via
# -c requirements/base.txt
# -r requirements/base.txt
# redis
attrs==21.2.0
# via
# -c requirements/base.txt
@@ -482,7 +487,6 @@ django-two-factor-auth[phonenumberslite,webauthn]==1.16.0
# via
# -c requirements/base.txt
# -r requirements/base.txt
# django-two-factor-auth
# maykin-2fa
django-view-breadcrumbs==2.5.1
# via
@@ -555,7 +559,6 @@ easy-thumbnails[svg]==2.8.5
# -r requirements/base.txt
# django-filer
# djangocms-picture
# easy-thumbnails
ecs-logging==2.1.0
# via
# -c requirements/base.txt
@@ -609,7 +612,6 @@ fonttools[woff]==4.43.0
# via
# -c requirements/base.txt
# -r requirements/base.txt
# fonttools
# weasyprint
freezegun==1.1.0
# via -r requirements/test-tools.in
@@ -845,7 +847,6 @@ pydantic[email]==2.6.4
# via
# -c requirements/base.txt
# -r requirements/base.txt
# pydantic
pydantic-core==2.16.3
# via
# -c requirements/base.txt
@@ -1057,7 +1058,6 @@ tablib[html,ods,xls,xlsx,yaml]==3.1.0
# -c requirements/base.txt
# -r requirements/base.txt
# django-import-export
# tablib
tblib==1.7.0
# via -r requirements/test-tools.in
tinycss2==1.1.1
10 changes: 5 additions & 5 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
@@ -43,6 +43,11 @@ astroid==2.15.8
# -c requirements/ci.txt
# -r requirements/ci.txt
# pylint
async-timeout==4.0.3
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
# redis
attrs==21.2.0
# via
# -c requirements/ci.txt
@@ -528,7 +533,6 @@ django-two-factor-auth[phonenumberslite,webauthn]==1.16.0
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
# django-two-factor-auth
# maykin-2fa
django-view-breadcrumbs==2.5.1
# via
@@ -605,7 +609,6 @@ easy-thumbnails[svg]==2.8.5
# -r requirements/ci.txt
# django-filer
# djangocms-picture
# easy-thumbnails
ecs-logging==2.1.0
# via
# -c requirements/ci.txt
@@ -672,7 +675,6 @@ fonttools[woff]==4.43.0
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
# fonttools
# weasyprint
freezegun==1.1.0
# via
@@ -980,7 +982,6 @@ pydantic[email]==2.6.4
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
# pydantic
pydantic-core==2.16.3
# via
# -c requirements/ci.txt
@@ -1251,7 +1252,6 @@ tablib[html,ods,xls,xlsx,yaml]==3.1.0
# -c requirements/ci.txt
# -r requirements/ci.txt
# django-import-export
# tablib
tblib==1.7.0
# via
# -c requirements/ci.txt
2 changes: 2 additions & 0 deletions src/open_inwoner/cms/plugins/cms_plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from .appointments import UserAppointmentsPlugin
from .tasks import TasksPlugin
from .userfeed import UserFeedPlugin
from .videoplayer import VideoPlayerPlugin

__all__ = [
"UserAppointmentsPlugin",
"UserFeedPlugin",
"TasksPlugin",
"VideoPlayerPlugin",
]
31 changes: 31 additions & 0 deletions src/open_inwoner/cms/plugins/cms_plugins/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from django.utils.translation import gettext as _

from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool

from ..models import TasksConfig


@plugin_pool.register_plugin
class TasksPlugin(CMSPluginBase):
"""
Uses the Objects API to retrieve and show tasks according to the MijnTaken Objecttypes schema
Reuses the UserFeedPlugin template
"""

model = TasksConfig
name = _("Task list Plugin")
render_template = "cms/plugins/tasks/tasks.html"
cache = False

def render(self, context, instance, placeholder):
request = context["request"]
context["instance"] = instance
context["tasks"] = []

if request.user.is_authenticated and (bsn := request.user.bsn):
tasks = instance.get_tasks_by_bsn(bsn)
for task in tasks:
task["task_url"] = task["url"]["uri"]
context["tasks"] = tasks
return context
53 changes: 53 additions & 0 deletions src/open_inwoner/cms/plugins/migrations/0006_tasksconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Generated by Django 4.2.16 on 2024-10-06 13:07

import django.db.models.deletion
from django.db import migrations, models

import objectsapiclient.models


class Migration(migrations.Migration):

dependencies = [
("cms", "0022_auto_20180620_1551"),
("plugins", "0005_userappointments"),
]

operations = [
migrations.CreateModel(
name="TasksConfig",
fields=[
(
"cmsplugin_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
related_name="%(app_label)s_%(class)s",
serialize=False,
to="cms.cmsplugin",
),
),
(
"title",
models.CharField(
default="Mijn Taken",
help_text="The title of the tasks block",
max_length=250,
verbose_name="Title",
),
),
(
"object_type",
objectsapiclient.models.ObjectTypeField(
db_index=False, max_length=100
),
),
],
options={
"abstract": False,
},
bases=("cms.cmsplugin",),
),
]
2 changes: 2 additions & 0 deletions src/open_inwoner/cms/plugins/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from .appointments import UserAppointments
from .tasks import TasksConfig
from .userfeed import UserFeed
from .videoplayer import VideoPlayer

__all__ = [
"UserAppointments",
"UserFeed",
"TasksConfig",
"VideoPlayer",
]
41 changes: 41 additions & 0 deletions src/open_inwoner/cms/plugins/models/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from cms.models import CMSPlugin
from objectsapiclient.models import Configuration, ObjectTypeField


class TasksConfig(CMSPlugin):
title = models.CharField(
_("Title"),
max_length=250,
help_text=_("The title of the tasks block"),
default=_("Mijn Taken"),
)
object_type = ObjectTypeField() # Stores the UUID of the selected object_type

def __str__(self):
return self.title or super().__str__()

def get_tasks(self):
# TODO: note that this filters client-side, better would be to filter this
# in the Objects API GET-request using data_attr
return [
obj.record["data"]
for obj in Configuration.get_solo().client.get_objects(
object_type_uuid=self.object_type
)
]

def get_tasks_by_bsn(self, bsn, status="open"):
tasks = []
for task in self.get_tasks():
identificatie = task["identificatie"]
if not identificatie or identificatie["type"] != "bsn":
continue
task_bsn = identificatie["value"]
if task_bsn and task_bsn == bsn:
if status and task["status"] != status:
continue
tasks += [task]
return tasks
1 change: 1 addition & 0 deletions src/open_inwoner/conf/base.py
Original file line number Diff line number Diff line change
@@ -581,6 +581,7 @@
"ProductLocationPlugin",
"UserFeedPlugin",
"UserAppointmentsPlugin",
"TasksPlugin",
],
"text_only_plugins": ["LinkPlugin"],
"name": _("Content"),
4 changes: 4 additions & 0 deletions src/open_inwoner/conf/fixtures/django-admin-index.json
Original file line number Diff line number Diff line change
@@ -336,6 +336,10 @@
"openzaak",
"zaaktypeinformatieobjecttypeconfig"
],
[
"objectsapiclient",
"configuration"
],
[
"qmatic",
"qmaticconfig"
32 changes: 32 additions & 0 deletions src/open_inwoner/templates/cms/plugins/tasks/tasks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{% load i18n icon_tags button_tags %}

{% if instance and tasks %}
<section class="plugin userfeed">
<header class="userfeed__header">
<div class="heading-2__indicator">
<h2 class="utrecht-heading-2 {% if userfeed.action_required %}indicator{% endif %}">
{{ instance.title }}
</h2>
</div>
</header>

<div class="card-container card-container--columns-2 plugin-card">
{% for task in tasks %}
<a href="{{ task.task_url }}" target="_blank" class="card card--status card--status--info {% if task.status != "open" %}card--completed{% endif %}">
<div class="userfeed__marker userfeed__marker--info"></div>

<div class="card__body card__body--tabled">
<h3 class="utrecht-heading-3 userfeed__title">Status:{{ task.status }} - Soort: {{ task.soort }}</h3>
{% include "components/StatusIndicator/StatusIndicator.html" with status_indicator=info status_indicator_text="Let op!" %}
<p class="userfeed-card__description">
<span class="status">{{ task.titel }}</span>
</p>
<span class="button button--icon-before button--transparent">
{% icon icon="east" icon_position="after" primary=True outlined=True %}
</span>
</div>
</a>
{% endfor %}
</div>
</section>
{% endif %}
165 changes: 107 additions & 58 deletions src/open_inwoner/templates/pages/berichten/list.html
Original file line number Diff line number Diff line change
@@ -1,69 +1,118 @@
{% extends 'master.html' %}
{% load i18n button_tags card_tags map_tags utils icon_tags file_tags action_tags dropdown_tags anchor_menu_tags file_tags %}
{% load i18n grid_tags button_tags icon_tags %}

{% block content %}
{% if bericht %}
{% render_grid %}
{% render_column span=12 %}
<h1 class="utrecht-heading-1" id="title">{{ bericht.onderwerp }}</h1>
<aside class="bericht__dashboard dashboard" aria-label="{% trans "Dashboard" %}">
<ul class="dashboard__list">
<li class="dashboard__list-item">
<p class="utrecht-paragraph utrecht-paragraph--oip utrecht-paragraph--oip-compact">
<span class="dashboard__item-label">Ontvangstdatum:</span> {{ bericht.publicatiedatum }}</p>
</li>
<li class="dashboard__list-item">
<p class="utrecht-paragraph utrecht-paragraph--oip utrecht-paragraph--oip-compact">
<span class="dashboard__item-label">Bericht type: </span> {{ bericht.bericht_type }}</p>
</li>
<li class="dashboard__list-item">
<p class="utrecht-paragraph utrecht-paragraph--oip utrecht-paragraph--oip-compact">
<span class="dashboard__item-label">{% if bericht.einddatum_handelingstermijn %}<span aria-hidden="true" class="material-icons-outlined icon--alert">warning</span>{% endif %}
Deadline: </span> {{ bericht.einddatum_handelingstermijn }}
<span class="dashboard__item-label"><span id="remainingDays" class="utrecht-paragraph table__item--notification-danger"></span> <!-- Placeholder for remaining days --></span>
</p>
</li>
</ul>
</aside>
{% endrender_column %}
{% endrender_grid %}

<div class="berichtenlist">
<h1 class="utrecht-heading-1" id="title">
Mijn berichten
</h1>
<p class="utrecht-paragraph">Welkom in uw berichtencentrum. Hier vindt u een overzicht van uw notificaties.</p>
{% render_grid %}
{% render_column start=4 span=6 %}
<section class="bericht__userfeed plugin userfeed">
<div class="card-container card-container--columns-1 plugin-card">
<a href="#handelingsperspectief" class="card card--status card--status--info ">
<div class="userfeed__marker userfeed__marker--info"></div>
<div class="card__body card__body--tabled">
<h3 class="utrecht-heading-3 userfeed__title">Actie</h3>
<p class="userfeed-card__description">
{% if bericht.handelingsperspectief %}
<span class="status">{{ bericht.handelingsperspectief }}</span>
{% else %}
<span class="status">{% trans "Geen verdere acties nodig. U hoeft niets te doen." %}</span>
{% endif %}
</p>
<span class="button button--icon-before button--transparent">
<span aria-hidden="true" class="material-icons-outlined ">east</span>
</span>
</div>
</a>
</div>
</section>
{# End of handelingsperspectief alert #}

<div class="berichtenlist__table">
<table class="table">
<thead class="table__heading">
<tr>
<td class="table__item berichtenlist__table-item--wide">Onderwerp</td>
<td class="table__item">Einddatum</td>
<td class="table__item">Status</td>
<td class="table__item">Bericht type</td>
</tr>
</thead>
<tbody>
{% if not berichten %}
<b>{% trans "U heeft op dit moment nog geen berichten." %}</b>
{% endif %}
<section class="bericht">
<div>
<p class="bericht__text">{{ bericht.bericht_tekst|linebreaks }}</p>
</div>

{% for bericht in berichten %}
<tr class="berichtenlist__table-row">
<td class="table__header berichtenlist__table-item--wide">
<a href="{{ bericht.object_uuid }}" class="link" aria-label="{{ bericht.berichtTekst }} " title="{{ bericht.berichtTekst }} ">
<span class="link__text {% if not bericht.geopend %}berichtenlist--unread{% endif %}">{{ bericht.onderwerp }}</span>
</a>
</td>
<td class="table__item table__item--no-lb">
<a href="{{ bericht.object_uuid }}" class="link">
{{ bericht.publicatiedatum }}</a></td>
<td class="table__item">
<a href="{{ bericht.object_uuid }}" class="link">
{% if bericht.geopend %}
<div class="table__item--notification-success">
Geopend
</div>
{% else %}
<div class="table__item--notification-danger">
Niet geopend
</div>
{% endif %}
</a>
</td>
<td class="table__item berichtenlist__table-item--flex">
<a href="{{ bericht.object_uuid }}" class="link nohover">
{{ bericht.bericht_type }}

<span class="button button--icon-before button--transparent button--secondary">
<span aria-hidden="true" class="material-icons-outlined ">arrow_forward</span>
</span>
<div>
<a href="/hackathon/berichten/" class="button button--primary bericht__actions" id="handelingsperspectief">
{% icon "arrow_backward" %}Terug naar mijn berichten
</a>
</div>
{% if destination %}
<div>
<a href="{{ destination.url }}" id="destination_link" class="button button--transparent bericht__actions">
<span>{{ destination.label }}</span>{% icon "arrow_forward" %}
</a>
</td>
</tr>
{% endfor %}
</div>
{% endif %}
</section>

</tbody>
</table>
{% if bericht.bijlages %}
<section class="bericht__bijlagen case-detail__documents">
<div class="heading-2__indicator">
<h2 class="utrecht-heading-2 " id="documents">Bijlagen</h2>
</div>
<div class="file-list">
<ul class="file-list__list">
{% for attachment in bericht.bijlages %}
<li class="file-list__list-item">
<aside class="file">
<div class="file__container ">
<div class="file__file">
<div class="file__symbol">
<span aria-hidden="true" class="material-icons-outlined ">image</span>
</div>
<div class="file__data">
<span class="file__name">
{{ attachment }}
(PDF)
</span>
<span class="file__date">{{ bericht.publicatiedatum }}</span>
</div>
<a href="https://www.maykinmedia.nl/media/filer_public/94/50/9450f9e9-70d1-4d00-99e4-08bc49fb3dc7/2194530_nl_2019.pdf" download="" class="link link--icon link--icon-position-before file__download link--primary file__download" aria-label="Download namefile" title="Download dit bestand">
<span aria-hidden="true" class="material-icons-outlined ">download</span>
</a>
</div>
</div>
</aside>
</li>
{% endfor %}
</ul>
</div>
</section>
{% else %}
<h2 class="utrecht-heading-2">{% trans 'Er zijn geen bijlagen bij dit bericht.' %}</h2>
{% endif %}

{# end service list#}
</div>
{% endrender_column %}
{% endrender_grid %}

</div>
{% endblock %}
{% else %}
<h2 class="utrecht-heading-2">{% trans 'Dit bericht is momenteel niet beschikbaar.' %}</h2>
{% endif %}
{% endblock content %}