Compare commits

...

15 Commits

Author SHA1 Message Date
Owen Schwartz
834e44336c New translations en-us.json (Spanish)
[ci skip]
2026-05-08 18:35:18 -07:00
Owen Schwartz
f2160ea270 New translations en-us.json (Norwegian Bokmal)
[ci skip]
2026-05-08 18:35:16 -07:00
Owen Schwartz
c0da2f184d New translations en-us.json (Chinese Simplified)
[ci skip]
2026-05-08 18:35:15 -07:00
Owen Schwartz
3e6eb06bd8 New translations en-us.json (Turkish)
[ci skip]
2026-05-08 18:35:13 -07:00
Owen Schwartz
fac3f3a8c0 New translations en-us.json (Russian)
[ci skip]
2026-05-08 18:35:11 -07:00
Owen Schwartz
425eda8e48 New translations en-us.json (Portuguese)
[ci skip]
2026-05-08 18:35:09 -07:00
Owen Schwartz
aa4860885c New translations en-us.json (Polish)
[ci skip]
2026-05-08 18:35:07 -07:00
Owen Schwartz
65605362da New translations en-us.json (Dutch)
[ci skip]
2026-05-08 18:35:06 -07:00
Owen Schwartz
5d1ce49f63 New translations en-us.json (Korean)
[ci skip]
2026-05-08 18:35:04 -07:00
Owen Schwartz
6119c07272 New translations en-us.json (Italian)
[ci skip]
2026-05-08 18:35:02 -07:00
Owen Schwartz
9daa95aeef New translations en-us.json (German)
[ci skip]
2026-05-08 18:35:01 -07:00
Owen Schwartz
3572d92174 New translations en-us.json (Czech)
[ci skip]
2026-05-08 18:34:59 -07:00
Owen Schwartz
9dcc423c88 New translations en-us.json (Bulgarian)
[ci skip]
2026-05-08 18:34:57 -07:00
Owen Schwartz
e445c2efbd New translations en-us.json (French)
[ci skip]
2026-05-08 18:34:55 -07:00
miloschwartz
9fb677e952 allow editing self and owner user roles 2026-05-08 17:48:43 -07:00
24 changed files with 307 additions and 80 deletions

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "След като бъде премахнат, този потребител няма да има достъп до организацията. Винаги можете да го поканите отново по-късно, но той ще трябва да приеме отново поканата.", "userMessageOrgRemove": "След като бъде премахнат, този потребител няма да има достъп до организацията. Винаги можете да го поканите отново по-късно, но той ще трябва да приеме отново поканата.",
"userRemoveOrgConfirm": "Потвърдете премахването на потребителя", "userRemoveOrgConfirm": "Потвърдете премахването на потребителя",
"userRemoveOrg": "Премахване на потребителя от организацията", "userRemoveOrg": "Премахване на потребителя от организацията",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Потребители", "users": "Потребители",
"accessRoleMember": "Член", "accessRoleMember": "Член",
"accessRoleOwner": "Собственик", "accessRoleOwner": "Собственик",
@@ -531,6 +537,11 @@
"emailInvalid": "Невалиден имейл адрес", "emailInvalid": "Невалиден имейл адрес",
"inviteValidityDuration": "Моля, изберете продължителност", "inviteValidityDuration": "Моля, изберете продължителност",
"accessRoleSelectPlease": "Моля, изберете роля", "accessRoleSelectPlease": "Моля, изберете роля",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Необходимо е потребителско име", "usernameRequired": "Необходимо е потребителско име",
"idpSelectPlease": "Моля, изберете доставчик на идентичност", "idpSelectPlease": "Моля, изберете доставчик на идентичност",
"idpGenericOidc": "Основен OAuth2/OIDC доставчик.", "idpGenericOidc": "Основен OAuth2/OIDC доставчик.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Po odstranění tohoto uživatele již nebude mít přístup k organizaci. Vždy je můžete znovu pozvat později, ale budou muset pozvání znovu přijmout.", "userMessageOrgRemove": "Po odstranění tohoto uživatele již nebude mít přístup k organizaci. Vždy je můžete znovu pozvat později, ale budou muset pozvání znovu přijmout.",
"userRemoveOrgConfirm": "Potvrdit odebrání uživatele", "userRemoveOrgConfirm": "Potvrdit odebrání uživatele",
"userRemoveOrg": "Odebrat uživatele z organizace", "userRemoveOrg": "Odebrat uživatele z organizace",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Uživatelé", "users": "Uživatelé",
"accessRoleMember": "Člen", "accessRoleMember": "Člen",
"accessRoleOwner": "Vlastník", "accessRoleOwner": "Vlastník",
@@ -531,6 +537,11 @@
"emailInvalid": "Neplatná e-mailová adresa", "emailInvalid": "Neplatná e-mailová adresa",
"inviteValidityDuration": "Zvolte prosím dobu trvání", "inviteValidityDuration": "Zvolte prosím dobu trvání",
"accessRoleSelectPlease": "Vyberte prosím roli", "accessRoleSelectPlease": "Vyberte prosím roli",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Uživatelské jméno je povinné", "usernameRequired": "Uživatelské jméno je povinné",
"idpSelectPlease": "Vyberte poskytovatele identity", "idpSelectPlease": "Vyberte poskytovatele identity",
"idpGenericOidc": "Generic OAuth2/OIDC provider.", "idpGenericOidc": "Generic OAuth2/OIDC provider.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Nach dem Entfernen hat dieser Benutzer keinen Zugriff mehr auf die Organisation. Sie können ihn später jederzeit wieder einladen, aber er muss die Einladung erneut annehmen.", "userMessageOrgRemove": "Nach dem Entfernen hat dieser Benutzer keinen Zugriff mehr auf die Organisation. Sie können ihn später jederzeit wieder einladen, aber er muss die Einladung erneut annehmen.",
"userRemoveOrgConfirm": "Entfernen des Benutzers bestätigen", "userRemoveOrgConfirm": "Entfernen des Benutzers bestätigen",
"userRemoveOrg": "Benutzer aus der Organisation entfernen", "userRemoveOrg": "Benutzer aus der Organisation entfernen",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Benutzer", "users": "Benutzer",
"accessRoleMember": "Mitglied", "accessRoleMember": "Mitglied",
"accessRoleOwner": "Eigentümer", "accessRoleOwner": "Eigentümer",
@@ -531,6 +537,11 @@
"emailInvalid": "Ungültige E-Mail-Adresse", "emailInvalid": "Ungültige E-Mail-Adresse",
"inviteValidityDuration": "Bitte wählen Sie eine Dauer", "inviteValidityDuration": "Bitte wählen Sie eine Dauer",
"accessRoleSelectPlease": "Bitte wählen Sie eine Rolle", "accessRoleSelectPlease": "Bitte wählen Sie eine Rolle",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Benutzername ist erforderlich", "usernameRequired": "Benutzername ist erforderlich",
"idpSelectPlease": "Bitte wählen Sie einen Identitätsanbieter", "idpSelectPlease": "Bitte wählen Sie einen Identitätsanbieter",
"idpGenericOidc": "Generischer OAuth2/OIDC-Anbieter.", "idpGenericOidc": "Generischer OAuth2/OIDC-Anbieter.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Once removed, this user will no longer have access to the organization. You can always re-invite them later, but they will need to accept the invitation again.", "userMessageOrgRemove": "Once removed, this user will no longer have access to the organization. You can always re-invite them later, but they will need to accept the invitation again.",
"userRemoveOrgConfirm": "Confirm Remove User", "userRemoveOrgConfirm": "Confirm Remove User",
"userRemoveOrg": "Remove User from Organization", "userRemoveOrg": "Remove User from Organization",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Users", "users": "Users",
"accessRoleMember": "Member", "accessRoleMember": "Member",
"accessRoleOwner": "Owner", "accessRoleOwner": "Owner",
@@ -531,6 +537,11 @@
"emailInvalid": "Invalid email address", "emailInvalid": "Invalid email address",
"inviteValidityDuration": "Please select a duration", "inviteValidityDuration": "Please select a duration",
"accessRoleSelectPlease": "Please select a role", "accessRoleSelectPlease": "Please select a role",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Username is required", "usernameRequired": "Username is required",
"idpSelectPlease": "Please select an identity provider", "idpSelectPlease": "Please select an identity provider",
"idpGenericOidc": "Generic OAuth2/OIDC provider.", "idpGenericOidc": "Generic OAuth2/OIDC provider.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Una vez eliminado, este usuario ya no tendrá acceso a la organización. Siempre puede volver a invitarlos más tarde, pero tendrán que aceptar la invitación de nuevo.", "userMessageOrgRemove": "Una vez eliminado, este usuario ya no tendrá acceso a la organización. Siempre puede volver a invitarlos más tarde, pero tendrán que aceptar la invitación de nuevo.",
"userRemoveOrgConfirm": "Confirmar eliminar usuario", "userRemoveOrgConfirm": "Confirmar eliminar usuario",
"userRemoveOrg": "Eliminar usuario de la organización", "userRemoveOrg": "Eliminar usuario de la organización",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Usuarios", "users": "Usuarios",
"accessRoleMember": "Miembro", "accessRoleMember": "Miembro",
"accessRoleOwner": "Propietario", "accessRoleOwner": "Propietario",
@@ -531,6 +537,11 @@
"emailInvalid": "Dirección de correo inválida", "emailInvalid": "Dirección de correo inválida",
"inviteValidityDuration": "Por favor, seleccione una duración", "inviteValidityDuration": "Por favor, seleccione una duración",
"accessRoleSelectPlease": "Por favor, seleccione un rol", "accessRoleSelectPlease": "Por favor, seleccione un rol",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Nombre de usuario requerido", "usernameRequired": "Nombre de usuario requerido",
"idpSelectPlease": "Por favor, seleccione un proveedor de identidad", "idpSelectPlease": "Por favor, seleccione un proveedor de identidad",
"idpGenericOidc": "Proveedor OAuth2/OIDC genérico.", "idpGenericOidc": "Proveedor OAuth2/OIDC genérico.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Une fois retiré, cet utilisateur n'aura plus accès à l'organisation. Vous pouvez toujours le réinviter plus tard, mais il devra accepter l'invitation à nouveau.", "userMessageOrgRemove": "Une fois retiré, cet utilisateur n'aura plus accès à l'organisation. Vous pouvez toujours le réinviter plus tard, mais il devra accepter l'invitation à nouveau.",
"userRemoveOrgConfirm": "Confirmer la suppression de l'utilisateur", "userRemoveOrgConfirm": "Confirmer la suppression de l'utilisateur",
"userRemoveOrg": "Retirer l'utilisateur de l'organisation", "userRemoveOrg": "Retirer l'utilisateur de l'organisation",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Utilisateurs", "users": "Utilisateurs",
"accessRoleMember": "Membre", "accessRoleMember": "Membre",
"accessRoleOwner": "Propriétaire", "accessRoleOwner": "Propriétaire",
@@ -531,6 +537,11 @@
"emailInvalid": "Adresse e-mail invalide", "emailInvalid": "Adresse e-mail invalide",
"inviteValidityDuration": "Veuillez sélectionner une durée", "inviteValidityDuration": "Veuillez sélectionner une durée",
"accessRoleSelectPlease": "Veuillez sélectionner un rôle", "accessRoleSelectPlease": "Veuillez sélectionner un rôle",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Le nom d'utilisateur est requis", "usernameRequired": "Le nom d'utilisateur est requis",
"idpSelectPlease": "Veuillez sélectionner un fournisseur d'identité", "idpSelectPlease": "Veuillez sélectionner un fournisseur d'identité",
"idpGenericOidc": "Fournisseur OAuth2/OIDC générique.", "idpGenericOidc": "Fournisseur OAuth2/OIDC générique.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Una volta rimosso questo utente non avrà più accesso all'organizzazione. Puoi sempre reinvitarlo in seguito, ma dovrà accettare nuovamente l'invito.", "userMessageOrgRemove": "Una volta rimosso questo utente non avrà più accesso all'organizzazione. Puoi sempre reinvitarlo in seguito, ma dovrà accettare nuovamente l'invito.",
"userRemoveOrgConfirm": "Conferma Rimozione Utente", "userRemoveOrgConfirm": "Conferma Rimozione Utente",
"userRemoveOrg": "Rimuovi Utente dall'Organizzazione", "userRemoveOrg": "Rimuovi Utente dall'Organizzazione",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Utenti", "users": "Utenti",
"accessRoleMember": "Membro", "accessRoleMember": "Membro",
"accessRoleOwner": "Proprietario", "accessRoleOwner": "Proprietario",
@@ -531,6 +537,11 @@
"emailInvalid": "Indirizzo email non valido", "emailInvalid": "Indirizzo email non valido",
"inviteValidityDuration": "Seleziona una durata", "inviteValidityDuration": "Seleziona una durata",
"accessRoleSelectPlease": "Seleziona un ruolo", "accessRoleSelectPlease": "Seleziona un ruolo",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Username richiesto", "usernameRequired": "Username richiesto",
"idpSelectPlease": "Seleziona un provider di identità", "idpSelectPlease": "Seleziona un provider di identità",
"idpGenericOidc": "Provider OAuth2/OIDC generico.", "idpGenericOidc": "Provider OAuth2/OIDC generico.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "이 사용자가 제거되면 더 이상 조직에 접근할 수 없습니다. 나중에 다시 초대할 수 있지만, 초대를 다시 수락해야 합니다.", "userMessageOrgRemove": "이 사용자가 제거되면 더 이상 조직에 접근할 수 없습니다. 나중에 다시 초대할 수 있지만, 초대를 다시 수락해야 합니다.",
"userRemoveOrgConfirm": "사용자 제거 확인", "userRemoveOrgConfirm": "사용자 제거 확인",
"userRemoveOrg": "조직에서 사용자 제거", "userRemoveOrg": "조직에서 사용자 제거",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "사용자", "users": "사용자",
"accessRoleMember": "회원", "accessRoleMember": "회원",
"accessRoleOwner": "소유자", "accessRoleOwner": "소유자",
@@ -531,6 +537,11 @@
"emailInvalid": "유효하지 않은 이메일 주소입니다.", "emailInvalid": "유효하지 않은 이메일 주소입니다.",
"inviteValidityDuration": "지속 시간을 선택하십시오.", "inviteValidityDuration": "지속 시간을 선택하십시오.",
"accessRoleSelectPlease": "역할을 선택하세요", "accessRoleSelectPlease": "역할을 선택하세요",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "사용자 이름은 필수입니다.", "usernameRequired": "사용자 이름은 필수입니다.",
"idpSelectPlease": "신원 제공자를 선택하십시오", "idpSelectPlease": "신원 제공자를 선택하십시오",
"idpGenericOidc": "일반 OAuth2/OIDC 공급자.", "idpGenericOidc": "일반 OAuth2/OIDC 공급자.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Når denne brukeren er fjernet, vil de ikke lenger ha tilgang til organisasjonen. Du kan alltid invitere dem på nytt senere, men de vil måtte godta invitasjonen på nytt.", "userMessageOrgRemove": "Når denne brukeren er fjernet, vil de ikke lenger ha tilgang til organisasjonen. Du kan alltid invitere dem på nytt senere, men de vil måtte godta invitasjonen på nytt.",
"userRemoveOrgConfirm": "Bekreft fjerning av bruker", "userRemoveOrgConfirm": "Bekreft fjerning av bruker",
"userRemoveOrg": "Fjern bruker fra organisasjon", "userRemoveOrg": "Fjern bruker fra organisasjon",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Brukere", "users": "Brukere",
"accessRoleMember": "Medlem", "accessRoleMember": "Medlem",
"accessRoleOwner": "Eier", "accessRoleOwner": "Eier",
@@ -531,6 +537,11 @@
"emailInvalid": "Ugyldig e-postadresse", "emailInvalid": "Ugyldig e-postadresse",
"inviteValidityDuration": "Vennligst velg en varighet", "inviteValidityDuration": "Vennligst velg en varighet",
"accessRoleSelectPlease": "Vennligst velg en rolle", "accessRoleSelectPlease": "Vennligst velg en rolle",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Brukernavn er påkrevd", "usernameRequired": "Brukernavn er påkrevd",
"idpSelectPlease": "Vennligst velg en identitetsleverandør", "idpSelectPlease": "Vennligst velg en identitetsleverandør",
"idpGenericOidc": "Generisk OAuth2/OIDC-leverandør.", "idpGenericOidc": "Generisk OAuth2/OIDC-leverandør.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Eenmaal verwijderd, heeft deze gebruiker geen toegang meer tot de organisatie. Je kunt ze later altijd opnieuw uitnodigen, maar ze zullen de uitnodiging opnieuw moeten accepteren.", "userMessageOrgRemove": "Eenmaal verwijderd, heeft deze gebruiker geen toegang meer tot de organisatie. Je kunt ze later altijd opnieuw uitnodigen, maar ze zullen de uitnodiging opnieuw moeten accepteren.",
"userRemoveOrgConfirm": "Bevestig verwijderen gebruiker", "userRemoveOrgConfirm": "Bevestig verwijderen gebruiker",
"userRemoveOrg": "Gebruiker uit organisatie verwijderen", "userRemoveOrg": "Gebruiker uit organisatie verwijderen",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Gebruikers", "users": "Gebruikers",
"accessRoleMember": "Lid", "accessRoleMember": "Lid",
"accessRoleOwner": "Eigenaar", "accessRoleOwner": "Eigenaar",
@@ -531,6 +537,11 @@
"emailInvalid": "Ongeldig e-mailadres", "emailInvalid": "Ongeldig e-mailadres",
"inviteValidityDuration": "Selecteer een tijdsduur", "inviteValidityDuration": "Selecteer een tijdsduur",
"accessRoleSelectPlease": "Selecteer een rol", "accessRoleSelectPlease": "Selecteer een rol",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Gebruikersnaam is verplicht", "usernameRequired": "Gebruikersnaam is verplicht",
"idpSelectPlease": "Selecteer een identiteitsprovider", "idpSelectPlease": "Selecteer een identiteitsprovider",
"idpGenericOidc": "Algemene OAuth2/OIDC provider.", "idpGenericOidc": "Algemene OAuth2/OIDC provider.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Po usunięciu ten użytkownik nie będzie miał już dostępu do organizacji. Zawsze możesz ponownie go zaprosić później, ale będzie musiał ponownie zaakceptować zaproszenie.", "userMessageOrgRemove": "Po usunięciu ten użytkownik nie będzie miał już dostępu do organizacji. Zawsze możesz ponownie go zaprosić później, ale będzie musiał ponownie zaakceptować zaproszenie.",
"userRemoveOrgConfirm": "Potwierdź usunięcie użytkownika", "userRemoveOrgConfirm": "Potwierdź usunięcie użytkownika",
"userRemoveOrg": "Usuń użytkownika z organizacji", "userRemoveOrg": "Usuń użytkownika z organizacji",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Użytkownicy", "users": "Użytkownicy",
"accessRoleMember": "Członek", "accessRoleMember": "Członek",
"accessRoleOwner": "Właściciel", "accessRoleOwner": "Właściciel",
@@ -531,6 +537,11 @@
"emailInvalid": "Nieprawidłowy adres e-mail", "emailInvalid": "Nieprawidłowy adres e-mail",
"inviteValidityDuration": "Proszę wybrać okres ważności", "inviteValidityDuration": "Proszę wybrać okres ważności",
"accessRoleSelectPlease": "Proszę wybrać rolę", "accessRoleSelectPlease": "Proszę wybrać rolę",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Nazwa użytkownika jest wymagana", "usernameRequired": "Nazwa użytkownika jest wymagana",
"idpSelectPlease": "Proszę wybrać dostawcę tożsamości", "idpSelectPlease": "Proszę wybrać dostawcę tożsamości",
"idpGenericOidc": "Ogólny dostawca OAuth2/OIDC.", "idpGenericOidc": "Ogólny dostawca OAuth2/OIDC.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Uma vez removido, este utilizador não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.", "userMessageOrgRemove": "Uma vez removido, este utilizador não terá mais acesso à organização. Você sempre pode reconvidá-lo depois, mas eles precisarão aceitar o convite novamente.",
"userRemoveOrgConfirm": "Confirmar Remoção do Usuário", "userRemoveOrgConfirm": "Confirmar Remoção do Usuário",
"userRemoveOrg": "Remover Usuário da Organização", "userRemoveOrg": "Remover Usuário da Organização",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Utilizadores", "users": "Utilizadores",
"accessRoleMember": "Membro", "accessRoleMember": "Membro",
"accessRoleOwner": "Proprietário", "accessRoleOwner": "Proprietário",
@@ -531,6 +537,11 @@
"emailInvalid": "Endereço de email inválido", "emailInvalid": "Endereço de email inválido",
"inviteValidityDuration": "Por favor, selecione uma duração", "inviteValidityDuration": "Por favor, selecione uma duração",
"accessRoleSelectPlease": "Por favor, selecione uma função", "accessRoleSelectPlease": "Por favor, selecione uma função",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Nome de utilizador é obrigatório", "usernameRequired": "Nome de utilizador é obrigatório",
"idpSelectPlease": "Por favor, selecione um provedor de identidade", "idpSelectPlease": "Por favor, selecione um provedor de identidade",
"idpGenericOidc": "Provedor genérico OAuth2/OIDC.", "idpGenericOidc": "Provedor genérico OAuth2/OIDC.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "После удаления этот пользователь больше не будет иметь доступ к организации. Вы всегда можете пригласить его заново, но ему нужно будет снова принять приглашение.", "userMessageOrgRemove": "После удаления этот пользователь больше не будет иметь доступ к организации. Вы всегда можете пригласить его заново, но ему нужно будет снова принять приглашение.",
"userRemoveOrgConfirm": "Подтвердить удаление пользователя", "userRemoveOrgConfirm": "Подтвердить удаление пользователя",
"userRemoveOrg": "Удалить пользователя из организации", "userRemoveOrg": "Удалить пользователя из организации",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Пользователи", "users": "Пользователи",
"accessRoleMember": "Участник", "accessRoleMember": "Участник",
"accessRoleOwner": "Владелец", "accessRoleOwner": "Владелец",
@@ -531,6 +537,11 @@
"emailInvalid": "Неверный адрес Email", "emailInvalid": "Неверный адрес Email",
"inviteValidityDuration": "Пожалуйста, выберите продолжительность", "inviteValidityDuration": "Пожалуйста, выберите продолжительность",
"accessRoleSelectPlease": "Пожалуйста, выберите роль", "accessRoleSelectPlease": "Пожалуйста, выберите роль",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Имя пользователя обязательно", "usernameRequired": "Имя пользователя обязательно",
"idpSelectPlease": "Пожалуйста, выберите Identity Provider", "idpSelectPlease": "Пожалуйста, выберите Identity Provider",
"idpGenericOidc": "Обычный OAuth2/OIDC provider.", "idpGenericOidc": "Обычный OAuth2/OIDC provider.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "Kaldırıldığında, bu kullanıcı organizasyona artık erişim sağlayamayacak. Kullanıcı tekrar davet edilebilir, ancak daveti kabul etmesi gerekecek.", "userMessageOrgRemove": "Kaldırıldığında, bu kullanıcı organizasyona artık erişim sağlayamayacak. Kullanıcı tekrar davet edilebilir, ancak daveti kabul etmesi gerekecek.",
"userRemoveOrgConfirm": "Kullanıcıyı Kaldırmayı Onayla", "userRemoveOrgConfirm": "Kullanıcıyı Kaldırmayı Onayla",
"userRemoveOrg": "Kullanıcıyı Organizasyondan Kaldır", "userRemoveOrg": "Kullanıcıyı Organizasyondan Kaldır",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "Kullanıcılar", "users": "Kullanıcılar",
"accessRoleMember": "Üye", "accessRoleMember": "Üye",
"accessRoleOwner": "Sahip", "accessRoleOwner": "Sahip",
@@ -531,6 +537,11 @@
"emailInvalid": "Geçersiz e-posta adresi", "emailInvalid": "Geçersiz e-posta adresi",
"inviteValidityDuration": "Lütfen bir süre seçin", "inviteValidityDuration": "Lütfen bir süre seçin",
"accessRoleSelectPlease": "Lütfen bir rol seçin", "accessRoleSelectPlease": "Lütfen bir rol seçin",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "Kullanıcı adı gereklidir", "usernameRequired": "Kullanıcı adı gereklidir",
"idpSelectPlease": "Lütfen bir kimlik sağlayıcı seçin", "idpSelectPlease": "Lütfen bir kimlik sağlayıcı seçin",
"idpGenericOidc": "Genel OAuth2/OIDC sağlayıcısı.", "idpGenericOidc": "Genel OAuth2/OIDC sağlayıcısı.",

