diff --git a/pyproject.toml b/pyproject.toml
index b382328de..1427edca3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -122,3 +122,4 @@ extension = "html"
profile = "django"
ignore = "T002,H006,H017,H023"
preserve_blank_lines = true
+extend_exclude = "htmlcov,staticfiles,static,node_modules"
diff --git a/src/config/settings/base.py b/src/config/settings/base.py
index 564f26b01..5a42c464c 100644
--- a/src/config/settings/base.py
+++ b/src/config/settings/base.py
@@ -540,8 +540,11 @@
"person-api-people-list",
"person-api-people-detail",
"team-api-teams-list",
+ "profile-get-card",
)
+AUTHBROKER_INTROSPECTION_TOKEN = env("AUTHBROKER_INTROSPECTION_TOKEN", default="XXX")
+
# There are some big pages with lots of content that need to send many fields.
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10240
diff --git a/src/peoplefinder/forms/profile.py b/src/peoplefinder/forms/profile.py
index a4b17791f..7392a8420 100644
--- a/src/peoplefinder/forms/profile.py
+++ b/src/peoplefinder/forms/profile.py
@@ -4,7 +4,6 @@
from django.contrib.auth import get_user_model
from django.core.validators import ValidationError
-
User = get_user_model()
diff --git a/src/peoplefinder/services/person.py b/src/peoplefinder/services/person.py
index 0b81d429a..b0268173c 100644
--- a/src/peoplefinder/services/person.py
+++ b/src/peoplefinder/services/person.py
@@ -2,6 +2,7 @@
from datetime import timedelta
from typing import Any, Dict, List, Optional, Tuple, TypedDict
+import requests
from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.postgres.aggregates import ArrayAgg
@@ -554,6 +555,24 @@ def get_profile_section_values(
)
return values
+ @staticmethod
+ def get_verified_emails(person: Person) -> list[str]:
+ user_email = person.user.email # @TODO prefer UUID if we can get it from SSO
+ url = f"{settings.AUTHBROKER_URL}/api/v1/user/emails/"
+ params = {"email": user_email}
+ headers = {"Authorization": f"bearer {settings.AUTHBROKER_INTROSPECTION_TOKEN}"}
+
+ response = requests.get(url, params, headers=headers, timeout=5)
+
+ if response.status_code == 200:
+ resp_json = response.json()
+ return resp_json["emails"]
+ else:
+ logger.error(
+ f"Response code [{response.status_code}] from authbroker emails endpoint for {user_email}"
+ )
+ return []
+
class PersonAuditLogSerializer(AuditLogSerializer):
model = Person
diff --git a/src/peoplefinder/static/peoplefinder/profile-card.js b/src/peoplefinder/static/peoplefinder/profile-card.js
new file mode 100644
index 000000000..9f0889dd7
--- /dev/null
+++ b/src/peoplefinder/static/peoplefinder/profile-card.js
@@ -0,0 +1,138 @@
+(function () {
+ /**
+ *
+ * @param {String} html
+ * @returns {Node}
+ */
+ function htmlTemplate(html) {
+ const template = document.createElement("template");
+ template.innerHTML = html;
+
+ return template.content.cloneNode(true);
+ }
+
+ /**
+ *
+ * @returns {String}
+ */
+ function getProfileCardUrl() {
+ const metaName = "profile-card-url";
+ const meta = document.querySelector(`meta[name="${metaName}"]`);
+
+ return meta.content;
+ }
+
+ /**
+ *
+ * @returns {Node}
+ */
+ async function getProfileCard() {
+ const url = getProfileCardUrl();
+
+ const response = await fetch(url, {
+ credentials: "include",
+ cache: "default",
+ });
+
+ if (!response.ok) {
+ throw new Error("Error fetching profile card");
+ }
+
+ const html = await response.text();
+
+ return htmlTemplate(html);
+ }
+
+ /**
+ *
+ */
+ class ProfileCard extends HTMLElement {
+ constructor() {
+ super();
+
+ const css = `
+
+
+ `;
+
+ const cssTemplate = htmlTemplate(css);
+
+ this.attachShadow({ mode: "open" });
+ this.shadowRoot.append(cssTemplate);
+ }
+
+ async connectedCallback() {
+ const profileName = this.getAttribute("profile-name");
+
+ if (profileName) {
+ this.find(".profile-name").innerHTML = profileName;
+ }
+
+ let html = null;
+
+ try {
+ html = await getProfileCard();
+ } catch (error) {
+ console.log(error);
+ }
+
+ if (html) {
+ this.shadowRoot.querySelector(".loading").style.display = "none";
+ this.shadowRoot.append(html);
+ }
+ }
+
+ find(selector) {
+ return this.shadowRoot.querySelector(selector);
+ }
+ }
+
+ customElements.define("profile-card", ProfileCard);
+})();
diff --git a/src/peoplefinder/templates/peoplefinder/components/profile-card.html b/src/peoplefinder/templates/peoplefinder/components/profile-card.html
new file mode 100644
index 000000000..f1b6ea9ce
--- /dev/null
+++ b/src/peoplefinder/templates/peoplefinder/components/profile-card.html
@@ -0,0 +1,31 @@
+{% comment %}
+context:
+ profile (Person)
+ profile_url (str)
+ no_photo_url (str)
+{% endcomment %}
+
+
+ {% if profile and profile.photo_small %}
+
+ {% else %}
+
+ {% endif %}
+
+ {% if profile %}
+ - Hi {{ profile.preferred_first_name }}
+ -
+ View your profile
+ {% if profile.profile_completion < 100 %}
+ ({{ profile.profile_completion }}% complete)
+ {% endif %}
+
+ {% else %}
+ - No profile found
+ {% endif %}
+
+
+
+
diff --git a/src/peoplefinder/templates/peoplefinder/profile-edit.html b/src/peoplefinder/templates/peoplefinder/profile-edit.html
index b5a19e4ed..9356e539d 100644
--- a/src/peoplefinder/templates/peoplefinder/profile-edit.html
+++ b/src/peoplefinder/templates/peoplefinder/profile-edit.html
@@ -32,7 +32,6 @@ {{ page_title }}