From 744aa40696dd6bcb21f78fb90694c0083abbf666 Mon Sep 17 00:00:00 2001
From: Andreas Ellewsen <andreas@ellewsen.no>
Date: Thu, 21 Jul 2022 14:18:52 +0200
Subject: [PATCH] Show identity info on guest info page

Sponsosr can now click a button next to passport/fnr values on the
guest info page to see the source and verifier of the value.

Resolves: GREG-275
---
 frontend/public/locales/en/common.json        |  4 ++
 frontend/public/locales/nb/common.json        |  4 ++
 frontend/public/locales/nn/common.json        |  4 ++
 .../src/components/identityLine/index.tsx     | 71 ++++++++++++++-----
 frontend/src/interfaces/index.ts              |  8 +++
 frontend/src/utils/index.ts                   |  3 +
 gregui/api/serializers/identity.py            | 16 ++++-
 7 files changed, 92 insertions(+), 18 deletions(-)

diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json
index e298c202..47d8d8e2 100644
--- a/frontend/public/locales/en/common.json
+++ b/frontend/public/locales/en/common.json
@@ -55,6 +55,10 @@
     "roleInfoHead": "Roles and periods",
     "roleInfoTableText": "Guest roles",
     "roleInfoBody": "You can only change roles connected to units that you have given.",