View File

@@ -523,6 +523,12 @@
"userMessageOrgRemove": "一旦删除,这个用户将不再能够访问组织。 你总是可以稍后重新邀请他们,但他们需要再次接受邀请。", "userMessageOrgRemove": "一旦删除,这个用户将不再能够访问组织。 你总是可以稍后重新邀请他们,但他们需要再次接受邀请。",
"userRemoveOrgConfirm": "确认删除用户", "userRemoveOrgConfirm": "确认删除用户",
"userRemoveOrg": "从组织中删除用户", "userRemoveOrg": "从组织中删除用户",
"userQuestionOrgRemoveSelf": "Are you sure you want to remove yourself from this organization?",
"userMessageOrgRemoveSelf": "You will lose access immediately. An administrator can invite you again later, but you will need to accept a new invitation.",
"userRemoveOrgConfirmSelf": "Confirm Remove Myself",
"userRemoveOrgSelf": "Remove yourself from the organization",
"userRemoveOrgSelfWarning": "You will lose access to this organization immediately.",
"userRemoveOrgConfirmPhraseSelf": "REMOVE MYSELF FROM ORG",
"users": "用户", "users": "用户",
"accessRoleMember": "成员", "accessRoleMember": "成员",
"accessRoleOwner": "所有者", "accessRoleOwner": "所有者",
@@ -531,6 +537,11 @@
"emailInvalid": "无效的电子邮件地址", "emailInvalid": "无效的电子邮件地址",
"inviteValidityDuration": "请选择持续时间", "inviteValidityDuration": "请选择持续时间",
"accessRoleSelectPlease": "请选择一个角色", "accessRoleSelectPlease": "请选择一个角色",
"removeOwnAdminRoleConfirmTitle": "Remove your administrator access?",
"removeOwnAdminRoleConfirmDescription": "You will no longer have administrator permissions in this organization after saving. Another administrator can restore access if needed.",
"removeOwnAdminRoleConfirmButton": "Remove My Administrator Access",
"removeOwnAdminRoleConfirmPhrase": "REMOVE MY ADMIN ACCESS",
"ownerMustRetainAdminRole": "The organization owner must keep at least one administrator role.",
"usernameRequired": "必须输入用户名", "usernameRequired": "必须输入用户名",
"idpSelectPlease": "请选择身份提供商", "idpSelectPlease": "请选择身份提供商",
"idpGenericOidc": "通用的 OAuth2/OIDC 提供商。", "idpGenericOidc": "通用的 OAuth2/OIDC 提供商。",

