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

[#1791] Update design for case statuses #821

Merged
merged 11 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 37 additions & 1 deletion src/open_inwoner/cms/cases/tests/test_contactform.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ def setUp(self):
vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar,
indicatieInternOfExtern="extern",
)
#
# statuses
#
self.status_new = generate_oas_component(
"zrc",
"schemas/Status",
url=f"{ZAKEN_ROOT}statussen/3da81560-c7fc-476a-ad13-beu760sle929",
zaak=self.zaak["url"],
statustype=f"{CATALOGI_ROOT}statustypen/e3798107-ab27-4c3c-977d-777yu878km09",
datumStatusGezet="2021-01-12",
statustoelichting="",
)
self.status_finish = generate_oas_component(
"zrc",
"schemas/Status",
Expand All @@ -122,14 +134,32 @@ def setUp(self):
datumStatusGezet="2021-03-12",
statustoelichting="",
)
#
# status types
#
self.status_type_new = generate_oas_component(
"ztc",
"schemas/StatusType",
url=self.status_new["statustype"],
zaaktype=self.zaaktype["url"],
catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a",
omschrijving="Initial request",
omschrijvingGeneriek="Nieuw",
statustekst="",
volgnummer=1,
isEindstatus=False,
)
self.status_type_finish = generate_oas_component(
"ztc",
"schemas/StatusType",
url=self.status_finish["statustype"],
zaaktype=self.zaaktype["url"],
catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a",
omschrijving="Finish",
omschrijvingGeneriek="some content",
omschrijvingGeneriek="Afgehandeld",
statustekst="",
volgnummer=1,
isEindstatus=True,
)
self.result = generate_oas_component(
"zrc",
Expand Down Expand Up @@ -181,6 +211,12 @@ def _setUpMocks(self, m):
]:
self.matchers.append(m.get(resource["url"], json=resource))

# mock `fetch_status_types_no_cache`
m.get(
f"{CATALOGI_ROOT}statustypen?zaaktype={self.zaak['zaaktype']}",
json=paginated_response([self.status_type_new, self.status_type_finish]),
)