+    "verifier": "Verified by",
+    "verified": "Verified",
+    "source": "Source",
+    "viewInfo": "View info",
     "identityCheck": {
       "failure": "<strong>Warning</strong>: Unable to check if person already exists in IGA.",
       "text": "<strong>Warning</strong>: <1>matches</1> already has this ID registerered to them. Please stop if this is not the same person."
diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json
index b142ab0b..8db7af58 100644
--- a/frontend/public/locales/nb/common.json
+++ b/frontend/public/locales/nb/common.json
@@ -55,6 +55,10 @@
     "roleInfoHead": "Roller og perioder",
     "roleInfoBody": "Du kan endre på tidsperioden, men kun på gjesteroller tilknyttet enheter du er vert for.",
     "roleInfoTableText": "Gjesteroller",
+    "verifier": "Godkjent av",
+    "verified": "Godkjent",
+    "source": "Kilde",
+    "viewInfo": "Se info",
     "identityCheck": {
       "failure": "<strong>Advarsel</strong>: Kunne ikke sjekke IGA om personen allerede er registrert.",
       "text": "<strong>Advarsel</strong>: <1>matches</1> har allerede denne ID-en registrert på seg i IGA. Stop dersom dette ikke er samme person."
diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json
index 28d7a797..12ff6ef2 100644
--- a/frontend/public/locales/nn/common.json
+++ b/frontend/public/locales/nn/common.json
@@ -55,6 +55,10 @@
     "roleInfoTableText": "Gjesteroller",
     "roleInfoHead": "Roller og periodar",
     "roleInfoBody": "Du kan endre på tidsperioden, men berre på gjesteroller knytta til eininger du er vert for.",
+    "verifier": "Godkjend av",
+    "verified": "Godkjend",
+    "source": "Kjelde",
+    "viewInfo": "Se info",
     "identityCheck": {
       "failure": "<strong>Advarsel</strong>: Kunne ikkje sjekke om personen allereie er registrert i andre system.",
       "text": "<strong>Advarsel</strong>: <1>matches</1> er allereie registrert med denne ID-en. Stopp dersom dette ikkje er same person."
diff --git a/frontend/src/components/identityLine/index.tsx b/frontend/src/components/identityLine/index.tsx
index 48f39df3..5c152a03 100644
--- a/frontend/src/components/identityLine/index.tsx
+++ b/frontend/src/components/identityLine/index.tsx
@@ -1,4 +1,5 @@
-import { Box, Button, TableRow, Typography } from '@mui/material'
+import * as React from 'react'
+import { Box, Button, Popper, TableRow } from '@mui/material'
 import ConfirmDialog from 'components/confirmDialog'
 import { Identity } from 'interfaces'
 import { useEffect, useState } from 'react'
@@ -32,6 +33,14 @@ const IdentityLine = ({
     reloadGuest()
     reloadGuests()
   }
+  const [verifiedBoxAnchor, setVerifiedBoxAnchor] =
+    React.useState<null | HTMLElement>(null)
+  const verifiedBoxOpen = Boolean(verifiedBoxAnchor)
+  const verifiedBoxId = verifiedBoxOpen ? 'verified-popper' : undefined
+
+  const handleVerifiedInfo = (event: React.MouseEvent<HTMLElement>) => {
+    setVerifiedBoxAnchor(verifiedBoxAnchor ? null : event.currentTarget)
+  }
 
   const [identityCheckText, setIdentityCheckText] = useState<string | null>(
     null
@@ -100,18 +109,10 @@ const IdentityLine = ({
             justifyContent: 'flex-start',
           }}
         >
-          {/* Setting flex grow to 1 to push the verify button to the right on large screens */}
-          <Box
-            sx={{
-              flexGrow: 1,
-            }}
-          >
+          <Box>
             {identity ? identity.value : ''}
-          </Box>
-
-          {!identity.verified_at ? (
-            <Box>
-              <Typography>
+            {!identity.verified ? (
+              <>
                 <Button
                   aria-label={t('button.verify')}
                   sx={{
@@ -137,11 +138,47 @@ const IdentityLine = ({
                   <IdentityCheckText />
                   {getDialogText()}
                 </ConfirmDialog>
-              </Typography>
-            </Box>
-          ) : (
-            <CheckIcon sx={{ fill: (theme) => theme.palette.success.main }} />
-          )}
+              </>
+            ) : (
+              <>
+                <CheckIcon
+                  sx={{
+                    fill: (theme) => theme.palette.success.main,
+                    verticalAlign: 'text-bottom',
+                    border: 0,
+                  }}
+                  titleAccess={t('guestInfo.verified')}
+                />
+              </>
+            )}
+            <Button
+              onClick={handleVerifiedInfo}
+              sx={{
+                alignSelf: { xs: 'auto', md: 'flex-end' },
+                marginLeft: { xs: '0rem', md: '1rem' },
+                marginTop: { xs: '0.3rem', md: '0rem' },
+              }}
+            >
+              {t('guestInfo.viewInfo')}
+            </Button>
+            <Popper
+              id={verifiedBoxId}
+              open={Boolean(verifiedBoxAnchor)}
+              anchorEl={verifiedBoxAnchor}
+            >
+              <Box sx={{ border: 1, p: 1, bgcolor: 'background.paper' }}>
+                {identity ? `${t('guestInfo.source')}: ${identity.source}` : ''}
+                <br />
+                {identity && identity.verified !== ''
+                  ? `${t('guestInfo.verifier')}: ${
+                      identity.verified === 'manual'
+                        ? identity.verified_by
+                        : identity.source
+                    }`
+                  : ''}
+              </Box>
+            </Popper>
+          </Box>
         </Box>
       </TableCell>
     </TableRow>
diff --git a/frontend/src/interfaces/index.ts b/frontend/src/interfaces/index.ts
index 311b4170..e0fcf7a2 100644
--- a/frontend/src/interfaces/index.ts
+++ b/frontend/src/interfaces/index.ts
@@ -14,10 +14,15 @@ export type Guest = {
   roles: Role[]
 }
 
+type VerifiedChoices = "manual" | "automatic" | ""
+
 export type Identity = {
   id: string
   type: string
   verified_at: Date | null
+  verified_by: string | null
+  verified: VerifiedChoices
+  source: string | null
   value: string
 }
 
@@ -25,6 +30,9 @@ export type FetchedIdentity = {
   id: string
   type: string
   verified_at: string | null
+  verified_by: string | null
+  verified: VerifiedChoices
+  source: string | null
   value: string
 }
 
diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts
index 645504e5..262f09f9 100644
--- a/frontend/src/utils/index.ts
+++ b/frontend/src/utils/index.ts
@@ -176,6 +176,9 @@ export function parseIdentity(
     type: identity.type,
     value: identity.value,
     verified_at: identity.verified_at ? parseISO(identity.verified_at) : null,
+    verified: identity.verified,
+    verified_by: identity.verified_by,
+    source: identity.source,
   }
 }
 
diff --git a/gregui/api/serializers/identity.py b/gregui/api/serializers/identity.py
index 6189d620..85edcf9a 100644
--- a/gregui/api/serializers/identity.py
+++ b/gregui/api/serializers/identity.py
@@ -82,6 +82,20 @@ class IdentitySerializer(serializers.ModelSerializer):
 
 
 class PartialIdentitySerializer(serializers.ModelSerializer):
+    verified_by = serializers.SerializerMethodField()
+
     class Meta:
         model = Identity
-        fields = ["id", "value", "type", "verified_at"]
+        fields = [
+            "id",
+            "value",
+            "type",
+            "source",
+            "verified",
+            "verified_by",
+            "verified_at",
+        ]
+
+    def get_verified_by(self, obj):
+        sponsor = obj.verified_by
+        return " ".join((sponsor.first_name, sponsor.last_name)) if sponsor else None
-- 
GitLab