View File

@@ -98,15 +98,6 @@ export async function addUserRole(
); );
} }
if (existingUser[0].isOwner) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"Cannot change the role of the owner of the organization"
)
);
}
const roleExists = await db const roleExists = await db
.select() .select()
.from(roles) .from(roles)

View File

@@ -98,11 +98,11 @@ export async function removeUserRole(
); );
} }
if (existingUser.isOwner) { if (existingUser.isOwner && role.isAdmin === true) {
return next( return next(
createHttpError( createHttpError(
HttpCode.FORBIDDEN, HttpCode.FORBIDDEN,
"Cannot change the roles of the owner of the organization" "Cannot remove the administrator role from the organization owner"
) )
); );
} }

View File

@@ -87,17 +87,8 @@ export async function setUserOrgRoles(
); );
} }
if (existingUser.isOwner) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"Cannot change the roles of the owner of the organization"
)
);
}
const orgRoles = await db const orgRoles = await db
.select({ roleId: roles.roleId }) .select({ roleId: roles.roleId, isAdmin: roles.isAdmin })
.from(roles) .from(roles)
.where( .where(
and( and(
@@ -115,6 +106,18 @@ export async function setUserOrgRoles(
); );
} }
if (existingUser.isOwner) {
const hasAdminRole = orgRoles.some((r) => r.isAdmin === true);
if (!hasAdminRole) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"The organization owner must retain an administrator role"
)
);
}
}
let orgClientsToRebuild: Client[] = []; let orgClientsToRebuild: Client[] = [];
await db.transaction(async (trx) => { await db.transaction(async (trx) => {
await trx await trx

View File

@@ -88,11 +88,11 @@ export async function addUserRoleLegacy(
); );
} }
if (existingUser.isOwner) { if (existingUser.isOwner && role.isAdmin !== true) {
return next( return next(
createHttpError( createHttpError(
HttpCode.FORBIDDEN, HttpCode.FORBIDDEN,
"Cannot change the role of the owner of the organization" "The organization owner must retain an administrator role"
) )
); );
} }

View File

@@ -47,10 +47,7 @@ export async function queryUser(orgId: string, userId: string) {
.from(userOrgRoles) .from(userOrgRoles)
.leftJoin(roles, eq(userOrgRoles.roleId, roles.roleId)) .leftJoin(roles, eq(userOrgRoles.roleId, roles.roleId))
.where( .where(
and( and(eq(userOrgRoles.userId, userId), eq(userOrgRoles.orgId, orgId))
eq(userOrgRoles.userId, userId),
eq(userOrgRoles.orgId, orgId)
)
); );
const isAdmin = roleRows.some((r) => r.isAdmin); const isAdmin = roleRows.some((r) => r.isAdmin);
@@ -61,7 +58,8 @@ export async function queryUser(orgId: string, userId: string) {
roleIds: roleRows.map((r) => r.roleId), roleIds: roleRows.map((r) => r.roleId),
roles: roleRows.map((r) => ({ roles: roleRows.map((r) => ({
roleId: r.roleId, roleId: r.roleId,
name: r.roleName ?? "" name: r.roleName ?? "",
isAdmin: r.isAdmin === true
})) }))
}; };
} }

