Repository: island-is/island.is
Pull request #18712: feat(j-s): Local User Admin
Author: login: gudjong
Labels: name: dirty bypass
description: Don't run dirty checks like 'newlint' and 'auto-format'
Created: 2025-04-09T08:34:47Z | Merged: 2025-04-14T13:19:57Z

# Local User Admin

[Bæta við local admin hlutverki í user](https://app.asana.com/1/203394141643832/project/1199153462262248/task/1209918369657537)

## What

- Bætir við nýju local admin hlutverki sem hægt er að búa til í notendaumsjón.
- Breytir unique skilyrði á user töflu þannig að hægt er að búa til marga notendur á sömu kennitölu í sama embætti, þó þannig að notendur þurfa að hafa mismunandi hlutverk.
- Local admin notendur geta ekkert gert eins og er.
- Í leiðinni er enum fyrir user role eytt og role geymd sem strengir í gagnagrunni. Þetta er liður í að hætta að nota enums í grunninum þar sem þau eru ekki auðveld í viðhaldi.

## Why

- Við þurfum að geta skipt upp notendaþjónustunni og þetta er fyrsta skrefið í því.

## Screenshots / Gifs

<img width="918" alt="image" src="https://github.com/user-attachments/assets/3f112aee-c088-4221-b843-1c19641452a7" />

## Checklist:

- [x] I have performed a self-review of my own code
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [x] Formatting passes locally with my changes
- [x] I have rebased against main before asking for a review


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced a new "Local Admin" role for enhanced administrative control.

- **Refactor**
  - Streamlined user management forms to focus on essential details.
  - Updated role selection displays with consistent, localized labels for clearer identification.
  - Enhanced user validation logic to incorporate the new "Local Admin" role.

- **Style**
  - Improved layout and spacing within administrative interfaces for a cleaner, more organized appearance.

- **Bug Fixes**
  - Adjusted redirect logic in the Landing component to ensure proper user eligibility checks. 

- **Documentation**
  - Updated GraphQL mutation to remove unnecessary fields from responses.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/apps/judicial-system/backend/migrations/20250407144915-update-user.js b/apps/judicial-system/backend/migrations/20250407144915-update-user.js
new file mode 100644
index 000000000000..5087864b6893
--- /dev/null
+++ b/apps/judicial-system/backend/migrations/20250407144915-update-user.js
@@ -0,0 +1,73 @@
+'use strict'
+
+module.exports = {
+  async up(queryInterface, Sequelize) {
+    return queryInterface.sequelize
+      .transaction((transaction) =>
+        queryInterface
+          .changeColumn(
+            'user',
+            'role',
+            { type: Sequelize.STRING, allowNull: false },
+            { transaction },
+          )
+          .then(() =>
+            queryInterface.sequelize.query('DROP TYPE "enum_user_role"', {
+              transaction,
+            }),
+          ),
+      )
+      .then(() =>
+        Promise.all([
+          queryInterface.removeConstraint(
+            'user',
+            'unique_national_id_per_institution',
+          ),
+          queryInterface.addConstraint('user', {
+            fields: ['national_id', 'role', 'institution_id'],
+            type: 'unique',
+            name: 'unique_national_id_and_role_per_institution',
+          }),
+        ]),
+      )
+  },
+
+  async down(queryInterface, Sequelize) {
+    return queryInterface.sequelize
+      .transaction((transaction) =>
+        queryInterface.changeColumn(
+          'user',
+          'role',
+          {
+            type: Sequelize.ENUM(
+              'PROSECUTOR',
+              'PROSECUTOR_REPRESENTATIVE',
+              'PUBLIC_PROSECUTOR_STAFF',
+              'DISTRICT_COURT_JUDGE',
+              'DISTRICT_COURT_REGISTRAR',
+              'DISTRICT_COURT_ASSISTANT',
+              'COURT_OF_APPEALS_JUDGE',
+              'COURT_OF_APPEALS_REGISTRAR',
+              'COURT_OF_APPEALS_ASSISTANT',
+              'PRISON_SYSTEM_STAFF',
+            ),
+            allowNull: false,
+          },
+          { transaction },
+        ),
+      )
+      .then(() =>
+        Promise.all([
+          queryInterface.removeConstraint(
+            'user',
+            'unique_national_id_and_role_per_institution',
+          ),
+          queryInterface.addConstraint('user', {
+            fields: ['national_id', 'institution_id'],
+            type: 'unique',
+            name: 'unique_national_id_per_institution',
+          }),
+        ]),
+      )
+  },
+}
diff --git a/apps/judicial-system/backend/src/app/modules/user/user.model.ts b/apps/judicial-system/backend/src/app/modules/user/user.model.ts
index 3ef4f917c516..ec021a6f139c 100644
--- a/apps/judicial-system/backend/src/app/modules/user/user.model.ts
+++ b/apps/judicial-system/backend/src/app/modules/user/user.model.ts
@@ -60,7 +60,9 @@ export class User extends Model {
   @Column({
     type: DataType.ENUM,
     allowNull: false,
-    values: Object.values(UserRole),
+    values: Object.values(UserRole).filter(
+      (role) => ![UserRole.ADMIN, UserRole.DEFENDER].includes(role),
+    ),
   })
   @ApiProperty({ enum: UserRole })
   role!: UserRole
diff --git a/apps/judicial-system/web/src/routes/Admin/ChangeUser/updateUser.graphql b/apps/judicial-system/web/src/routes/Admin/ChangeUser/updateUser.graphql
index b2020419fa1e..0b4c37e7a66a 100644
--- a/apps/judicial-system/web/src/routes/Admin/ChangeUser/updateUser.graphql
+++ b/apps/judicial-system/web/src/routes/Admin/ChangeUser/updateUser.graphql
@@ -1,6 +1,5 @@
 mutation UpdateUser($input: UpdateUserInput!) {
   updateUser(input: $input) {
     id
-    modified
   }
 }
diff --git a/apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx b/apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx
index 09b4d419c07f..603e5c55e6e4 100644
--- a/apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx
+++ b/apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx
@@ -11,7 +11,6 @@ import {
 import {
   CreateUserInput,
   User,
-  UserRole,
 } from '@island.is/judicial-system-web/src/graphql/schema'
 import { useInstitution } from '@island.is/judicial-system-web/src/utils/hooks'
 
@@ -22,16 +21,7 @@ import * as styles from '../Users/Users.css'
 
 const user: User = {
   id: '',
-  created: '',
-  modified: '',
-  nationalId: '',
-  name: '',
-  title: '',
-  mobileNumber: '',
-  email: '',
-  role: UserRole.PROSECUTOR,
-  institution: undefined,
-  active: true,
+  active: false,
   canConfirmIndictment: false,
 }
 
diff --git a/apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.css.ts b/apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.css.ts
index 999826fbfa1d..a1d3759325b2 100644
--- a/apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.css.ts
+++ b/apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.css.ts
@@ -2,14 +2,14 @@ import { style } from '@vanilla-extract/css'
 
 import { theme } from '@island.is/island-ui/theme'
 
+export const roleRow = style({
+  display: 'flex',
+  gap: theme.spacing[2],
+  marginBottom: theme.spacing[2],
+})
+
 export const roleColumn = style({
   flex: 1,
-
-  selectors: {
-    '&:nth-child(2)': {
-      margin: `0 ${theme.spacing[2]}px`,
-    },
-  },
 })
 
 export const userFormContainer = style({
diff --git a/apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx b/apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx
index 2eeb200fc7fa..6cf1360ecedb 100644
--- a/apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx
+++ b/apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx
@@ -29,6 +29,7 @@ import {
   validate,
   Validation,
 } from '../../../utils/validate'
+import { userRoleToString } from '../userRoleToString'
 import * as styles from './UserForm.css'
 
 type ExtendedOption = ReactSelectOption & { institution: Institution }
@@ -94,9 +95,7 @@ export const UserForm: FC<Props> = ({
     (institution) => institution.value === user.institution?.id,
   )
 
-  const isValid = () => {
-    return isAdminUserFormValid(user) && isCoreUser(user)
-  }
+  const isValid = isAdminUserFormValid(user) && isCoreUser(user)
 
   const storeAndRemoveErrorIfValid = (
     field: string,
@@ -218,139 +217,232 @@ export const UserForm: FC<Props> = ({
           />
         </Box>
         {user.institution?.type === InstitutionType.PROSECUTORS_OFFICE ? (
-          <Box display="flex" marginBottom={2}>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleProsecutor"
-                label="Saksóknari"
-                checked={user.role === UserRole.PROSECUTOR}
-                onChange={() => setUser({ ...user, role: UserRole.PROSECUTOR })}
-                large
-              />
+          <>
+            <Box className={styles.roleRow}>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleProsecutor"
+                  label={userRoleToString(UserRole.PROSECUTOR)}
+                  checked={user.role === UserRole.PROSECUTOR}
+                  onChange={() =>
+                    setUser({ ...user, role: UserRole.PROSECUTOR })
+                  }
+                  large
+                />
+              </Box>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleProsecutorRepresentative"
+                  label={userRoleToString(UserRole.PROSECUTOR_REPRESENTATIVE)}
+                  checked={user.role === UserRole.PROSECUTOR_REPRESENTATIVE}
+                  onChange={() =>
+                    setUser({
+                      ...user,
+                      role: UserRole.PROSECUTOR_REPRESENTATIVE,
+                    })
+                  }
+                  large
+                />
+              </Box>
             </Box>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleProsecutorRepresentative"
-                label="Fulltrúi"
-                checked={user.role === UserRole.PROSECUTOR_REPRESENTATIVE}
-                onChange={() =>
-                  setUser({ ...user, role: UserRole.PROSECUTOR_REPRESENTATIVE })
-                }
-                large
-              />
+            <Box className={styles.roleRow}>
+              {user.institution.id ===
+              '8f9e2f6d-6a00-4a5e-b39b-95fd110d762e' ? (
+                <>
+                  <Box className={styles.roleColumn}>
+                    <RadioButton
+                      name="role"
+                      id="rolePublicProsecutorStaff"
+                      label={userRoleToString(UserRole.PUBLIC_PROSECUTOR_STAFF)}
+                      checked={user.role === UserRole.PUBLIC_PROSECUTOR_STAFF}
+                      onChange={() =>
+                        setUser({
+                          ...user,
+                          role: UserRole.PUBLIC_PROSECUTOR_STAFF,
+                        })
+                      }
+                      large
+                    />
+                  </Box>
+                  <Box className={styles.roleColumn}>
+                    <RadioButton
+                      name="role"
+                      id="roleLocalAdmin"
+                      label={userRoleToString(UserRole.LOCAL_ADMIN)}
+                      checked={user.role === UserRole.LOCAL_ADMIN}
+                      onChange={() =>
+                        setUser({ ...user, role: UserRole.LOCAL_ADMIN })
+                      }
+                      large
+                    />
+                  </Box>
+                </>
+              ) : (
+                <>
+                  <Box className={styles.roleColumn}>
+                    <RadioButton
+                      name="role"
+                      id="roleLocalAdmin"
+                      label={userRoleToString(UserRole.LOCAL_ADMIN)}
+                      checked={user.role === UserRole.LOCAL_ADMIN}
+                      onChange={() =>
+                        setUser({ ...user, role: UserRole.LOCAL_ADMIN })
+                      }
+                      large
+                    />
+                  </Box>
+                  <Box className={styles.roleColumn} />
+                </>
+              )}
             </Box>
-            {user.institution.id === '8f9e2f6d-6a00-4a5e-b39b-95fd110d762e' && (
+          </>
+        ) : user.institution?.type === InstitutionType.DISTRICT_COURT ? (
+          <>
+            <Box className={styles.roleRow}>
               <Box className={styles.roleColumn}>
                 <RadioButton
                   name="role"
-                  id="rolePublicProsecutorStaff"
-                  label="Skrifstofa"
-                  checked={user.role === UserRole.PUBLIC_PROSECUTOR_STAFF}
+                  id="roleJudge"
+                  label={userRoleToString(UserRole.DISTRICT_COURT_JUDGE)}
+                  checked={user.role === UserRole.DISTRICT_COURT_JUDGE}
                   onChange={() =>
-                    setUser({ ...user, role: UserRole.PUBLIC_PROSECUTOR_STAFF })
+                    setUser({ ...user, role: UserRole.DISTRICT_COURT_JUDGE })
+                  }
+                  large
+                />
+              </Box>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleRegistrar"
+                  label={userRoleToString(UserRole.DISTRICT_COURT_REGISTRAR)}
+                  checked={user.role === UserRole.DISTRICT_COURT_REGISTRAR}
+                  onChange={() =>
+                    setUser({
+                      ...user,
+                      role: UserRole.DISTRICT_COURT_REGISTRAR,
+                    })
                   }
                   large
                 />
               </Box>
-            )}
-          </Box>
-        ) : user.institution?.type === InstitutionType.DISTRICT_COURT ? (
-          <Box display="flex" marginBottom={2}>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleJudge"
-                label="Dómari"
-                checked={user.role === UserRole.DISTRICT_COURT_JUDGE}
-                onChange={() =>
-                  setUser({ ...user, role: UserRole.DISTRICT_COURT_JUDGE })
-                }
-                large
-              />
-            </Box>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleRegistrar"
-                label="Dómritari"
-                checked={user.role === UserRole.DISTRICT_COURT_REGISTRAR}
-                onChange={() =>
-                  setUser({ ...user, role: UserRole.DISTRICT_COURT_REGISTRAR })
-                }
-                large
-              />
             </Box>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleAssistant"
-                label="Aðstoðarmaður dómara"
-                checked={user.role === UserRole.DISTRICT_COURT_ASSISTANT}
-                onChange={() =>
-                  setUser({ ...user, role: UserRole.DISTRICT_COURT_ASSISTANT })
-                }
-                large
-              />
+            <Box className={styles.roleRow}>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleAssistant"
+                  label={userRoleToString(UserRole.DISTRICT_COURT_ASSISTANT)}
+                  checked={user.role === UserRole.DISTRICT_COURT_ASSISTANT}
+                  onChange={() =>
+                    setUser({
+                      ...user,
+                      role: UserRole.DISTRICT_COURT_ASSISTANT,
+                    })
+                  }
+                  large
+                />
+              </Box>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleLocalAdmin"
+                  label={userRoleToString(UserRole.LOCAL_ADMIN)}
+                  checked={user.role === UserRole.LOCAL_ADMIN}
+                  onChange={() =>
+                    setUser({ ...user, role: UserRole.LOCAL_ADMIN })
+                  }
+                  large
+                />
+              </Box>
             </Box>
-          </Box>
+          </>
         ) : user.institution?.type === InstitutionType.COURT_OF_APPEALS ? (
-          <Box display="flex" marginBottom={2}>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleJudge"
-                label="Dómari"
-                checked={user.role === UserRole.COURT_OF_APPEALS_JUDGE}
-                onChange={() =>
-                  setUser({ ...user, role: UserRole.COURT_OF_APPEALS_JUDGE })
-                }
-                large
-              />
+          <>
+            <Box className={styles.roleRow}>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleJudge"
+                  label={userRoleToString(UserRole.COURT_OF_APPEALS_JUDGE)}
+                  checked={user.role === UserRole.COURT_OF_APPEALS_JUDGE}
+                  onChange={() =>
+                    setUser({ ...user, role: UserRole.COURT_OF_APPEALS_JUDGE })
+                  }
+                  large
+                />
+              </Box>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleRegistrar"
+                  label={userRoleToString(UserRole.COURT_OF_APPEALS_REGISTRAR)}
+                  checked={user.role === UserRole.COURT_OF_APPEALS_REGISTRAR}
+                  onChange={() =>
+                    setUser({
+                      ...user,
+                      role: UserRole.COURT_OF_APPEALS_REGISTRAR,
+                    })
+                  }
+                  large
+                />
+              </Box>
             </Box>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleRegistrar"
-                label="Dómritari"
-                checked={user.role === UserRole.COURT_OF_APPEALS_REGISTRAR}
-                onChange={() =>
-                  setUser({
-                    ...user,
-                    role: UserRole.COURT_OF_APPEALS_REGISTRAR,
-                  })
-                }
-                large
-              />
+            <Box className={styles.roleRow}>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleAssistant"
+                  label={userRoleToString(UserRole.COURT_OF_APPEALS_ASSISTANT)}
+                  checked={user.role === UserRole.COURT_OF_APPEALS_ASSISTANT}
+                  onChange={() =>
+                    setUser({
+                      ...user,
+                      role: UserRole.COURT_OF_APPEALS_ASSISTANT,
+                    })
+                  }
+                  large
+                />
+              </Box>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleLocalAdmin"
+                  label={userRoleToString(UserRole.LOCAL_ADMIN)}
+                  checked={user.role === UserRole.LOCAL_ADMIN}
+                  onChange={() =>
+                    setUser({ ...user, role: UserRole.LOCAL_ADMIN })
+                  }
+                  large
+                />
+              </Box>
             </Box>
+          </>
+        ) : user.institution?.type === InstitutionType.PRISON ||
+          user.institution?.type === InstitutionType.PRISON_ADMIN ? (
+          <Box className={styles.roleRow}>
             <Box className={styles.roleColumn}>
               <RadioButton
                 name="role"
-                id="roleAssistant"
-                label="Aðstoðarmaður dómara"
-                checked={user.role === UserRole.COURT_OF_APPEALS_ASSISTANT}
+                id="rolePrisonSystemStaff"
+                label={userRoleToString(UserRole.PRISON_SYSTEM_STAFF)}
+                checked={user.role === UserRole.PRISON_SYSTEM_STAFF}
                 onChange={() =>
-                  setUser({
-                    ...user,
-                    role: UserRole.COURT_OF_APPEALS_ASSISTANT,
-                  })
+                  setUser({ ...user, role: UserRole.PRISON_SYSTEM_STAFF })
                 }
                 large
               />
             </Box>
-          </Box>
-        ) : user.institution?.type === InstitutionType.PRISON ||
-          user.institution?.type === InstitutionType.PRISON_ADMIN ? (
-          <Box display="flex" marginBottom={2}>
             <Box className={styles.roleColumn}>
               <RadioButton
                 name="role"
-                id="rolePrisonSystemStaff"
-                label="Fangelsisyfirvöld"
-                checked={user.role === UserRole.PRISON_SYSTEM_STAFF}
+                id="roleLocalAdmin"
+                label={userRoleToString(UserRole.LOCAL_ADMIN)}
+                checked={user.role === UserRole.LOCAL_ADMIN}
                 onChange={() =>
-                  setUser({ ...user, role: UserRole.PRISON_SYSTEM_STAFF })
+                  setUser({ ...user, role: UserRole.LOCAL_ADMIN })
                 }
                 large
               />
@@ -475,7 +567,7 @@ export const UserForm: FC<Props> = ({
         <FormFooter
           nextButtonIcon="arrowForward"
           onNextButtonClick={saveUser}
-          nextIsDisabled={!isValid()}
+          nextIsDisabled={!isValid}
           nextIsLoading={loading}
           nextButtonText="Vista"
           previousUrl={constants.USERS_ROUTE}
diff --git a/apps/judicial-system/web/src/routes/Admin/Users/Users.tsx b/apps/judicial-system/web/src/routes/Admin/Users/Users.tsx
index d89f1e6cd27c..fc4a731a530e 100644
--- a/apps/judicial-system/web/src/routes/Admin/Users/Users.tsx
+++ b/apps/judicial-system/web/src/routes/Admin/Users/Users.tsx
@@ -20,12 +20,10 @@ import {
   Loading,
   PageHeader,
 } from '@island.is/judicial-system-web/src/components'
-import {
-  User,
-  UserRole,
-} from '@island.is/judicial-system-web/src/graphql/schema'
+import { User } from '@island.is/judicial-system-web/src/graphql/schema'
 import { useInstitution } from '@island.is/judicial-system-web/src/utils/hooks'
 
+import { userRoleToString } from '../userRoleToString'
 import { useUsersQuery } from './users.generated'
 import * as styles from './Users.css'
 
@@ -57,30 +55,6 @@ export const Users = () => {
     router.push(`${constants.CHANGE_USER_ROUTE}/${user.id}`)
   }
 
-  const userRoleToString = (userRole?: UserRole | null) => {
-    switch (userRole) {
-      case UserRole.PROSECUTOR:
-        return 'Saksóknari'
-      case UserRole.PROSECUTOR_REPRESENTATIVE:
-        return 'Fulltrúi'
-      case UserRole.DISTRICT_COURT_JUDGE:
-      case UserRole.COURT_OF_APPEALS_JUDGE:
-        return 'Dómari'
-      case UserRole.DISTRICT_COURT_REGISTRAR:
-      case UserRole.COURT_OF_APPEALS_REGISTRAR:
-        return 'Dómritari'
-      case UserRole.DISTRICT_COURT_ASSISTANT:
-      case UserRole.COURT_OF_APPEALS_ASSISTANT:
-        return 'Aðstoðarmaður dómara'
-      case UserRole.PRISON_SYSTEM_STAFF:
-        return 'Starfsmaður'
-      case UserRole.PUBLIC_PROSECUTOR_STAFF:
-        return 'Skrifstofa'
-      default:
-        return 'Óþekkt'
-    }
-  }
-
   return (
     <div className={styles.userControlContainer}>
       <PageHeader title={formatMessage(titles.admin.users)} />
diff --git a/apps/judicial-system/web/src/routes/Admin/userRoleToString.ts b/apps/judicial-system/web/src/routes/Admin/userRoleToString.ts
new file mode 100644
index 000000000000..eaebd10aec25
--- /dev/null
+++ b/apps/judicial-system/web/src/routes/Admin/userRoleToString.ts
@@ -0,0 +1,27 @@
+import { UserRole } from '@island.is/judicial-system-web/src/graphql/schema'
+
+export const userRoleToString = (userRole?: UserRole | null) => {
+  switch (userRole) {
+    case UserRole.PROSECUTOR:
+      return 'Saksóknari'
+    case UserRole.PROSECUTOR_REPRESENTATIVE:
+      return 'Fulltrúi'
+    case UserRole.DISTRICT_COURT_JUDGE:
+    case UserRole.COURT_OF_APPEALS_JUDGE:
+      return 'Dómari'
+    case UserRole.DISTRICT_COURT_REGISTRAR:
+    case UserRole.COURT_OF_APPEALS_REGISTRAR:
+      return 'Dómritari'
+    case UserRole.DISTRICT_COURT_ASSISTANT:
+    case UserRole.COURT_OF_APPEALS_ASSISTANT:
+      return 'Aðstoðarmaður dómara'
+    case UserRole.PRISON_SYSTEM_STAFF:
+      return 'Fangelsisyfirvöld'
+    case UserRole.PUBLIC_PROSECUTOR_STAFF:
+      return 'Skrifstofa'
+    case UserRole.LOCAL_ADMIN:
+      return 'Notendaumsjón'
+    default:
+      return 'Óþekkt'
+  }
+}
diff --git a/apps/judicial-system/web/src/routes/Shared/Landing/Landing.tsx b/apps/judicial-system/web/src/routes/Shared/Landing/Landing.tsx
index 5f7ebca3d154..b91c0873b7c9 100644
--- a/apps/judicial-system/web/src/routes/Shared/Landing/Landing.tsx
+++ b/apps/judicial-system/web/src/routes/Shared/Landing/Landing.tsx
@@ -34,7 +34,7 @@ const Landing = () => {
   const { user, eligibleUsers } = useContext(UserContext)
 
   useEffect(() => {
-    if (eligibleUsers && eligibleUsers.length === 1) {
+    if (user && eligibleUsers && eligibleUsers.length === 1) {
       const redirectRoute = isDefenceUser(user)
         ? DEFENDER_CASES_ROUTE
         : isPrisonStaffUser(user)
diff --git a/libs/judicial-system/types/src/lib/user.ts b/libs/judicial-system/types/src/lib/user.ts
index ebf006a02a0b..228d9ce307c6 100644
--- a/libs/judicial-system/types/src/lib/user.ts
+++ b/libs/judicial-system/types/src/lib/user.ts
@@ -11,6 +11,7 @@ export enum UserRole {
   COURT_OF_APPEALS_REGISTRAR = 'COURT_OF_APPEALS_REGISTRAR', // dómritari
   COURT_OF_APPEALS_ASSISTANT = 'COURT_OF_APPEALS_ASSISTANT', // aðstoðarmaður dómara
   PRISON_SYSTEM_STAFF = 'PRISON_SYSTEM_STAFF', // fangelsismálastarfsmaður
+  LOCAL_ADMIN = 'LOCAL_ADMIN',
   ADMIN = 'ADMIN', // Does not exist in the database // notendaumsjón
   DEFENDER = 'DEFENDER', // Does not exist in the database // verjandi
 }
@@ -140,12 +141,19 @@ export const isAdminUser = (user?: InstitutionUser): boolean => {
   return Boolean(user?.role && adminRoles.includes(user.role))
 }
 
+const localAdminRoles: string[] = [UserRole.LOCAL_ADMIN]
+
+export const isLocalAdminUser = (user?: InstitutionUser): boolean => {
+  return Boolean(user?.role && localAdminRoles.includes(user.role))
+}
+
 export const isCoreUser = (user?: InstitutionUser): boolean => {
   return (
     isProsecutionUser(user) ||
     isPublicProsecutorUser(user) ||
     isDistrictCourtUser(user) ||
     isCourtOfAppealsUser(user) ||
-    isPrisonSystemUser(user)
+    isPrisonSystemUser(user) ||
+    isLocalAdminUser(user)
   )
 }


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>apps/judicial-system/backend/src/app/modules/user/user.model.ts (1)</summary><blockquote>

`60-68`: **Role restrictions align with the LOCAL_ADMIN feature.**

The filter to exclude `UserRole.ADMIN` and `UserRole.DEFENDER` from the allowed role values properly implements the authorization boundary for the new local admin feature. This approach ensures data integrity at the model level.

One suggestion: Consider adding a comment explaining why these specific roles are excluded to help future developers understand the design decision.


```diff
 @Column({
   type: DataType.ENUM,
   allowNull: false,
   values: Object.values(UserRole).filter(
-    (role) => ![UserRole.ADMIN, UserRole.DEFENDER].includes(role),
+    (role) => {
+      // ADMIN is restricted as it has system-wide privileges
+      // DEFENDER role is handled separately and not managed through this interface
+      return ![UserRole.ADMIN, UserRole.DEFENDER].includes(role);
+    },
   ),
 })
```

</blockquote></details>
<details>
<summary>apps/judicial-system/backend/migrations/20250407144915-update-user.js (1)</summary><blockquote>

`1-1`: **Remove redundant 'use strict' directive.**

The 'use strict' directive is redundant in modules as they are automatically in strict mode.

```diff
-'use strict'
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.


(lint/suspicious/noRedundantUseStrict)

</details>

</details>

</blockquote></details>
<details>
<summary>apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx (2)</summary><blockquote>

`38-45`: **Consider adding more specific error handling with toast messages.**

The current error handling only shows a generic error message. For a better user experience, consider capturing specific error types (e.g., network errors, validation errors, or permission issues) and displaying more informative toast messages.

```diff
const [createUserMutation, { loading: userCreating }] = useCreateUserMutation(
  {
    onCompleted: () => router.push(constants.USERS_ROUTE),
-   onError: () => {
-     toast.error(formatMessage(strings.createError))
-   },
+   onError: (error) => {
+     // Handle specific error types
+     if (error.message.includes("duplicate")) {
+       toast.error(formatMessage(strings.duplicateUserError))
+     } else if (error.networkError) {
+       toast.error(formatMessage(strings.networkError))
+     } else {
+       toast.error(formatMessage(strings.createError))
+     }
+     console.error('User creation error:', error)
+   },
  },
)
```

---

`53-55`: **Consider type safety improvements for the role property.**

With the transition from enum to string for roles, ensure type safety by implementing a string literal union type or validation for the `role` property to prevent invalid role values.

```shell
#!/bin/bash
# Check for type definitions related to user roles

echo "Checking for user role type definitions:"
fd "(UserRole|user.?role|Role).(tsx|ts)" apps/judicial-system/web --exec cat {} \; | rg -A 5 -B 5 "type|interface|enum|const.*=.*["
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 1d2ebfa8019d7011a4f308ef792265ac40fca52e and 55d22045d194f8ea530ed87de81ee1512b60d86e.

</details>

<details>
<summary>📒 Files selected for processing (9)</summary>

* `apps/judicial-system/backend/migrations/20250407144915-update-user.js` (1 hunks)
* `apps/judicial-system/backend/src/app/modules/user/user.model.ts` (1 hunks)
* `apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx` (1 hunks)
* `apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.css.ts` (1 hunks)
* `apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx` (4 hunks)
* `apps/judicial-system/web/src/routes/Admin/Users/Users.tsx` (1 hunks)
* `apps/judicial-system/web/src/routes/Admin/userRoleToString.ts` (1 hunks)
* `apps/judicial-system/web/src/routes/Shared/Landing/Landing.tsx` (1 hunks)
* `libs/judicial-system/types/src/lib/user.ts` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (2)</summary>

<details>
<summary>`apps/**/*`: "Confirm that the code adheres to the following:
- NextJS best practices, including file structure, API routes, and static generation methods.
- Efficient state manage...</summary>

> `apps/**/*`: "Confirm that the code adheres to the following:
> - NextJS best practices, including file structure, API routes, and static generation methods.
> - Efficient state management and server-side rendering techniques.
> - Optimal use of TypeScript for component and utility type safety."
> 

- `apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.css.ts`
- `apps/judicial-system/web/src/routes/Shared/Landing/Landing.tsx`
- `apps/judicial-system/web/src/routes/Admin/userRoleToString.ts`
- `apps/judicial-system/backend/migrations/20250407144915-update-user.js`
- `apps/judicial-system/web/src/routes/Admin/Users/Users.tsx`
- `apps/judicial-system/backend/src/app/modules/user/user.model.ts`
- `apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx`
- `apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx`

</details>
<details>
<summary>`libs/**/*`: "Confirm that the code adheres to the following:
- Reusability of components and hooks across different NextJS apps.
- TypeScript usage for defining props and exportin...</summary>

> `libs/**/*`: "Confirm that the code adheres to the following:
> - Reusability of components and hooks across different NextJS apps.
> - TypeScript usage for defining props and exporting types.
> - Effective tree-shaking and bundling practices."
> 

- `libs/judicial-system/types/src/lib/user.ts`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (6)</summary>

<details>
<summary>apps/judicial-system/web/src/routes/Shared/Landing/Landing.tsx (1)</summary><blockquote>

<details>
<summary>apps/judicial-system/api/src/app/modules/user/user.resolver.ts (1)</summary>

* `user` (62-77)

</details>

</blockquote></details>
<details>
<summary>apps/judicial-system/web/src/routes/Admin/userRoleToString.ts (1)</summary><blockquote>

<details>
<summary>libs/judicial-system/types/src/index.ts (1)</summary>

* `UserRole` (41-41)

</details>

</blockquote></details>
<details>
<summary>apps/judicial-system/backend/migrations/20250407144915-update-user.js (1)</summary><blockquote>

<details>
<summary>libs/auth-api-lib/seeders/local/004-island.is.client.js (1)</summary>

* `transaction` (172-172)

</details>

</blockquote></details>
<details>
<summary>libs/judicial-system/types/src/lib/user.ts (1)</summary><blockquote>

<details>
<summary>libs/judicial-system/types/src/index.ts (1)</summary>

* `UserRole` (41-41)

</details>

</blockquote></details>
<details>
<summary>apps/judicial-system/backend/src/app/modules/user/user.model.ts (1)</summary><blockquote>

<details>
<summary>libs/judicial-system/types/src/index.ts (1)</summary>

* `UserRole` (41-41)

</details>

</blockquote></details>
<details>
<summary>apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx (4)</summary><blockquote>

<details>
<summary>apps/judicial-system/web/src/utils/validate.ts (1)</summary>

* `isAdminUserFormValid` (597-609)

</details>
<details>
<summary>libs/judicial-system/types/src/lib/user.ts (1)</summary>

* `isCoreUser` (150-159)

</details>
<details>
<summary>apps/judicial-system/web/src/routes/Admin/userRoleToString.ts (1)</summary>

* `userRoleToString` (3-27)

</details>
<details>
<summary>libs/judicial-system/types/src/index.ts (2)</summary>

* `UserRole` (41-41)
* `InstitutionType` (15-15)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>apps/judicial-system/backend/migrations/20250407144915-update-user.js</summary>

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.


(lint/suspicious/noRedundantUseStrict)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (21)</summary><blockquote>

<details>
<summary>apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.css.ts (2)</summary>

`5-9`: **Good refactoring to use flexbox layout with gap**

The addition of the `roleRow` style using flexbox with gap is a modern approach that simplifies the layout and spacing between role selection components. This follows best practices for component layout in NextJS applications and will improve maintainability.

---

`11-13`: **Clean simplification of roleColumn style**

The simplified roleColumn style now works in conjunction with the new roleRow style, removing redundant selector logic and making the code more maintainable. This adheres well to the coding guidelines for reusable components.

</details>
<details>
<summary>apps/judicial-system/web/src/routes/Shared/Landing/Landing.tsx (1)</summary>

`37-37`: **Good defensive programming practice added**

The addition of the `user` check in the conditional ensures that the code will only attempt to evaluate user roles when a valid user object exists. This prevents potential runtime errors and improves the robustness of the redirection logic.

</details>
<details>
<summary>apps/judicial-system/web/src/routes/Admin/userRoleToString.ts (2)</summary>

`1-27`: **Well-structured centralized role translation utility**

This new function follows the DRY principle by centralizing the translation of role enums to user-friendly strings. The implementation:

1. Properly handles optional/null inputs
2. Groups related roles that share the same display text
3. Includes a fallback for unexpected values

This utility aligns with the coding guidelines for effective component reusability across NextJS apps and improved maintainability.

---

`22-23`: **Verify the LOCAL_ADMIN role translation is correct**

The translation "Notendaumsjón" for the LOCAL_ADMIN role appears correct based on context, but since this is a new role as mentioned in the PR objectives, ensure this translation accurately reflects the intended meaning for users.

</details>
<details>
<summary>libs/judicial-system/types/src/lib/user.ts (3)</summary>

`14-14`: **New LOCAL_ADMIN role added correctly**

The addition of the LOCAL_ADMIN role to the UserRole enum aligns with the PR objectives to introduce a new local admin role within the user management system.

---

`144-148`: **Well-implemented role validation function**

The implementation of `isLocalAdminUser` follows the same pattern as other role validation functions in the file, maintaining consistency in the codebase. The function properly checks if the user's role is included in the localAdminRoles array.

---

`156-157`: **Proper integration with existing core user validation**

The `isLocalAdminUser` function is correctly integrated into the `isCoreUser` validation, ensuring that local admins are recognized as core users in the system. This change supports the PR objective to allow for proper user segmentation.

</details>
<details>
<summary>apps/judicial-system/web/src/routes/Admin/Users/Users.tsx (2)</summary>

`23-23`: **Good use of direct import.**

The import of `User` without needing to import `UserRole` reflects the code's better separation of concerns. This change aligns with TypeScript best practices by only importing what's needed.

---

`26-26`: **Improved code organization by extracting the userRoleToString function.**

Moving the `userRoleToString` function to a separate file is a good refactoring that enhances maintainability by centralizing role formatting logic. This approach makes it easier to update role display names in a single location.

</details>
<details>
<summary>apps/judicial-system/backend/migrations/20250407144915-update-user.js (2)</summary>

`4-33`: **Well-structured migration for role changes and unique constraints.**

The migration correctly implements the database changes needed to support the new local admin role functionality. The transaction ensures that changes are atomic, and the sequence of operations is logical:

1. Changing the column type from ENUM to STRING
2. Dropping the enum type
3. Updating the unique constraints

This implementation properly supports the PR objective of allowing users with the same ID but different roles within an institution.

---

`35-72`: **Complete rollback implementation.**

The `down` method properly reverts all changes made in the `up` method, ensuring the migration is fully reversible. The order of operations is correct, and the transaction ensures atomicity.

</details>
<details>
<summary>apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx (7)</summary>

`32-32`: **Good use of centralized role label utility.**

Importing the `userRoleToString` utility function follows best practices for code organization and maintainability.

---

`98-98`: **Simplified validation logic.**

Converting the validation from a function call to a direct boolean expression improves readability and performance by eliminating an unnecessary function wrapper.

---

`220-299`: **Improved role selection UI for prosecutors offices.**

The restructured UI for prosecutor office roles:
1. Properly organizes roles into logical groupings
2. Uses the centralized `userRoleToString` function for consistent labeling
3. Correctly implements the new LOCAL_ADMIN role option
4. Maintains specific behavior for the public prosecutor's office

This implementation aligns well with NextJS best practices for component structure and TypeScript type safety.

---

`301-362`: **Well-structured role selection for district courts.**

The district court role selection UI is well-organized with:
1. Consistent layout using Box components
2. Proper integration of the LOCAL_ADMIN role
3. Clear hierarchy and visual organization

The implementation follows Island.is UI system design patterns correctly.

---

`363-423`: **Consistent role UI pattern for court of appeals.**

The court of appeals role selection maintains the same pattern as other institution types, ensuring a consistent user experience throughout the application.

---

`424-451`: **Proper role handling for prison institutions.**

The prison institution role selection correctly implements the LOCAL_ADMIN role option while maintaining the existing PRISON_SYSTEM_STAFF role.

---

`570-570`: **Improved form validation implementation.**

Using the boolean expression directly in `nextIsDisabled` is cleaner than calling a function and aligns with React best practices for prop passing.

</details>
<details>
<summary>apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx (2)</summary>

`22-26`: **The user object has been simplified to only include essential default properties.**

This change aligns with the PR objective to modify the user role structure, removing the previously hardcoded role value based on the `UserRole` enum. This simplification is appropriate as the actual user properties will be populated by the `UserForm` component when a user is created.

---

`47-65`: <details>
<summary>❓ Verification inconclusive</summary>

**Verify that the UserForm component properly initializes all required properties.**

The `createUser` function still expects properties like `name`, `nationalId`, `role`, etc., which are no longer initialized in the `user` object declaration. This approach is valid if the `UserForm` component ensures these properties are populated before `createUser` is called, but it creates an implicit dependency that could be prone to errors.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if UserForm initializes the required properties or uses default values

# Find and analyze the UserForm component
echo "Examining UserForm component implementation:"
fd "UserForm.(tsx|ts|js|jsx)" apps/judicial-system/web/src --exec cat {} \; | rg -A 10 -B 10 "function UserForm|const UserForm|class UserForm"

# Check for validation before form submission
echo "Checking for validation before form submission:"
fd "UserForm.(tsx|ts|js|jsx)" apps/judicial-system/web/src --exec cat {} \; | rg -A 5 -B 5 "onSave|onSubmit|handleSubmit"
```

Length of output: 1676

---



</details>

**Action Required: Confirm Complete Initialization of User Properties**

The `createUser` function in  
`apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx`  
expects a `user` object with fully defined properties (such as `name`, `nationalId`, `role`, etc.). However, the current implementation in the `UserForm` component relies on the incoming `existingUser` prop to initialize its state and later spreads that state in the `onSave` callback. This creates an implicit dependency on the caller to ensure that all required properties are present.

- Confirm that the initial `existingUser` provided to `UserForm` includes valid, non-empty values for all required fields.
- Consider incorporating explicit validation or default value assignments within `UserForm` to guarantee the completeness of the user object before invoking the `onSave` callback.
- Re-assess the data flow to ensure that no properties are inadvertently omitted or left undefined, which could lead to errors when the `createUser` mutation is executed.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T12:08:50Z
user: login: coderabbitai[bot]
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T12:08:53Z
user: login: oddsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T12:38:25Z
user: login: oddsson
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T12:39:36Z
user: login: gudjong
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T12:45:34Z

Review comments:
diff_hunk: @@ -218,139 +217,232 @@ export const UserForm: FC<Props> = ({
           />
         </Box>
         {user.institution?.type === InstitutionType.PROSECUTORS_OFFICE ? (
-          <Box display="flex" marginBottom={2}>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleProsecutor"
-                label="Saksóknari"
-                checked={user.role === UserRole.PROSECUTOR}
-                onChange={() => setUser({ ...user, role: UserRole.PROSECUTOR })}
-                large
-              />
+          <>
+            <Box className={styles.roleRow}>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleProsecutor"
+                  label={userRoleToString(UserRole.PROSECUTOR)}
+                  checked={user.role === UserRole.PROSECUTOR}
+                  onChange={() =>
+                    setUser({ ...user, role: UserRole.PROSECUTOR })
+                  }
+                  large
+                />
+              </Box>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleProsecutorRepresentative"
+                  label={userRoleToString(UserRole.PROSECUTOR_REPRESENTATIVE)}
+                  checked={user.role === UserRole.PROSECUTOR_REPRESENTATIVE}
+                  onChange={() =>
+                    setUser({
+                      ...user,
+                      role: UserRole.PROSECUTOR_REPRESENTATIVE,
+                    })
+                  }
+                  large
+                />
+              </Box>
             </Box>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleProsecutorRepresentative"
-                label="Fulltrúi"
-                checked={user.role === UserRole.PROSECUTOR_REPRESENTATIVE}
-                onChange={() =>
-                  setUser({ ...user, role: UserRole.PROSECUTOR_REPRESENTATIVE })
-                }
-                large
-              />
+            <Box className={styles.roleRow}>
+              {user.institution.id ===
+              '8f9e2f6d-6a00-4a5e-b39b-95fd110d762e' ? (
+                <>
+                  <Box className={styles.roleColumn}>
+                    <RadioButton
+                      name="role"
+                      id="rolePublicProsecutorStaff"
+                      label={userRoleToString(UserRole.PUBLIC_PROSECUTOR_STAFF)}
+                      checked={user.role === UserRole.PUBLIC_PROSECUTOR_STAFF}
+                      onChange={() =>
+                        setUser({
+                          ...user,
+                          role: UserRole.PUBLIC_PROSECUTOR_STAFF,
+                        })
+                      }
+                      large
+                    />
+                  </Box>
+                  <Box className={styles.roleColumn}>
+                    <RadioButton
+                      name="role"
+                      id="roleLocalAdmin"
+                      label={userRoleToString(UserRole.LOCAL_ADMIN)}
+                      checked={user.role === UserRole.LOCAL_ADMIN}
+                      onChange={() =>
+                        setUser({ ...user, role: UserRole.LOCAL_ADMIN })
+                      }
+                      large
+                    />
+                  </Box>
+                </>
+              ) : (
+                <>
+                  <Box className={styles.roleColumn}>
+                    <RadioButton
+                      name="role"
+                      id="roleLocalAdmin"
+                      label={userRoleToString(UserRole.LOCAL_ADMIN)}
+                      checked={user.role === UserRole.LOCAL_ADMIN}
+                      onChange={() =>
+                        setUser({ ...user, role: UserRole.LOCAL_ADMIN })
+                      }
+                      large
+                    />
+                  </Box>
+                  <Box className={styles.roleColumn} />
path: apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx
user: login: oddsson
body: Is this empty div needed for something?
created_at: 2025-04-09T12:38:25Z
author_association: MEMBER
diff_hunk: @@ -218,139 +217,232 @@ export const UserForm: FC<Props> = ({
           />
         </Box>
         {user.institution?.type === InstitutionType.PROSECUTORS_OFFICE ? (
-          <Box display="flex" marginBottom={2}>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleProsecutor"
-                label="Saksóknari"
-                checked={user.role === UserRole.PROSECUTOR}
-                onChange={() => setUser({ ...user, role: UserRole.PROSECUTOR })}
-                large
-              />
+          <>
+            <Box className={styles.roleRow}>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleProsecutor"
+                  label={userRoleToString(UserRole.PROSECUTOR)}
+                  checked={user.role === UserRole.PROSECUTOR}
+                  onChange={() =>
+                    setUser({ ...user, role: UserRole.PROSECUTOR })
+                  }
+                  large
+                />
+              </Box>
+              <Box className={styles.roleColumn}>
+                <RadioButton
+                  name="role"
+                  id="roleProsecutorRepresentative"
+                  label={userRoleToString(UserRole.PROSECUTOR_REPRESENTATIVE)}
+                  checked={user.role === UserRole.PROSECUTOR_REPRESENTATIVE}
+                  onChange={() =>
+                    setUser({
+                      ...user,
+                      role: UserRole.PROSECUTOR_REPRESENTATIVE,
+                    })
+                  }
+                  large
+                />
+              </Box>
             </Box>
-            <Box className={styles.roleColumn}>
-              <RadioButton
-                name="role"
-                id="roleProsecutorRepresentative"
-                label="Fulltrúi"
-                checked={user.role === UserRole.PROSECUTOR_REPRESENTATIVE}
-                onChange={() =>
-                  setUser({ ...user, role: UserRole.PROSECUTOR_REPRESENTATIVE })
-                }
-                large
-              />
+            <Box className={styles.roleRow}>
+              {user.institution.id ===
+              '8f9e2f6d-6a00-4a5e-b39b-95fd110d762e' ? (
+                <>
+                  <Box className={styles.roleColumn}>
+                    <RadioButton
+                      name="role"
+                      id="rolePublicProsecutorStaff"
+                      label={userRoleToString(UserRole.PUBLIC_PROSECUTOR_STAFF)}
+                      checked={user.role === UserRole.PUBLIC_PROSECUTOR_STAFF}
+                      onChange={() =>
+                        setUser({
+                          ...user,
+                          role: UserRole.PUBLIC_PROSECUTOR_STAFF,
+                        })
+                      }
+                      large
+                    />
+                  </Box>
+                  <Box className={styles.roleColumn}>
+                    <RadioButton
+                      name="role"
+                      id="roleLocalAdmin"
+                      label={userRoleToString(UserRole.LOCAL_ADMIN)}
+                      checked={user.role === UserRole.LOCAL_ADMIN}
+                      onChange={() =>
+                        setUser({ ...user, role: UserRole.LOCAL_ADMIN })
+                      }
+                      large
+                    />
+                  </Box>
+                </>
+              ) : (
+                <>
+                  <Box className={styles.roleColumn}>
+                    <RadioButton
+                      name="role"
+                      id="roleLocalAdmin"
+                      label={userRoleToString(UserRole.LOCAL_ADMIN)}
+                      checked={user.role === UserRole.LOCAL_ADMIN}
+                      onChange={() =>
+                        setUser({ ...user, role: UserRole.LOCAL_ADMIN })
+                      }
+                      large
+                    />
+                  </Box>
+                  <Box className={styles.roleColumn} />
path: apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx
user: login: gudjong
body: Just making sure all role radio buttons have the same size. This was an easy way to do that.
created_at: 2025-04-09T12:45:34Z
author_association: MEMBER

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-09T08:34:57Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This pull request introduces several modifications to the judicial system application. On the backend, a database migration alters the `user` table by changing the `role` column from an ENUM to a STRING and updating unique constraints. The user model is adjusted to filter out specific roles. In the frontend, several components related to user management are refactored by removing unnecessary properties, streamlining the role selection UI, and migrating role label generation to an external utility. Additionally, a new `LOCAL_ADMIN` role is added to the types with its corresponding validation function, and a minor redirect logic update is applied in the landing page.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `apps/judicial-system/backend/migrations/.../update-user.js` | Database migration: Changed `role` column to STRING, dropped `enum_user_role`, and updated unique constraints (adding and subsequently reverting the change in `down`). |
| `apps/judicial-system/backend/src/app/modules/user/user.model.ts` | Updated the `User` model by filtering out `UserRole.ADMIN` and `UserRole.DEFENDER` from allowed role values. |
| `apps/judicial-system/web/src/routes/Admin/NewUser/NewUser.tsx` | Removed several properties (`created`, `modified`, `nationalId`, `name`, `title`, `mobileNumber`, `email`, `role`, `institution`) from the `user` object and deleted the `UserRole` import. |
| `apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.tsx`<br>`apps/judicial-system/web/src/routes/Admin/UserForm/UserForm.css.ts` | Refactored role selection: Integrated an external `userRoleToString` for label generation, simplified validation logic, restructured UI layout, added new `roleRow` styling, and removed specific margin rules in `roleColumn`. |
| `apps/judicial-system/web/src/routes/Admin/Users/Users.tsx`<br>`apps/judicial-system/web/src/routes/Admin/userRoleToString.ts` | Removed the internal `userRoleToString` function and `UserRole` import in favor of an external utility (`userRoleToString.ts`) that maps user roles to Icelandic strings. |
| `apps/judicial-system/web/src/routes/Shared/Landing/Landing.tsx` | Updated the redirect logic in the `useEffect` hook to ensure that a user is defined before verifying `eligibleUsers`. |
| `libs/judicial-system/types/src/lib/user.ts` | Expanded the `UserRole` enum by adding `LOCAL_ADMIN`, introduced the `isLocalAdminUser` function, and updated `isCoreUser` to incorporate the local admin check. |
| `apps/judicial-system/web/src/routes/Admin/ChangeUser/updateUser.graphql` | Removed the `modified` field from the response of the `updateUser` mutation. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant U as User
    participant L as Landing Component
    participant R as Router

    U ->> L: Visit landing page
    L ->> L: Execute useEffect (check user & eligibleUsers)
    alt User is defined and eligibleUsers count is 1
        L ->> R: Initiate redirect to designated route
    else
        L -->> U: Render landing view
    end
```

## Possibly related PRs

- **island-is/island.is#18714**: Updates the `UpdateUserInput` class to make the `role` property optional, aligning with changes in user role handling.

## Suggested reviewers

- oddsson
- thorhildurt

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 55d22045d194f8ea530ed87de81ee1512b60d86e and fc177bbfff2baad80dede5440e070776d6f7b4df.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `apps/judicial-system/web/src/routes/Admin/ChangeUser/updateUser.graphql` (0 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* apps/judicial-system/web/src/routes/Admin/ChangeUser/updateUser.graphql

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: prepare / install
* GitHub Check: Lintfix Changed Files

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=island-is/island.is&utm_content=18712):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: nx-cloud[bot]
created_at: 2025-04-09T08:41:51Z
body: 
View your [CI Pipeline Execution ↗](https://cloud.nx.app/cipes/67fbe62ee94071427fdf1864?utm_source=pull-request&utm_medium=comment) for commit 6e998310ad2e0923bd7affd3aa76a469c625f8db.

| Command | Status | Duration | Result |
|---------|--------|----------:|--------|
| `nx run-many --projects judicial-system-backend ...` | ✅ Succeeded | 16m 24s | [View ↗](https://cloud.nx.app/runs/ry3e3cbYXQ?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=build --projects=judicial-...` | ✅ Succeeded | 5m 49s | [View ↗](https://cloud.nx.app/runs/VLO9SAImbB?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --projects judicial-system-api,judi...` | ✅ Succeeded | 3m 57s | [View ↗](https://cloud.nx.app/runs/8slEmzQxNE?utm_source=pull-request&utm_medium=comment) |
| `nx run-many -t lint --output-style=static --pro...` | ✅ Succeeded | 25s | [View ↗](https://cloud.nx.app/runs/7mo7SDgAbA?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=build --projects=judicial-...` | ✅ Succeeded | 41s | [View ↗](https://cloud.nx.app/runs/13OoLW1gSr?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=codegen/frontend-client --...` | ✅ Succeeded | 15s | [View ↗](https://cloud.nx.app/runs/gaids93jjB?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=codegen/backend-schema --a...` | ✅ Succeeded | 28s | [View ↗](https://cloud.nx.app/runs/DjBvkL6t8L?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=codegen/backend-client --a...` | ✅ Succeeded | 23s | [View ↗](https://cloud.nx.app/runs/3mqpEL98lB?utm_source=pull-request&utm_medium=comment) |

---

☁️ [Nx Cloud](https://cloud.nx.app?utm_source=pull-request&utm_medium=comment) last updated this comment at `2025-04-13 16:49:18` UTC

<!-- NX_CLOUD_APP_COMMENT_END -->
author_association: NONE
user: login: datadog-island-is[bot]
created_at: 2025-04-09T08:59:02Z
body: 
# Datadog Report

All test runs `4de5102` [:link:](https://app.datadoghq.eu/ci/test-runs?query=%40git.repository.id_v2%3A%22github.com%2Fisland-is%2Fisland.is%22+%40git.commit.sha%3A4de510279271d7c4a7c165aa2a29c0f5010b9a2a+%40git.branch%3A%22j-s%2Flocal-admin%22&_user_click_origin_=github)

:white_check_mark: **9 Total Test Services**: 0 Failed, 9 Passed



<details>
<summary>Test Services</summary>

| Service Name | Failed | Known Flaky | New Flaky | Passed | Skipped | Total Time | Test Service View |
| ------------ | ------ | ----------- | --------- | ------ | ------- | --------- | ----------------- |
| [**`judicial-system-api`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/judicial-system-api/j-s%2Flocal-admin/4de510279271d7c4a7c165aa2a29c0f5010b9a2a?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 61 | 0 | 5.7s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/judicial-system-api/j-s%2Flocal-admin?env=ci&index=citest&_user_click_origin_=github) |
| [**`judicial-system-backend`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/judicial-system-backend/j-s%2Flocal-admin/4de510279271d7c4a7c165aa2a29c0f5010b9a2a?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 22466 | 0 | 16m 19.34s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/judicial-system-backend/j-s%2Flocal-admin?env=ci&index=citest&_user_click_origin_=github) |
| [**`judicial-system-formatters`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/judicial-system-formatters/j-s%2Flocal-admin/4de510279271d7c4a7c165aa2a29c0f5010b9a2a?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 40 | 0 | 8.34s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/judicial-system-formatters/j-s%2Flocal-admin?env=ci&index=citest&_user_click_origin_=github) |
| [**`judicial-system-message`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/judicial-system-message/j-s%2Flocal-admin/4de510279271d7c4a7c165aa2a29c0f5010b9a2a?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 45 | 0 | 11.59s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/judicial-system-message/j-s%2Flocal-admin?env=ci&index=citest&_user_click_origin_=github) |
| [**`judicial-system-message-handler`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/judicial-system-message-handler/j-s%2Flocal-admin/4de510279271d7c4a7c165aa2a29c0f5010b9a2a?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 4 | 0 | 8.24s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/judicial-system-message-handler/j-s%2Flocal-admin?env=ci&index=citest&_user_click_origin_=github) |
| [**`judicial-system-scheduler`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/judicial-system-scheduler/j-s%2Flocal-admin/4de510279271d7c4a7c165aa2a29c0f5010b9a2a?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 4 | 0 | 9.82s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/judicial-system-scheduler/j-s%2Flocal-admin?env=ci&index=citest&_user_click_origin_=github) |
| [**`judicial-system-types`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/judicial-system-types/j-s%2Flocal-admin/4de510279271d7c4a7c165aa2a29c0f5010b9a2a?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 28 | 0 | 7.15s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/judicial-system-types/j-s%2Flocal-admin?env=ci&index=citest&_user_click_origin_=github) |
| [**`judicial-system-web`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/judicial-system-web/j-s%2Flocal-admin/4de510279271d7c4a7c165aa2a29c0f5010b9a2a?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 344 | 0 | 3m 3.4s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/judicial-system-web/j-s%2Flocal-admin?env=ci&index=citest&_user_click_origin_=github) |
| [**`judicial-system-xrd-api`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/judicial-system-xrd-api/j-s%2Flocal-admin/4de510279271d7c4a7c165aa2a29c0f5010b9a2a?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 6 | 0 | 40.63s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/judicial-system-xrd-api/j-s%2Flocal-admin?env=ci&index=citest&_user_click_origin_=github) |
</details>



author_association: NONE