Skip to content

Commit

Permalink
Merge pull request #851 from maykinmedia/feature/1706-design-personal…
Browse files Browse the repository at this point in the history
…-info

[#1706] Redesign personal info page
  • Loading branch information
alextreme authored Dec 1, 2023
2 parents aeec19a + 99aa32c commit a42b82a
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 173 deletions.
2 changes: 1 addition & 1 deletion src/open_inwoner/accounts/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def __init__(self, user, *args, **kwargs):
self.fields["infix"].required = False
self.fields["last_name"].required = True

if user.is_digid_and_brp():
if user.is_digid_user_with_brp:
self.fields["first_name"].disabled = True
self.fields["infix"].disabled = True
self.fields["last_name"].disabled = True
Expand Down
11 changes: 8 additions & 3 deletions src/open_inwoner/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ def get_active_notifications(self) -> str:
def require_necessary_fields(self) -> bool:
"""returns whether user needs to fill in necessary fields"""
if (
self.is_digid_and_brp()
self.is_digid_user_with_brp
and self.email
and not self.email.endswith("@example.org")
):
Expand Down Expand Up @@ -441,12 +441,17 @@ def get_plan_contact_new_count(self):
def clear_plan_contact_new_count(self):
PlanContact.objects.filter(user=self).update(notify_new=False)

def is_digid_and_brp(self) -> bool:
@property
def is_digid_user(self) -> bool:
return self.login_type == LoginTypeChoices.digid

@property
def is_digid_user_with_brp(self) -> bool:
"""
Returns whether user is logged in with digid and data has
been requested from haal centraal
"""
return self.login_type == LoginTypeChoices.digid and self.is_prepopulated
return self.is_digid_user and self.is_prepopulated


class Document(models.Model):
Expand Down
11 changes: 9 additions & 2 deletions src/open_inwoner/accounts/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import requests_mock
from django_webtest import WebTest
from furl import furl
from pyquery import PyQuery as PQ

from open_inwoner.configurations.models import SiteConfiguration
from open_inwoner.contrib.kvk.models import KvKConfig
Expand Down Expand Up @@ -1565,14 +1566,20 @@ def test_password_change_form_done_custom_template_is_rendered(self):

def test_password_change_button_is_rendered_with_default_login_type(self):
response = self.app.get(reverse("profile:detail"), user=self.user)
self.assertContains(response, _("Wijzig wachtwoord"))

doc = PQ(response.content)
link = doc.find("[aria-label='Wachtwoord']")[0]
self.assertTrue(doc(link).is_("a"))

def test_password_change_button_is_not_rendered_with_digid_login_type(self):
digid_user = UserFactory(
login_type=LoginTypeChoices.digid, email="[email protected]"
)
response = self.app.get(reverse("profile:detail"), user=digid_user)
self.assertNotContains(response, _("Wijzig wachtwoord"))

doc = PQ(response.content)
links = doc.find("[aria-label='Wachtwoord']")
self.assertEqual(len(links), 0)

def test_anonymous_user_is_redirected_to_login_page_if_password_change_is_accessed(
self,
Expand Down
36 changes: 24 additions & 12 deletions src/open_inwoner/accounts/tests/test_profile_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,25 +60,28 @@ def test_login_required(self):
def test_user_information_profile_page(self):
response = self.app.get(self.url, user=self.user)

self.assertContains(response, self.user.get_full_name())
self.assertContains(response, self.user.first_name)
self.assertContains(response, self.user.last_name)
self.assertContains(response, self.user.infix)
self.assertContains(response, self.user.email)
self.assertContains(response, self.user.phonenumber)
self.assertContains(response, self.user.get_address())
self.assertContains(response, self.user.street)
self.assertContains(response, self.user.housenumber)
self.assertContains(response, self.user.city)

def test_get_empty_profile_page(self):
response = self.app.get(self.url, user=self.user)

self.assertEquals(response.status_code, 200)
self.assertContains(response, _("U heeft nog geen contacten."))
self.assertContains(response, "0 acties staan open.")
self.assertContains(response, _("U heeft nog geen contacten"))
self.assertContains(response, "0 acties staan open")
self.assertNotContains(response, reverse("products:questionnaire_list"))
self.assertContains(response, _("messages, plans"))

def test_get_filled_profile_page(self):
ActionFactory(created_by=self.user)
contact = UserFactory()
self.user.user_contacts.add(contact)
category = CategoryFactory()
CategoryFactory()
QuestionnaireStepFactory(published=True)

response = self.app.get(self.url, user=self.user)
Expand All @@ -87,14 +90,13 @@ def test_get_filled_profile_page(self):
response,
f"{contact.first_name} ({contact.get_contact_type_display()})",
)
self.assertContains(response, "1 acties staan open.")
self.assertContains(response, "1 acties staan open")
self.assertContains(response, reverse("products:questionnaire_list"))

def test_only_open_actions(self):
action = ActionFactory(created_by=self.user, status=StatusChoices.closed)
ActionFactory(created_by=self.user, status=StatusChoices.closed)
response = self.app.get(self.url, user=self.user)
self.assertEquals(response.status_code, 200)
self.assertContains(response, "0 acties staan open.")
self.assertIn("0 acties staan open", response)

def test_mydata_shown_with_digid_and_brp(self):
user = UserFactory(
Expand Down Expand Up @@ -123,8 +125,18 @@ def test_mydata_not_shown_without_digid(self):
self.assertNotContains(response, _("My details"))

def test_active_user_notifications_are_shown(self):
response = self.app.get(self.url, user=self.user)
self.assertContains(response, _("messages, plans"))
user = UserFactory(
bsn="999993847",
first_name="name",
last_name="surname",
is_prepopulated=False,
login_type=LoginTypeChoices.digid,
messages_notifications=True,
plans_notifications=True,
cases_notifications=False,
)
response = self.app.get(self.url, user=user)
self.assertContains(response, _("Mijn Berichten, Samenwerken"))

def test_expected_message_is_shown_when_all_notifications_disabled(self):
self.user.cases_notifications = False
Expand Down
78 changes: 51 additions & 27 deletions src/open_inwoner/accounts/views/profile.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import date, datetime
from typing import Generator, Union

from django.conf import settings
from django.contrib import messages
Expand Down Expand Up @@ -48,51 +49,74 @@ class MyProfileView(
def crumbs(self):
return [(_("Mijn profiel"), reverse("profile:detail"))]

@staticmethod
def stringify(
items: list, string_func: callable, lump: bool = False
) -> Union[Generator, str]:
"""
Create string representation(s) of `items` for display
:param string_func: the function used to stringify elements in `items`
:param lump: if `True`, `string_func` is applied to `items` collectively
:returns: a `Generator` of strings representing elements in `items`, or a
`str` representing `items` as a whole, depending on whether `lump` is
`True`
"""
if lump:
return string_func(items)
return (string_func(item) for item in items)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = self.request.user
today = date.today()

context["anchors"] = [
("#title", _("Persoonlijke gegevens")),
("#overview", _("Persoonlijk overzicht")),
("#personal-info", _("Persoonlijke gegevens")),
("#notifications", _("Voorkeuren voor meldingen")),
("#overview", _("Overzicht")),
]

user_files = user.get_all_files()

# List of names of 'mentor' users that are a contact of me
mentor_contacts = [
c.get_full_name()
for c in user.user_contacts.filter(
contact_type=ContactTypeChoices.begeleider
)
]

# Mentor contacts + names for display
mentor_contacts = user.user_contacts.filter(
contact_type=ContactTypeChoices.begeleider
)
context["mentor_contacts"] = mentor_contacts
context["mentor_contact_names"] = self.stringify(
mentor_contacts,
string_func=lambda m: m.get_full_name,
)

# Regular contacts + names for display
contacts = user.get_active_contacts()
context["contact_names"] = self.stringify(
contacts,
string_func=lambda c: f"{c.first_name} ({c.get_contact_type_display()})",
)

# Actions
actions = (
Action.objects.visible()
.connected(self.request.user)
.filter(status=StatusChoices.open)
)
context["action_text"] = self.stringify(
actions,
string_func=lambda actions: f"{actions.count()} acties staan open",
lump=True,
)

context["next_action"] = (
Action.objects.visible()
.connected(self.request.user)
.filter(end_date__gte=today, status=StatusChoices.open)
.order_by("end_date")
.first()
)
context["files"] = user_files
context["action_text"] = _(
f"{Action.objects.visible().connected(self.request.user).filter(status=StatusChoices.open).count()} acties staan open."
)
contacts = user.get_active_contacts()
# Invited contacts
contact_names = [
f"{contact.first_name} ({contact.get_contact_type_display()})"
for contact in contacts[:3]
]

if contacts.count() > 0:
context[
"contact_text"
] = f"{', '.join(contact_names)}{'...' if contacts.count() > 3 else ''}"
else:
context["contact_text"] = _("U heeft nog geen contacten.")
context["files"] = user_files

context["questionnaire_exists"] = QuestionnaireStep.objects.filter(
published=True
Expand Down Expand Up @@ -186,7 +210,7 @@ def update_klant_api(self, user_form_data: dict):

def get_form_class(self):
user = self.request.user
if user.is_digid_and_brp():
if user.is_digid_user_with_brp:
return BrpUserForm
return super().get_form_class()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

{% endif %}

<!-- dynamic anchor item for products -->
{# dynamic anchor item for products #}
<div class="anchor-menu__start-inquiry">
{% if not product.has_cta_tag %}
{% if product.form %}
Expand Down
8 changes: 4 additions & 4 deletions src/open_inwoner/configurations/tests/test_show_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ def test_default_enabled(self):
def test_when_enabled_and_user_is_logged_in(self):
response = self.app.get(self.profile_url, user=self.user)

links = response.pyquery(".personal-overview")
self.assertNotEqual(links.find(".personal-overview__actions"), [])
links = response.pyquery(".profile-section")
self.assertNotEqual(links.find("#profile-section-actions"), [])
self.assertNotEqual(links.find(f'a[href="{self.actions_list_url}"]'), [])

def test_when_disabled_and_user_is_logged_in(self):
self.profile_config.actions = False
self.profile_config.save()
response = self.app.get(self.profile_url, user=self.user)

links = response.pyquery(".personal-overview")
self.assertEqual(links.find(".personal-overview__actions"), [])
links = response.pyquery(".profile-section")
self.assertEqual(links.find("#profile-section-actions"), [])
self.assertEqual(links.find(f'a[href="{self.actions_list_url}"]'), [])

def test_action_pages_show_404_when_disabled(self):
Expand Down
2 changes: 1 addition & 1 deletion src/open_inwoner/scss/components/List/_List.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
overflow-y: auto;

.case-list,
.contactmomenten-list {
.contactmomenten-list .profile-list {
text-decoration: none;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.personal-overview {
.profile-section {
h2,
.h2 {
margin: var(--row-height) 0 0 0;
margin: calc(var(--row-height) * 1.5) 0 0 0;
}
}
52 changes: 48 additions & 4 deletions src/open_inwoner/scss/components/Table/Tabled.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
display: flex;
flex-direction: column;

.h2 {
&.title--extra-padding {
padding-bottom: var(--spacing-large);
}
}

&__key {
color: var(--color-gray-90);
}
&__value {
color: var(--color-gray-dark);
}

&__title {
padding-bottom: var(--spacing-extra-large);
}

&.tabled--tiny-end {
.tabled__header,
.tabled__row {
Expand All @@ -24,6 +41,24 @@
margin: var(--row-height) 0 0 0;
}

.card {
.h4 .link {
color: var(--color-black);
}

.profile__link {
text-decoration: none;
}

.list-item {
p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}

.tabled__header {
display: grid;
grid-template-columns: 1fr 1fr;
Expand All @@ -41,11 +76,23 @@
}
}

.tabled__section {
padding-bottom: var(--spacing-extra-large);

.title {
border-bottom: 1px solid var(--color-gray-light);
}
}

.tabled__row {
display: grid;
grid-template-columns: 1fr 1fr;
border-bottom: 1px solid var(--color-gray-light);

&--blank {
border-bottom-width: 0;
}

@media (min-width: 768px) {
grid-template-columns: 1fr 1fr 125px 125px;
}
Expand All @@ -54,10 +101,7 @@
.tabled__item {
font-family: var(--font-family-body);
padding: var(--spacing-medium);

&--bold {
font-weight: bold;
}
padding-left: 0;

&--right {
@media (min-width: 768px) {
Expand Down
Loading

0 comments on commit a42b82a

Please sign in to comment.