View File

@@ -1,5 +1,6 @@
"use client"; "use client";
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import IdpTypeBadge from "@app/components/IdpTypeBadge"; import IdpTypeBadge from "@app/components/IdpTypeBadge";
import OrgRolesTagField from "@app/components/OrgRolesTagField"; import OrgRolesTagField from "@app/components/OrgRolesTagField";
import { import {
@@ -25,6 +26,7 @@ import { useEnvContext } from "@app/hooks/useEnvContext";
import { userOrgUserContext } from "@app/hooks/useOrgUserContext"; import { userOrgUserContext } from "@app/hooks/useOrgUserContext";
import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { usePaidStatus } from "@app/hooks/usePaidStatus";
import { toast } from "@app/hooks/useToast"; import { toast } from "@app/hooks/useToast";
import { useUserContext } from "@app/hooks/useUserContext";
import { createApiClient, formatAxiosError } from "@app/lib/api"; import { createApiClient, formatAxiosError } from "@app/lib/api";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { build } from "@server/build"; import { build } from "@server/build";
@@ -32,7 +34,7 @@ import { tierMatrix } from "@server/lib/billing/tierMatrix";
import { UserType } from "@server/types/UserTypes"; import { UserType } from "@server/types/UserTypes";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { useActionState, useEffect } from "react"; import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { z } from "zod"; import { z } from "zod";
@@ -42,13 +44,15 @@ const accessControlsFormSchema = z.object({
roles: z.array( roles: z.array(
z.object({ z.object({
id: z.string(), id: z.string(),
text: z.string() text: z.string(),
isAdmin: z.boolean().optional()
}) })
) )
}); });
export default function AccessControlsPage() { export default function AccessControlsPage() {
const { orgUser: user, updateOrgUser } = userOrgUserContext(); const { orgUser: user, updateOrgUser } = userOrgUserContext();
const { user: sessionUser } = useUserContext();
const { env } = useEnvContext(); const { env } = useEnvContext();
const api = createApiClient({ env }); const api = createApiClient({ env });
@@ -72,7 +76,8 @@ export default function AccessControlsPage() {
autoProvisioned: user.autoProvisioned || false, autoProvisioned: user.autoProvisioned || false,
roles: (user.roles ?? []).map((r) => ({ roles: (user.roles ?? []).map((r) => ({
id: r.roleId.toString(), id: r.roleId.toString(),
text: r.name text: r.name,
isAdmin: r.isAdmin === true
})) }))
} }
}); });
@@ -84,7 +89,8 @@ export default function AccessControlsPage() {
"roles", "roles",
(user.roles ?? []).map((r) => ({ (user.roles ?? []).map((r) => ({
id: r.roleId.toString(), id: r.roleId.toString(),
text: r.name text: r.name,
isAdmin: r.isAdmin === true
})) }))
); );
form.setValue("autoProvisioned", user.autoProvisioned || false); form.setValue("autoProvisioned", user.autoProvisioned || false);
@@ -95,11 +101,11 @@ export default function AccessControlsPage() {
? t("singleRolePerUserPlanNotice") ? t("singleRolePerUserPlanNotice")
: t("singleRolePerUserEditionNotice"); : t("singleRolePerUserEditionNotice");
const [, action, isSubmitting] = useActionState(onSubmit, null); const [isSaving, setIsSaving] = useState(false);
async function onSubmit() { const [confirmRemoveOwnAdminOpen, setConfirmRemoveOwnAdminOpen] =
const isValid = await form.trigger(); useState(false);
if (!isValid) return;
async function executeSave() {
const values = form.getValues(); const values = form.getValues();
if (values.roles.length === 0) { if (values.roles.length === 0) {
@@ -111,6 +117,7 @@ export default function AccessControlsPage() {
return; return;
} }
setIsSaving(true);
try { try {
const roleIds = values.roles.map((r) => parseInt(r.id, 10)); const roleIds = values.roles.map((r) => parseInt(r.id, 10));
const updateRoleRequest = supportsMultipleRolesPerUser const updateRoleRequest = supportsMultipleRolesPerUser
@@ -130,7 +137,8 @@ export default function AccessControlsPage() {
roleIds, roleIds,
roles: values.roles.map((r) => ({ roles: values.roles.map((r) => ({
roleId: parseInt(r.id, 10), roleId: parseInt(r.id, 10),
name: r.text name: r.text,
isAdmin: r.isAdmin === true
})), })),
autoProvisioned: values.autoProvisioned autoProvisioned: values.autoProvisioned
}); });
@@ -149,11 +157,61 @@ export default function AccessControlsPage() {
t("accessRoleErrorAddDescription") t("accessRoleErrorAddDescription")
) )
}); });
} finally {
setIsSaving(false);
} }
} }
async function handleAccessControlsSubmit(e: React.FormEvent) {
e.preventDefault();
const isValid = await form.trigger();
if (!isValid) return;
const values = form.getValues();
if (values.roles.length === 0) {
toast({
variant: "destructive",
title: t("accessRoleErrorAdd"),
description: t("accessRoleSelectPlease")
});
return;
}
const willHaveAdminRole = values.roles.some(
(r) => r.isAdmin === true
);
const isRemovingOwnAdmin =
sessionUser.userId === user.userId &&
user.isAdmin &&
!willHaveAdminRole;
if (isRemovingOwnAdmin) {
setConfirmRemoveOwnAdminOpen(true);
return;
}
await executeSave();
}
return ( return (
<SettingsContainer> <SettingsContainer>
<ConfirmDeleteDialog
open={confirmRemoveOwnAdminOpen}
setOpen={setConfirmRemoveOwnAdminOpen}
title={t("removeOwnAdminRoleConfirmTitle")}
dialog={
<div className="space-y-2">
<p>{t("removeOwnAdminRoleConfirmDescription")}</p>
</div>
}
buttonText={t("removeOwnAdminRoleConfirmButton")}
string={t("removeOwnAdminRoleConfirmPhrase")}
onConfirm={executeSave}
/>
<SettingsSection> <SettingsSection>
<SettingsSectionHeader> <SettingsSectionHeader>
<SettingsSectionTitle> <SettingsSectionTitle>
@@ -168,7 +226,7 @@ export default function AccessControlsPage() {
<SettingsSectionForm> <SettingsSectionForm>
<Form {...form}> <Form {...form}>
<form <form
action={action} onSubmit={(e) => void handleAccessControlsSubmit(e)}
className="space-y-4" className="space-y-4"
id="access-controls-form" id="access-controls-form"
> >
@@ -237,8 +295,8 @@ export default function AccessControlsPage() {
<SettingsSectionFooter> <SettingsSectionFooter>
<Button <Button
type="submit" type="submit"
loading={isSubmitting} loading={isSaving}
disabled={isSubmitting} disabled={isSaving}
form="access-controls-form" form="access-controls-form"
> >
{t("accessControlsSubmit")} {t("accessControlsSubmit")}

View File

@@ -99,6 +99,14 @@ export default function UsersTable({
]; ];
}, [searchParams.toString()]); }, [searchParams.toString()]);
const isRemovingSelf = useMemo(() => {
if (!selectedUser || !user) return false;
return (
`${selectedUser.username}-${selectedUser.idpId}` ===
`${user.username}-${user.idpId}`
);
}, [selectedUser, user]);
function handleFilterChange( function handleFilterChange(
column: string, column: string,
value: string | undefined | null value: string | undefined | null
@@ -223,10 +231,7 @@ export default function UsersTable({
header: () => <span className="p-3"></span>, header: () => <span className="p-3"></span>,
cell: ({ row }) => { cell: ({ row }) => {
const userRow = row.original; const userRow = row.original;
const isCurrentUser = const canRemoveFromOrg = !userRow.isOwner;
`${userRow.username}-${userRow.idpId}` ===
`${user?.username}-${user?.idpId}`;
const isDisabled = userRow.isOwner || isCurrentUser;
return ( return (
<div className="flex items-center justify-end"> <div className="flex items-center justify-end">
<div> <div>
@@ -235,7 +240,6 @@ export default function UsersTable({
<Button <Button
variant="ghost" variant="ghost"
className="h-8 w-8 p-0" className="h-8 w-8 p-0"
disabled={isDisabled}
> >
<span className="sr-only"> <span className="sr-only">
{t("openMenu")} {t("openMenu")}
@@ -247,16 +251,12 @@ export default function UsersTable({
<Link <Link
href={`/${org?.org.orgId}/settings/access/users/${userRow.id}`} href={`/${org?.org.orgId}/settings/access/users/${userRow.id}`}
className="block w-full" className="block w-full"
aria-disabled={isDisabled}
onClick={(e) =>
isDisabled && e.preventDefault()
}
> >
<DropdownMenuItem disabled={isDisabled}> <DropdownMenuItem>
{t("accessUserManage")} {t("accessUserManage")}
</DropdownMenuItem> </DropdownMenuItem>
</Link> </Link>
{!isDisabled && ( {canRemoveFromOrg && (
<DropdownMenuItem <DropdownMenuItem
onClick={() => { onClick={() => {
setIsDeleteModalOpen(true); setIsDeleteModalOpen(true);
@@ -271,25 +271,14 @@ export default function UsersTable({
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
{isDisabled ? ( <Link
<Button href={`/${org?.org.orgId}/settings/access/users/${userRow.id}`}
variant={"outline"} >
className="ml-2" <Button variant={"outline"} className="ml-2">
disabled
>
{t("manage")} {t("manage")}
<ArrowRight className="ml-2 w-4 h-4" /> <ArrowRight className="ml-2 w-4 h-4" />
</Button> </Button>
) : ( </Link>
<Link
href={`/${org?.org.orgId}/settings/access/users/${userRow.id}`}
>
<Button variant={"outline"} className="ml-2">
{t("manage")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
)}
</div> </div>
); );
} }
@@ -359,22 +348,45 @@ export default function UsersTable({
}} }}
dialog={ dialog={
<div className="space-y-2"> <div className="space-y-2">
<p>{t("userQuestionOrgRemove")}</p> <p>
<p>{t("userMessageOrgRemove")}</p> {t(
isRemovingSelf
? "userQuestionOrgRemoveSelf"
: "userQuestionOrgRemove"
)}
</p>
<p>
{t(
isRemovingSelf
? "userMessageOrgRemoveSelf"
: "userMessageOrgRemove"
)}
</p>
</div> </div>
} }
buttonText={t("userRemoveOrgConfirm")} buttonText={t(
isRemovingSelf
? "userRemoveOrgConfirmSelf"
: "userRemoveOrgConfirm"
)}
warningText={
isRemovingSelf ? t("userRemoveOrgSelfWarning") : undefined
}
onConfirm={async () => startTransition(removeUser)} onConfirm={async () => startTransition(removeUser)}
string={ string={
selectedUser isRemovingSelf
? getUserDisplayName({ ? t("userRemoveOrgConfirmPhraseSelf")
email: selectedUser.email, : selectedUser
name: selectedUser.name, ? getUserDisplayName({
username: selectedUser.username email: selectedUser.email,
}) name: selectedUser.name,
: "" username: selectedUser.username
})
: ""
} }
title={t("userRemoveOrg")} title={t(
isRemovingSelf ? "userRemoveOrgSelf" : "userRemoveOrg"
)}
/> />
<ControlledDataTable <ControlledDataTable

View File

@@ -11,7 +11,7 @@ import { cn } from "@app/lib/cn";
import { CheckIcon } from "lucide-react"; import { CheckIcon } from "lucide-react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
export type TagValue = { text: string; id: string }; export type TagValue = { text: string; id: string; isAdmin?: boolean };
export type MultiSelectTagsProps<T extends TagValue> = { export type MultiSelectTagsProps<T extends TagValue> = {
emptyPlaceholder?: string; emptyPlaceholder?: string;

View File

@@ -6,7 +6,7 @@ import { useDebounce } from "use-debounce";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { MultiSelectTagInput } from "./multi-select/multi-select-tag-input"; import { MultiSelectTagInput } from "./multi-select/multi-select-tag-input";
export type SelectedRole = { id: string; text: string }; export type SelectedRole = { id: string; text: string; isAdmin?: boolean };
export type RolesSelectorProps = { export type RolesSelectorProps = {
orgId: string; orgId: string;