From 920f01ea7528ed5b98e0f271d5bdb4f67179f342 Mon Sep 17 00:00:00 2001
From: "Tore.Brede" <tore.brede@uib.no>
Date: Fri, 29 Jul 2022 09:24:20 +0200
Subject: [PATCH] GREG-255: Doing some changes on the edit role screen

---
 frontend/src/interfaces/index.ts              |  2 +
 .../guest/guestRoleInfo/index.test.tsx        | 58 ++++++++++++++++---
 .../sponsor/guest/guestRoleInfo/index.tsx     | 32 +++++++---
 frontend/src/utils/index.ts                   |  1 +
 gregui/api/serializers/role.py                |  5 ++
 gregui/tests/api/serializers/test_guest.py    |  1 +
 6 files changed, 84 insertions(+), 15 deletions(-)

diff --git a/frontend/src/interfaces/index.ts b/frontend/src/interfaces/index.ts
index 4e5766da..311b4170 100644
--- a/frontend/src/interfaces/index.ts
+++ b/frontend/src/interfaces/index.ts
@@ -56,6 +56,7 @@ export type Role = {
   contact_person_unit: string | null
   comments: string | null
   sponsor_name: string
+  ou_id: number
 }
 
 export type FetchedRole = {
@@ -70,6 +71,7 @@ export type FetchedRole = {
   contact_person_unit: string | null
   comments: string | null
   sponsor_name: string
+  ou_id: number
 }
 
 export type ConsentType = {
diff --git a/frontend/src/routes/sponsor/guest/guestRoleInfo/index.test.tsx b/frontend/src/routes/sponsor/guest/guestRoleInfo/index.test.tsx
index 5556a8e7..4ef2255c 100644
--- a/frontend/src/routes/sponsor/guest/guestRoleInfo/index.test.tsx
+++ b/frontend/src/routes/sponsor/guest/guestRoleInfo/index.test.tsx
@@ -5,7 +5,8 @@ import { LocalizationProvider } from '@mui/lab'
 // eslint-disable-next-line import/no-extraneous-dependencies
 import { Guest } from 'interfaces'
 import parse from 'date-fns/parse'
-import { BrowserRouter } from 'react-router-dom'
+import { MemoryRouter, Route, Routes } from 'react-router-dom'
+import { addDays } from 'date-fns/fp'
 import GuestRoleInfo from './index'
 import { waitFor } from '../../../../test-utils'
 
@@ -30,11 +31,12 @@ const guest: Guest = {
       name_en: 'Guest role',
       name_nb: 'Gjesterolle',
       start_date: parse('2021-08-10', 'yyyy-MM-dd', new Date()),
-      end_date: parse('2021-08-16', 'yyyy-MM-dd', new Date()),
+      end_date: addDays(10)(new Date()),
       contact_person_unit: 'Test contact person',
       max_days: 100,
       comments: 'Test comment',
       sponsor_name: 'Test',
+      ou_id: 1,
     },
     {
       id: '201',
@@ -43,22 +45,41 @@ const guest: Guest = {
       name_en: 'Test role',
       name_nb: 'Testrolle',
       start_date: parse('2021-09-06', 'yyyy-MM-dd', new Date()),
-      end_date: parse('2021-09-20', 'yyyy-MM-dd', new Date()),
+      end_date: addDays(10)(new Date()),
       contact_person_unit: 'Test contact person',
       max_days: 100,
       comments: 'Test comment',
       sponsor_name: 'Test',
+      ou_id: 2,
     },
   ],
 }
 
-test('Button state correct on load', async () => {
+// Mock useOus so that it looks like the user is a sponsor for
+// the unit with ID 1
+jest.mock('hooks/useOus', () => () => ({
+  ous: [
+    {
+      id: 1,
+      nb: 'Test',
+      en: 'Test2',
+    },
+  ],
+  loading: false,
+}))
+
+test('End now enabled for role that is not expired and in unit where user is host', async () => {
   render(
-    <BrowserRouter>
+    <MemoryRouter initialEntries={['/sponsor/guest/1/roles/200']}>
       <LocalizationProvider dateAdapter={AdapterDateFns}>
-        <GuestRoleInfo guest={guest} reloadGuest={() => {}} />
+        <Routes>
+          <Route
+            path="sponsor/guest/:pid/roles/:id"
+            element={<GuestRoleInfo guest={guest} reloadGuest={() => {}} />}
+          />
+        </Routes>
       </LocalizationProvider>
-    </BrowserRouter>
+    </MemoryRouter>
   )
 
   await waitFor(
@@ -69,3 +90,26 @@ test('Button state correct on load', async () => {
     { timeout: 5000 }
   )
 }, 10000)
+
+test('End now disabled for role that is not expired and in unit where user is not host', async () => {
+  render(
+    <MemoryRouter initialEntries={['/sponsor/guest/1/roles/201']}>
+      <LocalizationProvider dateAdapter={AdapterDateFns}>
+        <Routes>
+          <Route
+            path="sponsor/guest/:pid/roles/:id"
+            element={<GuestRoleInfo guest={guest} reloadGuest={() => {}} />}
+          />
+        </Routes>
+      </LocalizationProvider>
+    </MemoryRouter>
+  )
+
+  await waitFor(
+    () => {
+      expect(screen.getByText('button.save')).toBeDisabled()
+      expect(screen.getByText('sponsor.endNow')).toBeDisabled()
+    },
+    { timeout: 5000 }
+  )
+}, 10000)
diff --git a/frontend/src/routes/sponsor/guest/guestRoleInfo/index.tsx b/frontend/src/routes/sponsor/guest/guestRoleInfo/index.tsx
index c8293c72..9bcba27e 100644
--- a/frontend/src/routes/sponsor/guest/guestRoleInfo/index.tsx
+++ b/frontend/src/routes/sponsor/guest/guestRoleInfo/index.tsx
@@ -18,6 +18,7 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form'
 import { getRoleName, getRoleOuName, submitJsonOpts } from 'utils'
 import { useFeatureContext } from 'contexts/featureContext'
 import ConfirmDialog from '../../../../components/confirmDialog'
+import useOus from '../../../../hooks/useOus'
 
 interface GuestRoleInfoProps {
   guest: Guest
@@ -101,6 +102,7 @@ export default function GuestRoleInfo({
   reloadGuest,
 }: GuestRoleInfoProps) {
   const { pid, id } = useParams<GuestRoleInfoParams>()
+  const { ous } = useOus()
   const [t] = useTranslation('common')
   const { displayContactAtUnit, displayComment } = useFeatureContext()
   const navigate = useNavigate()
@@ -116,6 +118,7 @@ export default function GuestRoleInfo({
     contact_person_unit: null,
     comments: null,
     sponsor_name: '',
+    ou_id: -1,
   })
   // Prepare min and max date values
   const today = new Date()
@@ -141,6 +144,13 @@ export default function GuestRoleInfo({
     formState: { isDirty, isValid },
   } = useForm<RoleFormData>({ mode: 'onChange' })
 
+  // A sponsor can only edit roles belonging to departments where he is a sponsor.
+  // Look at the unit where the role is registered and see if that unit is present
+  // in the list of units where a sponsor can assign roles to determine if
+  // he can edit this role
+  const allowEdit =
+    ous === undefined ? false : ous.some((value) => value.id === role.ou_id)
+
   // Submit function for the save button
   const submit: SubmitHandler<RoleFormData> = (data) => {
     const payload: { start_date?: string; end_date: string } = {
@@ -198,12 +208,16 @@ export default function GuestRoleInfo({
         to={`/sponsor/guest/${pid}`}
         name={`${guest.first} ${guest.last}`}
       />
-      <Typography sx={{ marginBottom: '1rem' }} variant="h2">
-        {t('sponsor.roleInfoHead')}
-      </Typography>
-      <Typography sx={{ marginBottom: '1rem' }} variant="body1">
-        {t('sponsor.roleInfoText')}
-      </Typography>
+      {allowEdit && (
+        <>
+          <Typography sx={{ marginBottom: '1rem' }} variant="h2">
+            {t('sponsor.roleInfoHead')}
+          </Typography>
+          <Typography sx={{ marginBottom: '1rem' }} variant="body1">
+            {t('sponsor.roleInfoText')}
+          </Typography>
+        </>
+      )}
       <form onSubmit={onSubmit}>
         <TableContainer>
           <Table sx={{ minWidth: 650 }} aria-label="simple table">
@@ -239,6 +253,7 @@ export default function GuestRoleInfo({
                         inputFormat="yyyy-MM-dd"
                         onChange={onChange}
                         renderInput={(params) => <TextField {...params} />}
+                        disabled={!allowEdit}
                       />
                     )}
                   />
@@ -257,6 +272,7 @@ export default function GuestRoleInfo({
                         inputFormat="yyyy-MM-dd"
                         onChange={onChange}
                         renderInput={(params) => <TextField {...params} />}
+                        disabled={!allowEdit}
                       />
                     )}
                   />
@@ -301,7 +317,7 @@ export default function GuestRoleInfo({
           variant="contained"
           color="success"
           type="submit"
-          disabled={!isDirty || !isValid}
+          disabled={!isDirty || !isValid || !allowEdit}
         >
           {t('button.save')}
         </Button>{' '}
@@ -309,7 +325,7 @@ export default function GuestRoleInfo({
         <Button
           aria-label={t('sponsor.endNow')}
           color="primary"
-          disabled={role.end_date < today}
+          disabled={role.end_date < today || !allowEdit}
           onClick={() => setShowEndRoleConfirmationDialog(true)}
         >
           {t('sponsor.endNow')}
diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts
index dd3d3fe3..645504e5 100644
--- a/frontend/src/utils/index.ts
+++ b/frontend/src/utils/index.ts
@@ -150,6 +150,7 @@ export function parseRole(role: FetchedRole): Role {
     contact_person_unit: role.contact_person_unit,
     comments: role.comments,
     sponsor_name: role.sponsor_name,
+    ou_id: role.ou_id
   }
 }
 
diff --git a/gregui/api/serializers/role.py b/gregui/api/serializers/role.py
index 4353e4b5..c80bad5e 100644
--- a/gregui/api/serializers/role.py
+++ b/gregui/api/serializers/role.py
@@ -104,6 +104,7 @@ class ExtendedRoleSerializer(serializers.ModelSerializer):
     ou_en = SerializerMethodField(source="orgunit")
     max_days = SerializerMethodField(source="type")
     sponsor_name = SerializerMethodField(source="sponsor")
+    ou_id = SerializerMethodField(source="orgunit")
 
     def get_name_nb(self, obj):
         return obj.type.name_nb
@@ -123,6 +124,9 @@ class ExtendedRoleSerializer(serializers.ModelSerializer):
     def get_sponsor_name(self, obj):
         return f"{obj.sponsor.first_name} {obj.sponsor.last_name}"
 
+    def get_ou_id(self, obj):
+        return obj.orgunit.id
+
     class Meta:
         model = Role
         fields = [
@@ -137,5 +141,6 @@ class ExtendedRoleSerializer(serializers.ModelSerializer):
             "contact_person_unit",
             "comments",
             "sponsor_name",
+            "ou_id",
         ]
         read_only_fields = ["contact_person_unit", "sponsor_name"]
diff --git a/gregui/tests/api/serializers/test_guest.py b/gregui/tests/api/serializers/test_guest.py
index ed473128..6798cc40 100644
--- a/gregui/tests/api/serializers/test_guest.py
+++ b/gregui/tests/api/serializers/test_guest.py
@@ -35,6 +35,7 @@ def test_serialize_guest(invited_person):
                 "contact_person_unit": "",
                 "comments": "",
                 "sponsor_name": "Sponsor Bar",
+                "ou_id": 1,
             },
         ],
         "verified": False,
-- 
GitLab