self.matchers += [
m.get(
f"{ZAKEN_ROOT}rollen?zaak={self.zaak['url']}",
Expand Down
38 changes: 37 additions & 1 deletion src/open_inwoner/cms/cases/tests/test_htmx.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ def setUp(self) -> None:
vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar,
indicatieInternOfExtern="extern",
)
#
# statuses
#
self.status_new = generate_oas_component(
"zrc",
"schemas/Status",
url=f"{ZAKEN_ROOT}statussen/3da81560-c7fc-476a-ad13-beu760sle929",
zaak=self.zaak["url"],
statustype=f"{CATALOGI_ROOT}statustypen/e3798107-ab27-4c3c-977d-777yu878km09",
datumStatusGezet="2021-01-12",
statustoelichting="",
)
self.status_finish = generate_oas_component(
"zrc",
"schemas/Status",
Expand All @@ -131,14 +143,32 @@ def setUp(self) -> None:
datumStatusGezet="2021-03-12",
statustoelichting="",
)
#
# status types
#
self.status_type_new = generate_oas_component(
"ztc",
"schemas/StatusType",
url=self.status_new["statustype"],
zaaktype=self.zaaktype["url"],
catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a",
omschrijving="Initial request",
omschrijvingGeneriek="Nieuw",
statustekst="",
volgnummer=1,
isEindstatus=False,
)
self.status_type_finish = generate_oas_component(
"ztc",
"schemas/StatusType",
url=self.status_finish["statustype"],
zaaktype=self.zaaktype["url"],
catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a",
omschrijving="Finish",
omschrijvingGeneriek="some content",
omschrijvingGeneriek="Afgehandeld",
statustekst="",
volgnummer=1,
isEindstatus=True,
)
self.user_role = generate_oas_component(
"zrc",
Expand Down Expand Up @@ -285,6 +315,12 @@ def _setUpMocks(self, m):
]:
self.matchers.append(m.get(resource["url"], json=resource))

# mock `fetch_status_types_no_cache`
m.get(
f"{CATALOGI_ROOT}statustypen?zaaktype={self.zaak['zaaktype']}",
json=paginated_response([self.status_type_new, self.status_type_finish]),
)

self.matchers += [
m.get(
f"{ZAKEN_ROOT}zaken?rol__betrokkeneIdentificatie__natuurlijkPersoon__inpBsn={self.user.bsn}&maximaleVertrouwelijkheidaanduiding=beperkt_openbaar",
Expand Down
67 changes: 59 additions & 8 deletions src/open_inwoner/cms/cases/views/status.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dataclasses
from collections import defaultdict
from typing import List
from typing import List, Optional

from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
Expand Down Expand Up @@ -31,7 +31,10 @@
fetch_single_result,
fetch_status_history,
)
from open_inwoner.openzaak.catalog import fetch_single_status_type
from open_inwoner.openzaak.catalog import (
fetch_single_status_type,
fetch_status_types_no_cache,
)
from open_inwoner.openzaak.documents import (
download_document,
fetch_single_information_object_url,
Expand All @@ -43,6 +46,7 @@
StatusTranslation,
ZaakTypeConfig,
ZaakTypeInformatieObjectTypeConfig,
ZaakTypeStatusTypeConfig,
)
from open_inwoner.openzaak.utils import get_role_name_display, is_info_object_visible
from open_inwoner.utils.translate import TranslationLookup
Expand Down Expand Up @@ -116,6 +120,23 @@ def get_context_data(self, **kwargs):

statuses = fetch_status_history(self.case.url)

statustype_config_mapping = {
zaaktype_statustype.statustype_url: zaaktype_statustype
for zaaktype_statustype in ZaakTypeStatusTypeConfig.objects.all()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be refactored in develop?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alextreme Do you mean there is an equivalent for this in Develop already? These values are part of Steven's commits on which I am relying to build the front-end, so I need them :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jiromaykin I mean that the objects.all() call is incorrect/inefficient, but this should have already been fixed on develop. The values should remain the same afaik

}

# NOTE maybe this should be cached?
statustypen = fetch_status_types_no_cache(self.case.zaaktype.url)
end_statustype = next(
(
status_type
for status_type in statustypen
if status_type.is_eindstatus
),
None,
)
end_statustype_data = None

# NOTE we cannot sort on the Status.datum_status_gezet (datetime) because eSuite returns zeros as the time component of the datetime,
# so we're going with the observation that on both OpenZaak and eSuite the returned list is ordered 'oldest-last'
# here we want it 'oldest-first' so we reverse() it instead of sort()-ing
Expand All @@ -134,6 +155,25 @@ def get_context_data(self, **kwargs):
for status in _statuses:
status.statustype = status_type

# The end status data is not passed if the end status has been reached,
# because in that case the end status data is already included in `statuses`
if not status_types.get(end_statustype.url):
end_statustype_data = {
"label": status_translate(
end_statustype.omschrijving, default=_("No data available")
),
"status_indicator": getattr(
statustype_config_mapping.get(end_statustype.url),
"status_indicator",
None,
),
"status_indicator_text": getattr(
statustype_config_mapping.get(end_statustype.url),
"status_indicator_text",
None,
),
}

context["case"] = {
"id": str(self.case.uuid),
"identification": self.case.identification,
Expand All @@ -146,12 +186,10 @@ def get_context_data(self, **kwargs):
self.case, "uiterlijke_einddatum_afdoening", None
),
"description": self.case.zaaktype.omschrijving,
"current_status": status_translate.from_glom(
self.case,
"status.statustype.omschrijving",
default=_("No data available"),
"statuses": self.get_statuses_data(
statuses, status_translate, statustype_config_mapping
),
"statuses": self.get_statuses_data(statuses, status_translate),
"end_statustype_data": end_statustype_data,
"documents": documents,
"allowed_file_extensions": sorted(config.allowed_file_extensions),
}
Expand Down Expand Up @@ -236,14 +274,27 @@ def get_initiator_display(self, case: Zaak) -> str:
return ", ".join([get_role_name_display(r) for r in roles])

def get_statuses_data(
self, statuses: List[Status], lookup: TranslationLookup
self,
statuses: List[Status],
lookup: TranslationLookup,
statustype_config_mapping: Optional[dict] = None,
) -> List[dict]:
return [
{
"date": s.datum_status_gezet,
"label": lookup.from_glom(
s, "statustype.omschrijving", default=_("No data available")
),
"status_indicator": getattr(
statustype_config_mapping.get(s.statustype.url),
"status_indicator",
None,
),
"status_indicator_text": getattr(
statustype_config_mapping.get(s.statustype.url),
"status_indicator_text",
None,
),
}
for s in statuses
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
{% load icon_tags %}
{% if status_indicator %}
<div class="card__header {{ status_indicator }}">
{% if status_indicator == "success" %}
{% icon icon="check_circle" icon_position="after" extra_classes="icon--"|add:status_indicator %} {{status_indicator_text}}
{% elif status_indicator == "failure" %}
{% icon icon="cancel" icon_position="after" extra_classes="icon--"|add:status_indicator %} {{status_indicator_text}}
{% else %}
{% icon icon=status_indicator icon_position="after" extra_classes="icon--"|add:status_indicator %} {{status_indicator_text}}
{% endif %}
</div>
{% if status_indicator_text %}
<div class="card__header {{ status_indicator }}">
{% if status_indicator == "info" %}
{% icon icon="info" icon_position="after" extra_classes="icon--"|add:status_indicator outlined=True %}
<span class="card__status_indicator_text">{{status_indicator_text}}</span>
{% elif status_indicator == "success" %}
{% icon icon="check_circle" icon_position="after" extra_classes="icon--"|add:status_indicator outlined=True %}
jiromaykin marked this conversation as resolved.
Show resolved Hide resolved
<span class="card__status_indicator_text">{{status_indicator_text}}</span>
{% elif status_indicator == "failure" %}
{% icon icon="cancel" icon_position="after" extra_classes="icon--"|add:status_indicator outlined=True %}
<span class="card__status_indicator_text">{{status_indicator_text}}</span>
{% elif status_indicator == "warning" %}
{% icon icon="warning_amber" icon_position="after" extra_classes="icon--"|add:status_indicator outlined=True %}
<span class="card__status_indicator_text">{{status_indicator_text}}</span>
{% else %}
{% icon icon="info" icon_position="after" extra_classes="icon--default" outlined=True %}
<span class="card__status_indicator_text">{{status_indicator_text}}</span>
{% endif %}
</div>
{% else %}
<div class="card__header }}"></div>
{% endif %}
2 changes: 1 addition & 1 deletion src/open_inwoner/components/templatetags/table_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def case_table(case: dict, **kwargs) -> dict:
{% case_table case %}

Variables:
+ case: dict | The case to be able to build the dashboard, fetching the documents and statusses of the case.
+ case: dict | The case to be able to build the dashboard, fetching the documents and statuses of the case.

Extra context:
+ table: TableConfig | The configuration of the table.
Expand Down
25 changes: 25 additions & 0 deletions src/open_inwoner/js/components/cases/case_contact_form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export class DisableCaseContactButton {
static selector = '.case-contact-form'

constructor(form) {
this.form = form
this.contactTextarea = this.form.querySelector('textarea')
this.form.addEventListener('input', this.handleTextareaInput.bind(this))
}

handleTextareaInput() {
const submitButton = this.form.querySelector('button[type="submit"]')

if (this.contactTextarea.value === '') {
submitButton.setAttribute('disabled', 'true')
submitButton.classList.add('button--disabled')
} else {
submitButton.removeAttribute('disabled')
submitButton.classList.remove('button--disabled')
}
}
}

document
.querySelectorAll(DisableCaseContactButton.selector)
.forEach((caseContactForm) => new DisableCaseContactButton(caseContactForm))
1 change: 1 addition & 0 deletions src/open_inwoner/js/components/cases/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
import './document_upload'
import './spinner'
import './status_accordion'
49 changes: 49 additions & 0 deletions src/open_inwoner/js/components/cases/status_accordion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
export class StatusAccordion {
static selector = '.status-list__list-item'

constructor(node) {
this.node = node
this.buttons = node.querySelectorAll('.status-list__button')
this.buttons.forEach((button) => {
button.addEventListener(
'click',
this.toggleStatusAccordion.bind(this, node)
)
})
}

toggleStatusAccordion(node, event) {
event.preventDefault()
const statusContent = node.querySelector(
'.status-list__notification-content'
)
setTimeout(() => {
console.log('status is clicked')

// Toggle any status list-element (current, completed, final, future)
node.classList.toggle(
'status--open',
!node.classList.contains('status--open')
)
// Control toggle of only current and final elements
statusContent.classList.toggle(
'status-content--open',
!statusContent.classList.contains('status-content--open')
)

this.buttons.forEach((button) => {
button.setAttribute(
'aria-expanded',
button.getAttribute('aria-expanded') === 'true' ? 'false' : 'true'
)
})
}, 5)
}
}

/**
* Controls the toggling of expanded case-status content
*/
document
.querySelectorAll(StatusAccordion.selector)
.forEach((statusToggle) => new StatusAccordion(statusToggle))
Loading
Loading