From 0680486e293d6751286a5b68151db51a088ef353 Mon Sep 17 00:00:00 2001 From: Ryan Berger Date: Mon, 7 Apr 2025 08:44:43 -0400 Subject: [PATCH 01/59] UISAUTHCOM-55 Filter out any capabilities with property `dummyCapability = true` since they are invalid. (#84) --- CHANGELOG.md | 1 + .../useChunkedApplicationCapabilities.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e19e7bd..742b9a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * *BREAKING* [UISAUTHCOM-46](https://folio-org.atlassian.net/browse/UISAUTHCOM-46) migrate react-intl to v7. * *BREAKING* [UISAUTHCOM-50](https://folio-org.atlassian.net/browse/UISAUTHCOM-50) migrate stripes dependencies to their Sunflower versions. +* [UISAUTHCOM-55](https://folio-org.atlassian.net/browse/UISAUTHCOM-55) Filter out any capabilities with property `dummyCapability = true` since they are invalid. API will suppress once MODROLESKC-285 is completed, but this immediately fixes the issue in the UI. ## 1.1.0 diff --git a/lib/hooks/useChunkedApplicationCapabilities/useChunkedApplicationCapabilities.js b/lib/hooks/useChunkedApplicationCapabilities/useChunkedApplicationCapabilities.js index 7390ef0..02ef6e2 100644 --- a/lib/hooks/useChunkedApplicationCapabilities/useChunkedApplicationCapabilities.js +++ b/lib/hooks/useChunkedApplicationCapabilities/useChunkedApplicationCapabilities.js @@ -18,7 +18,8 @@ export const useChunkedApplicationCapabilities = (appIds, options = {}) => { ids: appIds, limit: CAPABILITIES_LIMIT, idName: 'applicationId', - reduceFunction: data => data.flatMap(d => d.data?.capabilities || []), + // Remove dummy capabilities from the results, if any, since they are not valid. + reduceFunction: data => data.flatMap(d => d.data?.capabilities.filter((c) => c && !c.dummyCapability) || []), queryOptions:{ enabled: !isEmpty(appIds) }, From 9a4254bdcbf7f8d0caaea2212463ab2983c81a01 Mon Sep 17 00:00:00 2001 From: Ryan Berger Date: Wed, 9 Apr 2025 16:19:08 -0400 Subject: [PATCH 02/59] Sync CHANGELOG with 2.0.1 release (#86) --- CHANGELOG.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 742b9a2..5e51198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,15 @@ # Change history for stripes-authorization-components -# 2.0.0 (IN PROGRESS) +# [2.0.1](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.1) (2025-04-09) -* *BREAKING* [UISAUTHCOM-46](https://folio-org.atlassian.net/browse/UISAUTHCOM-46) migrate react-intl to v7. -* *BREAKING* [UISAUTHCOM-50](https://folio-org.atlassian.net/browse/UISAUTHCOM-50) migrate stripes dependencies to their Sunflower versions. * [UISAUTHCOM-55](https://folio-org.atlassian.net/browse/UISAUTHCOM-55) Filter out any capabilities with property `dummyCapability = true` since they are invalid. API will suppress once MODROLESKC-285 is completed, but this immediately fixes the issue in the UI. -## 1.1.0 +# [2.0.0](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.0) (2025-03-13) + +* *BREAKING* [UISAUTHCOM-46](https://folio-org.atlassian.net/browse/UISAUTHCOM-46) migrate react-intl to v7. +* *BREAKING* [UISAUTHCOM-50](https://folio-org.atlassian.net/browse/UISAUTHCOM-50) migrate stripes dependencies to their Sunflower versions. + +## 1.1.0 * [UISAUTHCOM-44](https://folio-org.atlassian.net/browse/UISAUTHCOM-44) Add Deque Axe a11y tests. * [UISAUTHCOM-47](https://folio-org.atlassian.net/browse/UISAUTHCOM-47) Retry get publication details request for `404` response status. From 2a245ed96fcb435914f0af20f493ceec9f75192e Mon Sep 17 00:00:00 2001 From: aidynoJ Date: Tue, 15 Apr 2025 18:21:51 +0500 Subject: [PATCH 03/59] bump version to 2.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5444c7d..5ad4082 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@folio/stripes-authorization-components", - "version": "2.0.0", + "version": "2.0.1", "description": "Component library for Stripes Authorization modules", "publishConfig": { "registry": "https://repository.folio.org/repository/npm-folio/" From d7bbf25050c59c1a84db30df41bee5b743ebaee5 Mon Sep 17 00:00:00 2001 From: aidynoJ <121284650+aidynoJ@users.noreply.github.com> Date: Tue, 15 Apr 2025 18:48:22 +0500 Subject: [PATCH 04/59] UISAUTHCOM-51: Provide `expand=false` parameter to `useRoleCapabilities` that used in `useInitalRoleSharing` to correctly retrieve directly assigned capabilities (#87) Refs UISAUTHCOM-51. --- CHANGELOG.md | 4 ++++ .../consortia/useInitialRoleSharing/useInitialRoleSharing.js | 2 +- lib/hooks/useRoleCapabilities/useRoleCapabilities.js | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e51198..2fac181 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change history for stripes-authorization-components +# [2.0.2](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.2) + +* [UISAUTHCOM-51](https://folio-org.atlassian.net/browse/UISAUTHCOM-51) Provide `expand=false` parameter to `useRoleCapabilities` that used in `useInitalRoleSharing` to correctly retrieve directly assigned capabilities + # [2.0.1](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.1) (2025-04-09) * [UISAUTHCOM-55](https://folio-org.atlassian.net/browse/UISAUTHCOM-55) Filter out any capabilities with property `dummyCapability = true` since they are invalid. API will suppress once MODROLESKC-285 is completed, but this immediately fixes the issue in the UI. diff --git a/lib/hooks/consortia/useInitialRoleSharing/useInitialRoleSharing.js b/lib/hooks/consortia/useInitialRoleSharing/useInitialRoleSharing.js index 120294c..5db9c72 100644 --- a/lib/hooks/consortia/useInitialRoleSharing/useInitialRoleSharing.js +++ b/lib/hooks/consortia/useInitialRoleSharing/useInitialRoleSharing.js @@ -18,7 +18,7 @@ export const useInitialRoleSharing = (role, { tenantId }) => { ); const { initialRoleCapabilitySetsNames } = useRoleCapabilitySets(role?.id, centralTenantId, { enabled: sharingEnabled }); - const { initialRoleCapabilitiesNames } = useRoleCapabilities(role?.id, centralTenantId, true, { enabled: sharingEnabled }); + const { initialRoleCapabilitiesNames } = useRoleCapabilities(role?.id, centralTenantId, false, { enabled: sharingEnabled }); const { upsertSharedRole, isLoading } = useRoleSharing(); diff --git a/lib/hooks/useRoleCapabilities/useRoleCapabilities.js b/lib/hooks/useRoleCapabilities/useRoleCapabilities.js index c06dbd3..1ff68ac 100644 --- a/lib/hooks/useRoleCapabilities/useRoleCapabilities.js +++ b/lib/hooks/useRoleCapabilities/useRoleCapabilities.js @@ -24,6 +24,7 @@ import { getCapabilitiesGroupedByTypeAndResource } from '../../utils'; * @param {string} roleId The Role ID. * @param {string} tenant The Tenant ID. Passes into `useOkapiKy` which will default to `stripes.okapi.tenant` if omitted. * @param {boolean} expand Defines if capability sets must be expanded in the API response. Defaults to `false`. + * with expand=false API returns capabilities that was assigned directly, not by capability set. * @param {object} options Any additional options to pass into `useQuery()`. * @returns Capabilities. */ From 8619f0074e01181face0cefa33e05851f4e76041 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Thu, 17 Apr 2025 23:58:39 -0400 Subject: [PATCH 05/59] Update translation strings --- .../stripes-authorization-components/ar.json | 136 +++++++++--------- .../cs_CZ.json | 2 +- .../stripes-authorization-components/nl.json | 58 ++++---- .../stripes-authorization-components/pl.json | 26 ++-- .../stripes-authorization-components/uk.json | 6 +- .../zh_CN.json | 10 +- 6 files changed, 119 insertions(+), 119 deletions(-) diff --git a/translations/stripes-authorization-components/ar.json b/translations/stripes-authorization-components/ar.json index 28028d0..d334e0f 100644 --- a/translations/stripes-authorization-components/ar.json +++ b/translations/stripes-authorization-components/ar.json @@ -1,92 +1,92 @@ { - "columns.application": "Application", - "columns.resource": "Resource", - "columns.view": "View", - "columns.edit": "Edit", - "columns.create": "Create", - "columns.delete": "Delete", - "columns.manage": "Manage", - "columns.policies": "Policies", - "columns.settings": "Settings/Settings sets", - "columns.procedure": "Procedure", - "columns.execute": "Execute", - "details.data": "Data", - "details.settings": "Settings", - "details.procedural": "Procedural", + "columns.application": "التطبيق", + "columns.resource": "المورد", + "columns.view": "عرض", + "columns.edit": "تحرير", + "columns.create": "إنشاء", + "columns.delete": "حذف", + "columns.manage": "أدر", + "columns.policies": "السياسات", + "columns.settings": "الإعدادات/مجموعات الإعدادات", + "columns.procedure": "الإجراءات", + "columns.execute": "تنفيذ", + "details.data": "البيانات", + "details.settings": "الإعدادات", + "details.procedural": "إجرائي", "search": "Search", "new": "New", - "columns.name": "Name", - "columns.description": "Description", - "columns.updatedDate": "Updated", - "columns.updatedBy": "Updated by", - "assignedUsers": "Assigned users", - "assignUnassign": "Assign/Unassign", + "columns.name": "الاسم", + "columns.description": "الوصف", + "columns.updatedDate": "تم التحديث", + "columns.updatedBy": "تم التحديث بواسطة", + "assignedUsers": "المستخدمين المعينين", + "assignUnassign": "تعيين/إلغاء تعيين", "generalInformation": "General Information", "details.capabilities": "Capabilities", "details.capabilitySets": "Capability sets", - "details.nonSinglePolicyText": "* indicates one or more policies are set for the action.", - "crud.createRole": "Create role", - "crud.deleteRole": "Delete role", - "crud.deleteRoleConfirmation": "{rolename} will be deleted", - "crud.editRole": "Edit role", - "crud.cancel": "Cancel", - "crud.selectApplication": "Select application", - "crud.edit": "Edit", - "crud.delete": "Delete", + "details.nonSinglePolicyText": "* يشير إلى أنه تم تعيين سياسة واحدة أو أكثر للإجراء.", + "crud.createRole": "إنشاء دور", + "crud.deleteRole": "حذف الدور", + "crud.deleteRoleConfirmation": "سيتم حذف {rolename}", + "crud.editRole": "تحرير الدور", + "crud.cancel": "إلغاء", + "crud.selectApplication": "حدد التطبيق", + "crud.edit": "تحرير", + "crud.delete": "حذف", "form.labels.name": "Name", "form.labels.description": "Description", "form.labels.multiplePolicies": "Multiple policies on action {action}", - "applications.select": "Select application", - "applications.found": "{count} applications found", - "applications.totalSelected": "Total selected: {count}", - "applications": "Applications", - "role-details.accordion-users.columns.fullName": "Name", - "role-details.accordion-users.columns.patronGroup": "Patron group", + "applications.select": "حدد التطبيق", + "applications.found": "تم العثور على {count} تطبيقاً", + "applications.totalSelected": "إجمالي المحدد: {count}", + "applications": "التطبيقات", + "role-details.accordion-users.columns.fullName": "الاسم", + "role-details.accordion-users.columns.patronGroup": "مجموعة المستفيدين", "policyDetails.columns.name": "Name", "policyDetails.columns.description": "Description", "form.labels.policyType": "Policy type", "form.labels.sourceType": "Source type", "form.labels.policyType.user": "User", "form.labels.policyType.time": "Time", - "form.labels.policyType.role": "Role", + "form.labels.policyType.role": "الدور", "form.labels.sourceType.user": "User", - "form.labels.sourceType.system": "System", + "form.labels.sourceType.system": "النظام", "form.labels.sourceType.consortium": "Consortium", - "policy.edit": "Edit policy", + "policy.edit": "تحرير السياسة", "policy.create": "Create policy", - "policy.create.success": "Policy has been created successfully", - "policy.edit.success": "Policy has been updated successfully", + "policy.create.success": "تم إنشاء السياسة بنجاح", + "policy.edit.success": "تم تحديث السياسة بنجاح", "policy.create.error": "Policy could not be created", - "policy.edit.error": "Policy could not be updated", - "form.unassignAllCapabilities": "Unassign all capabilities/sets", - "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", - "keycloak.records.confirmationLabel": "Create user records in Keycloak", - "keycloak.records.checking": "Checking Keycloak records for the selected users", - "crud.duplicate": "Duplicate", - "crud.duplicateRole": "Duplicate role?", - "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", - "applications.notAvailable": "Select application is not available", - "crud.duplicate.suffix": "(duplicate)", + "policy.edit.error": "تعذر تحديث السياسة", + "form.unassignAllCapabilities": "إلغاء تعيين جميع القدرات/المجموعات", + "keycloak.records.creation": "ستؤدي هذه العملية إلى إنشاء تسجيلات جديدة في Keycloak للمستخدمين التاليين: {users}", + "keycloak.records.confirmationLabel": "إنشاء تسجيلات المستخدم في Keycloak", + "keycloak.records.checking": "التحقق من تسجيلات Keycloak للمستخدمين المحددين", + "crud.duplicate": "تكرار", + "crud.duplicateRole": "تكرار الدور؟", + "crud.duplicateConfirmation": "هل أنت متأكد أنك تريد إنشاء نسخة مكررة من هذا الدور {name}؟", + "applications.notAvailable": "تم تحديد التطبيق غير متاح", + "crud.duplicate.suffix": "(تكرار)", "details.centrallyManaged": "Centrally managed", "filter.true": "Yes", "filter.false": "No", "shareToAll": "Share to all", - "keycloak.records.created": "Keycloak records created for the following users: {users}", - "policy.share.success": "Policy has been shared successfully", - "policy.share.error": "Policy could not be shared", - "role.create.success": "Role has been created successfully", - "role.create.error": "Role could not be created", - "role.delete.success": "Role has been deleted successfully", - "role.delete.error": "Role could not be deleted", - "role.edit.error": "Role could not be updated", - "role.edit.success": "Role has been updated successfully", - "role.share.error": "Role could not be shared", - "role.share.success": "Role has been shared successfully", - "columns.type": "Type", - "role.type.default": "Default", - "role.type.support": "Support", - "role.type.regular": "Regular", - "role.type.consortium": "Consortium", - "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "keycloak.records.created": "تم إنشاء تسجيلات Keycloak للمستخدمين التاليين: {users}", + "policy.share.success": "تمت مشاركة السياسة بنجاح", + "policy.share.error": "تعذر مشاركة السياسة", + "role.create.success": "تم إنشاء الدور بنجاح", + "role.create.error": "تعذر إنشاء الدور", + "role.delete.success": "تم حذف الدور بنجاح", + "role.delete.error": "تعذر حذف الدور", + "role.edit.error": "تعذر تحديث الدور", + "role.edit.success": "تم تحديث الدور بنجاح", + "role.share.error": "تعذر مشاركة الدور", + "role.share.success": "تمت مشاركة الدور بنجاح", + "columns.type": "النوع", + "role.type.default": "الافتراضي", + "role.type.support": "دعم", + "role.type.regular": "منتظم", + "role.type.consortium": "اتحاد المكتبات", + "assignUnassign.modal.label": "حدد المستخدم ( {tenant} )", + "columns.header.checkbox": "حدد كل إمكانيات {action} " } \ No newline at end of file diff --git a/translations/stripes-authorization-components/cs_CZ.json b/translations/stripes-authorization-components/cs_CZ.json index 1f68ba2..6a239a8 100644 --- a/translations/stripes-authorization-components/cs_CZ.json +++ b/translations/stripes-authorization-components/cs_CZ.json @@ -71,7 +71,7 @@ "filter.true": "Ano", "filter.false": "Ne", "shareToAll": "Sdílet všem", - "keycloak.records.created": "Záznamy Keycloak vytvořeny pro vybrané uživatele: {users}", + "keycloak.records.created": "Záznamy Keycloak vytvořeny pro následující uživatele: {users}", "policy.share.success": "Pravidlo bylo sdíleno úspěšně", "policy.share.error": "Pravidlo se nepodařilo sdílet", "role.create.success": "Role byla úspěšně vytvořena", diff --git a/translations/stripes-authorization-components/nl.json b/translations/stripes-authorization-components/nl.json index ddf50ce..c0095b9 100644 --- a/translations/stripes-authorization-components/nl.json +++ b/translations/stripes-authorization-components/nl.json @@ -58,35 +58,35 @@ "policy.edit.success": "Beleid is succesvol bijgewerkt", "policy.create.error": "Beleid kon niet worden aangemaakt", "policy.edit.error": "Beleid kon niet worden bijgewerkt", - "form.unassignAllCapabilities": "Unassign all capabilities/sets", - "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", - "keycloak.records.confirmationLabel": "Create user records in Keycloak", - "keycloak.records.checking": "Checking Keycloak records for the selected users", - "crud.duplicate": "Duplicate", - "crud.duplicateRole": "Duplicate role?", - "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", - "applications.notAvailable": "Select application is not available", + "form.unassignAllCapabilities": "Alle mogelijkheden/sets verwijderen", + "keycloak.records.creation": "This operation will create new records in Keycloak for the following gebruikers: {users}", + "keycloak.records.confirmationLabel": "Gebruikersrecords aanmaken in Keycloak", + "keycloak.records.checking": "Keycloak records voor de geselecteerde gebruikers controleren", + "crud.duplicate": "Dupliceren", + "crud.duplicateRole": "Rol Dupliceren?", + "crud.duplicateConfirmation": "Weet u zeker dat u een duplicaat van deze rol wilt maken ?{name}?", + "applications.notAvailable": "De toepassing Selecteren is niet beschikbaar", "crud.duplicate.suffix": "(dupliceren)", - "details.centrallyManaged": "Centrally managed", - "filter.true": "Yes", - "filter.false": "No", - "shareToAll": "Share to all", - "keycloak.records.created": "Keycloak records created for the following users: {users}", - "policy.share.success": "Policy has been shared successfully", - "policy.share.error": "Policy could not be shared", - "role.create.success": "Role has been created successfully", - "role.create.error": "Role could not be created", - "role.delete.success": "Role has been deleted successfully", - "role.delete.error": "Role could not be deleted", - "role.edit.error": "Role could not be updated", - "role.edit.success": "Role has been updated successfully", - "role.share.error": "Role could not be shared", - "role.share.success": "Role has been shared successfully", - "columns.type": "Type", - "role.type.default": "Default", - "role.type.support": "Support", - "role.type.regular": "Regular", + "details.centrallyManaged": "Centraal Beheren", + "filter.true": "Ja", + "filter.false": "Geen", + "shareToAll": "Deel met iedereen", + "keycloak.records.created": "Keycloak records created for the following gebruikers: {users}", + "policy.share.success": "Het beleid is met succes gedeeld", + "policy.share.error": "Beleid kon niet worden gedeeld", + "role.create.success": "Rol is succesvol aangemaakt", + "role.create.error": "Rol kon niet worden aangemaakt", + "role.delete.success": "Rol is succesvol verwijderd", + "role.delete.error": "Rol kon niet worden verwijderd", + "role.edit.error": "Rol kon niet worden geüpdatet", + "role.edit.success": "Rol is succesvol geüpdatet", + "role.share.error": "Rol kon niet worden gedeeld", + "role.share.success": "Rol is succesvol gedeeld", + "columns.type": "Soorten", + "role.type.default": "Standaard", + "role.type.support": "Ondersteuning", + "role.type.regular": "Regelmatig", "role.type.consortium": "Consortium", - "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "assignUnassign.modal.label": "Gebruiker selecteren ({tenant})", + "columns.header.checkbox": "Alles selecteren {action} mogelijkheden" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/pl.json b/translations/stripes-authorization-components/pl.json index 1bb89aa..008e002 100644 --- a/translations/stripes-authorization-components/pl.json +++ b/translations/stripes-authorization-components/pl.json @@ -13,29 +13,29 @@ "details.data": "Data", "details.settings": "Settings", "details.procedural": "Procedural", - "search": "Search", - "new": "New", + "search": "Szukaj", + "new": "Nowy", "columns.name": "Nazwa", "columns.description": "Opis", - "columns.updatedDate": "Updated", + "columns.updatedDate": "Zaktualizowano", "columns.updatedBy": "Zaktualizowane przez", - "assignedUsers": "Assigned users", + "assignedUsers": "Przypisani użytkownicy", "assignUnassign": "Przypisz/Odpisz", - "generalInformation": "General Information", + "generalInformation": "Informacje ogólne", "details.capabilities": "Capabilities", "details.capabilitySets": "Capability sets", - "details.nonSinglePolicyText": "* indicates one or more policies are set for the action.", - "crud.createRole": "Create role", - "crud.deleteRole": "Delete role", - "crud.deleteRoleConfirmation": "{rolename} will be deleted", - "crud.editRole": "Edit role", + "details.nonSinglePolicyText": "* oznacza, że dla akcji ustawiono jedną lub więcej reguł.", + "crud.createRole": "Utwórz rolę", + "crud.deleteRole": "Usuń rolę", + "crud.deleteRoleConfirmation": "{rolename} zostanie usunięta", + "crud.editRole": "Edytuj rolę", "crud.cancel": "Anuluj", "crud.selectApplication": "Select application", "crud.edit": "Edytuj", "crud.delete": "Usuń", "form.labels.name": "Nazwa", "form.labels.description": "Opis", - "form.labels.multiplePolicies": "Multiple policies on action {action}", + "form.labels.multiplePolicies": "Wiele reguł dotyczących akcji {action}", "applications.select": "Select application", "applications.found": "{count} applications found", "applications.totalSelected": "Total selected: {count}", @@ -51,7 +51,7 @@ "form.labels.policyType.role": "Rola", "form.labels.sourceType.user": "Użytkownik", "form.labels.sourceType.system": "System", - "form.labels.sourceType.consortium": "Consortium", + "form.labels.sourceType.consortium": "Konsorcjum", "policy.edit": "Edit policy", "policy.create": "Create policy", "policy.create.success": "Policy has been created successfully", @@ -86,7 +86,7 @@ "role.type.default": "Default", "role.type.support": "Support", "role.type.regular": "Regular", - "role.type.consortium": "Consortium", + "role.type.consortium": "Konsorcjum", "assignUnassign.modal.label": "Select user ({tenant})", "columns.header.checkbox": "Zaznacz wszystkie {action} możliwości" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/uk.json b/translations/stripes-authorization-components/uk.json index c1baf3c..72aba0c 100644 --- a/translations/stripes-authorization-components/uk.json +++ b/translations/stripes-authorization-components/uk.json @@ -16,7 +16,7 @@ "search": "Search", "new": "New", "columns.name": "Name", - "columns.description": "Description", + "columns.description": "Опис", "columns.updatedDate": "Updated", "columns.updatedBy": "Оновлено за", "assignedUsers": "Assigned users", @@ -34,7 +34,7 @@ "crud.edit": "Edit", "crud.delete": "Delete", "form.labels.name": "Name", - "form.labels.description": "Description", + "form.labels.description": "Опис", "form.labels.multiplePolicies": "Multiple policies on action {action}", "applications.select": "Select application", "applications.found": "{count} applications found", @@ -43,7 +43,7 @@ "role-details.accordion-users.columns.fullName": "Name", "role-details.accordion-users.columns.patronGroup": "Patron group", "policyDetails.columns.name": "Name", - "policyDetails.columns.description": "Description", + "policyDetails.columns.description": "Опис", "form.labels.policyType": "Policy type", "form.labels.sourceType": "Source type", "form.labels.policyType.user": "User", diff --git a/translations/stripes-authorization-components/zh_CN.json b/translations/stripes-authorization-components/zh_CN.json index 35d0c87..3d3fd59 100644 --- a/translations/stripes-authorization-components/zh_CN.json +++ b/translations/stripes-authorization-components/zh_CN.json @@ -38,7 +38,7 @@ "form.labels.multiplePolicies": "针对操作{action}的多项政策", "applications.select": "选择应用", "applications.found": "找到{count}个应用", - "applications.totalSelected": "已选总数: {count}", + "applications.totalSelected": "已选总数 : {count}", "applications": "应用", "role-details.accordion-users.columns.fullName": "名称", "role-details.accordion-users.columns.patronGroup": "读者组", @@ -59,19 +59,19 @@ "policy.create.error": "政策无法创建", "policy.edit.error": "政策无法更新", "form.unassignAllCapabilities": "取消分配所有功能/集", - "keycloak.records.creation": "此操作将在 Keycloak 中为以下用户创建新记录: {users}", + "keycloak.records.creation": "此操作将在 Keycloak 中为以下用户创建新记录 : {users}", "keycloak.records.confirmationLabel": "在 Keycloak 中创建用户记录", "keycloak.records.checking": "检查选定用户的 Keycloak 记录", "crud.duplicate": "复制", "crud.duplicateRole": "复制角色?", "crud.duplicateConfirmation": "确定要创建此角色 {name} 的副本吗?", "applications.notAvailable": "选择的应用程序不可用", - "crud.duplicate.suffix": "(复制)", + "crud.duplicate.suffix": " : 复制 ) ", "details.centrallyManaged": "集中管理", "filter.true": "是", "filter.false": "否", "shareToAll": "分享给所有人", - "keycloak.records.created": "为以下用户创建了 Keycloak 记录: {users}", + "keycloak.records.created": "为以下用户创建了 Keycloak 记录 : {users}", "policy.share.success": "政策共享成功", "policy.share.error": "政策无法共享", "role.create.success": "角色创建成功", @@ -88,5 +88,5 @@ "role.type.regular": "定期", "role.type.consortium": "联盟", "assignUnassign.modal.label": "选择用户 ( {tenant} )", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "选择所有{action}功能" } \ No newline at end of file From 9633f3dae139cecdef27ecdd0faa815129863ce2 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Tue, 29 Apr 2025 14:29:32 -0400 Subject: [PATCH 06/59] Update translation strings --- .../stripes-authorization-components/ar.json | 32 +++++++++---------- .../stripes-authorization-components/pl.json | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/translations/stripes-authorization-components/ar.json b/translations/stripes-authorization-components/ar.json index d334e0f..6fa7a1f 100644 --- a/translations/stripes-authorization-components/ar.json +++ b/translations/stripes-authorization-components/ar.json @@ -14,16 +14,16 @@ "details.settings": "الإعدادات", "details.procedural": "إجرائي", "search": "Search", - "new": "New", + "new": "جديد", "columns.name": "الاسم", "columns.description": "الوصف", "columns.updatedDate": "تم التحديث", "columns.updatedBy": "تم التحديث بواسطة", "assignedUsers": "المستخدمين المعينين", "assignUnassign": "تعيين/إلغاء تعيين", - "generalInformation": "General Information", - "details.capabilities": "Capabilities", - "details.capabilitySets": "Capability sets", + "generalInformation": "معلومات عامة", + "details.capabilities": "قدرات", + "details.capabilitySets": "مجموعات القدرات", "details.nonSinglePolicyText": "* يشير إلى أنه تم تعيين سياسة واحدة أو أكثر للإجراء.", "crud.createRole": "إنشاء دور", "crud.deleteRole": "حذف الدور", @@ -33,9 +33,9 @@ "crud.selectApplication": "حدد التطبيق", "crud.edit": "تحرير", "crud.delete": "حذف", - "form.labels.name": "Name", - "form.labels.description": "Description", - "form.labels.multiplePolicies": "Multiple policies on action {action}", + "form.labels.name": "الاسم", + "form.labels.description": "الوصف", + "form.labels.multiplePolicies": "سياسات متعددة بشأن الإجراء {action}", "applications.select": "حدد التطبيق", "applications.found": "تم العثور على {count} تطبيقاً", "applications.totalSelected": "إجمالي المحدد: {count}", @@ -44,14 +44,14 @@ "role-details.accordion-users.columns.patronGroup": "مجموعة المستفيدين", "policyDetails.columns.name": "Name", "policyDetails.columns.description": "Description", - "form.labels.policyType": "Policy type", - "form.labels.sourceType": "Source type", - "form.labels.policyType.user": "User", - "form.labels.policyType.time": "Time", + "form.labels.policyType": "نوع السياسة", + "form.labels.sourceType": "نوع المصدر", + "form.labels.policyType.user": "المستخدم", + "form.labels.policyType.time": "الوقت", "form.labels.policyType.role": "الدور", - "form.labels.sourceType.user": "User", + "form.labels.sourceType.user": "المستخدم", "form.labels.sourceType.system": "النظام", - "form.labels.sourceType.consortium": "Consortium", + "form.labels.sourceType.consortium": "اتحاد المكتبات", "policy.edit": "تحرير السياسة", "policy.create": "Create policy", "policy.create.success": "تم إنشاء السياسة بنجاح", @@ -67,9 +67,9 @@ "crud.duplicateConfirmation": "هل أنت متأكد أنك تريد إنشاء نسخة مكررة من هذا الدور {name}؟", "applications.notAvailable": "تم تحديد التطبيق غير متاح", "crud.duplicate.suffix": "(تكرار)", - "details.centrallyManaged": "Centrally managed", - "filter.true": "Yes", - "filter.false": "No", + "details.centrallyManaged": "مدار مركزياً", + "filter.true": "نعم", + "filter.false": "لا", "shareToAll": "Share to all", "keycloak.records.created": "تم إنشاء تسجيلات Keycloak للمستخدمين التاليين: {users}", "policy.share.success": "تمت مشاركة السياسة بنجاح", diff --git a/translations/stripes-authorization-components/pl.json b/translations/stripes-authorization-components/pl.json index 008e002..815963b 100644 --- a/translations/stripes-authorization-components/pl.json +++ b/translations/stripes-authorization-components/pl.json @@ -41,7 +41,7 @@ "applications.totalSelected": "Total selected: {count}", "applications": "Applications", "role-details.accordion-users.columns.fullName": "Nazwa", - "role-details.accordion-users.columns.patronGroup": "Grupa czytelnika", + "role-details.accordion-users.columns.patronGroup": "Grupa czytelników", "policyDetails.columns.name": "Nazwa", "policyDetails.columns.description": "Opis", "form.labels.policyType": "Policy type", From 8e6ad6bbaedf58d1afea6e9d28d3db7072b9ef12 Mon Sep 17 00:00:00 2001 From: Azizjon Nurov Date: Wed, 30 Apr 2025 21:41:27 +0500 Subject: [PATCH 07/59] UISAUTHCOM-60 Make role details users links clickable only in settings (#92) * Make role details users links clickable only in settings * Add new isClickableRolesUsers prop instead * Rename isClickableRolesUsers to isUserLinkEnabled * Add hideUserLink instead to preserve existing functionality * Update change log * Add tests * Remove hideUserLink default value from RoleDetails * Format change log --- CHANGELOG.md | 2 ++ lib/RoleDetails/RoleDetails.js | 3 ++ .../RoleDetailsUsersAccordion.js | 9 ++++-- .../RoleDetailsUsersAccordion.test.js | 32 +++++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fac181..17ae4d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Change history for stripes-authorization-components +* [UISAUTHCOM-60](https://folio-org.atlassian.net/browse/UISAUTHCOM-60) Add new `hideUserLink` prop to `RoleDetails` component that will display users in assigned users list as a text if enabled. + # [2.0.2](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.2) * [UISAUTHCOM-51](https://folio-org.atlassian.net/browse/UISAUTHCOM-51) Provide `expand=false` parameter to `useRoleCapabilities` that used in `useInitalRoleSharing` to correctly retrieve directly assigned capabilities diff --git a/lib/RoleDetails/RoleDetails.js b/lib/RoleDetails/RoleDetails.js index d2ee273..b6dcce5 100644 --- a/lib/RoleDetails/RoleDetails.js +++ b/lib/RoleDetails/RoleDetails.js @@ -51,6 +51,7 @@ import css from './style.css'; export const RoleDetails = ({ displayShareAction = false, + hideUserLink, isLoading: isLoadingProp = false, onDuplicate = noop, path, @@ -227,6 +228,7 @@ export const RoleDetails = ({ /> @@ -281,6 +283,7 @@ export const RoleDetails = ({ RoleDetails.propTypes = { displayShareAction: PropTypes.bool, + hideUserLink: PropTypes.bool, isLoading: PropTypes.bool, onDuplicate: PropTypes.func.isRequired, path: PropTypes.string.isRequired, diff --git a/lib/RoleDetails/RoleDetailsUsersAccordion/RoleDetailsUsersAccordion.js b/lib/RoleDetails/RoleDetailsUsersAccordion/RoleDetailsUsersAccordion.js index b7060c9..bb0991b 100644 --- a/lib/RoleDetails/RoleDetailsUsersAccordion/RoleDetailsUsersAccordion.js +++ b/lib/RoleDetails/RoleDetailsUsersAccordion/RoleDetailsUsersAccordion.js @@ -20,7 +20,11 @@ import { } from '../../hooks'; import { RoleDetailsAssignUsers } from '../RoleDetailsAssignUsers/RoleDetailsAssignUsers'; -export const RoleDetailsUsersAccordion = ({ roleId, tenantId }) => { +export const RoleDetailsUsersAccordion = ({ + hideUserLink = false, + roleId, + tenantId, +}) => { const { formatMessage } = useIntl(); const { users, isLoading: usersIsLoading, refetch } = useUsersByRoleId(roleId, { tenantId }); @@ -44,7 +48,7 @@ export const RoleDetailsUsersAccordion = ({ roleId, tenantId }) => { }); const usersData = sortedUsers.map(i => { - const fullName = (); + const fullName = hideUserLink ? getFullName(i) : (); const patronGroup = groupHash[i.patronGroup]?.group || ; return { @@ -108,6 +112,7 @@ export const RoleDetailsUsersAccordion = ({ roleId, tenantId }) => { }; RoleDetailsUsersAccordion.propTypes = { + hideUserLink: PropTypes.string.isRequired, roleId: PropTypes.string.isRequired, tenantId: PropTypes.string, }; diff --git a/lib/RoleDetails/RoleDetailsUsersAccordion/RoleDetailsUsersAccordion.test.js b/lib/RoleDetails/RoleDetailsUsersAccordion/RoleDetailsUsersAccordion.test.js index d943e51..2f8229c 100644 --- a/lib/RoleDetails/RoleDetailsUsersAccordion/RoleDetailsUsersAccordion.test.js +++ b/lib/RoleDetails/RoleDetailsUsersAccordion/RoleDetailsUsersAccordion.test.js @@ -83,4 +83,36 @@ describe('RoleDetailsUsersAccordion', () => { getByText('Loading'); }); + + it('renders user full name as plain text when hideUserLink is true', () => { + useUsersByRoleId.mockReturnValue({ users, isLoading: false }); + useUserGroups.mockReturnValue({ userGroups, isLoading: false }); + + const { getByText } = render( + renderWithRouter( + + + + ) + ); + + const text = getByText(users[0].personal.firstName, { exact: false }); + expect(text.tagName).not.toBe('A'); + }); + + it('renders user full name as a link when hideUserLink is false', () => { + useUsersByRoleId.mockReturnValue({ users, isLoading: false }); + useUserGroups.mockReturnValue({ userGroups, isLoading: false }); + + const { getByText } = render( + renderWithRouter( + + + + ) + ); + + const link = getByText(users[0].personal.firstName, { exact: false }); + expect(link.tagName).toBe('A'); + }); }); From cd5cc3b61c87f3d9eddfa7c9d985d848bd177a6f Mon Sep 17 00:00:00 2001 From: aidynoJ <121284650+aidynoJ@users.noreply.github.com> Date: Wed, 30 Apr 2025 23:23:40 +0500 Subject: [PATCH 08/59] UISAUTHCOM-59: Increase request timeouts in useCreateRoleMutation, useEditRoleMutation (#91) Co-authored-by: Zak Burke --- CHANGELOG.md | 3 +++ lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js | 2 +- lib/hooks/useEditRoleMutation/useEditRoleMutation.js | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17ae4d1..7252f3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Change history for stripes-authorization-components +# [2.1.0] In Progress + * [UISAUTHCOM-60](https://folio-org.atlassian.net/browse/UISAUTHCOM-60) Add new `hideUserLink` prop to `RoleDetails` component that will display users in assigned users list as a text if enabled. +* [UISAUTHCOM-59](https://folio-org.atlassian.net/browse/UISAUTHCOM-59) Increase request timeout in `useCreateRoleMutation`, `useEditRoleMutation`. # [2.0.2](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.2) diff --git a/lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js b/lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js index 14a1f6d..46e58bf 100644 --- a/lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js +++ b/lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js @@ -11,7 +11,7 @@ import { export const useCreateRoleMutation = (roleCapabilitiesListIds, capabilitySetListIds, handleError, options = {}) => { const { tenantId } = options; - const ky = useOkapiKy({ tenant: tenantId }); + const ky = useOkapiKy({ tenant: tenantId, timeout: 180000 }); const queryClient = useQueryClient(); const [namespace] = useNamespace(); const { mutateAsync, isLoading } = useMutation({ diff --git a/lib/hooks/useEditRoleMutation/useEditRoleMutation.js b/lib/hooks/useEditRoleMutation/useEditRoleMutation.js index 9b34359..8806661 100644 --- a/lib/hooks/useEditRoleMutation/useEditRoleMutation.js +++ b/lib/hooks/useEditRoleMutation/useEditRoleMutation.js @@ -6,7 +6,7 @@ export const useEditRoleMutation = ( { roleCapabilitiesListIds, shouldUpdateCapabilities, shouldUpdateCapabilitySets, roleCapabilitySetsListIds }, { handleError, tenantId } ) => { - const ky = useOkapiKy({ tenant: tenantId }); + const ky = useOkapiKy({ tenant: tenantId, timeout: 180000 }); const queryClient = useQueryClient(); const [namespace] = useNamespace(); const { mutateAsync, isLoading } = useMutation({ From eaaa11db9dfb51806d086c877b681b9263e3201d Mon Sep 17 00:00:00 2001 From: Ryan Berger Date: Mon, 12 May 2025 13:33:45 -0400 Subject: [PATCH 09/59] UISAUTHCOM-59 Increase default timeout for create role API calls to minutes (#95) --- CHANGELOG.md | 2 +- lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7252f3c..1571d7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ # [2.1.0] In Progress * [UISAUTHCOM-60](https://folio-org.atlassian.net/browse/UISAUTHCOM-60) Add new `hideUserLink` prop to `RoleDetails` component that will display users in assigned users list as a text if enabled. -* [UISAUTHCOM-59](https://folio-org.atlassian.net/browse/UISAUTHCOM-59) Increase request timeout in `useCreateRoleMutation`, `useEditRoleMutation`. +* [UISAUTHCOM-59](https://folio-org.atlassian.net/browse/UISAUTHCOM-59) Increase request timeout in `useCreateRoleMutation`, `useEditRoleMutation` from default 30 seconds to 10 minutes. This can be decreased if back-end performance improves. # [2.0.2](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.2) diff --git a/lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js b/lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js index 46e58bf..7503921 100644 --- a/lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js +++ b/lib/hooks/useCreateRoleMutation/useCreateRoleMutation.js @@ -8,10 +8,13 @@ import { useOkapiKy, } from '@folio/stripes/core'; +// Currently set to 10 minutes, but this can be reduced if back-end response times are improved. +const REQUEST_TIMEOUT = 600000; + export const useCreateRoleMutation = (roleCapabilitiesListIds, capabilitySetListIds, handleError, options = {}) => { const { tenantId } = options; - const ky = useOkapiKy({ tenant: tenantId, timeout: 180000 }); + const ky = useOkapiKy({ tenant: tenantId }); const queryClient = useQueryClient(); const [namespace] = useNamespace(); const { mutateAsync, isLoading } = useMutation({ @@ -19,10 +22,10 @@ export const useCreateRoleMutation = (roleCapabilitiesListIds, capabilitySetList onSuccess: async (newRole) => { await queryClient.invalidateQueries(namespace); if (roleCapabilitiesListIds.length > 0) { - await ky.post('roles/capabilities', { json: { roleId: newRole.id, capabilityIds: roleCapabilitiesListIds } }).json(); + await ky.post('roles/capabilities', { json: { roleId: newRole.id, capabilityIds: roleCapabilitiesListIds }, timeout: REQUEST_TIMEOUT }).json(); } if (capabilitySetListIds.length > 0) { - await ky.post('roles/capability-sets', { json: { roleId: newRole.id, capabilitySetIds: capabilitySetListIds } }).json(); + await ky.post('roles/capability-sets', { json: { roleId: newRole.id, capabilitySetIds: capabilitySetListIds }, timeout: REQUEST_TIMEOUT }).json(); } }, onError: handleError, From 219403efa67ea8dc9e363904e0d94129772ac389 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Mon, 12 May 2025 20:07:03 -0400 Subject: [PATCH 10/59] Update translation strings --- .../stripes-authorization-components/pl.json | 26 +++++++++---------- .../stripes-authorization-components/uk.json | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/translations/stripes-authorization-components/pl.json b/translations/stripes-authorization-components/pl.json index 815963b..b333e33 100644 --- a/translations/stripes-authorization-components/pl.json +++ b/translations/stripes-authorization-components/pl.json @@ -22,24 +22,24 @@ "assignedUsers": "Przypisani użytkownicy", "assignUnassign": "Przypisz/Odpisz", "generalInformation": "Informacje ogólne", - "details.capabilities": "Capabilities", - "details.capabilitySets": "Capability sets", + "details.capabilities": "Możliwości", + "details.capabilitySets": "Zestawy możliwości", "details.nonSinglePolicyText": "* oznacza, że dla akcji ustawiono jedną lub więcej reguł.", "crud.createRole": "Utwórz rolę", "crud.deleteRole": "Usuń rolę", "crud.deleteRoleConfirmation": "{rolename} zostanie usunięta", "crud.editRole": "Edytuj rolę", "crud.cancel": "Anuluj", - "crud.selectApplication": "Select application", + "crud.selectApplication": "Wybierz aplikację", "crud.edit": "Edytuj", "crud.delete": "Usuń", "form.labels.name": "Nazwa", "form.labels.description": "Opis", "form.labels.multiplePolicies": "Wiele reguł dotyczących akcji {action}", - "applications.select": "Select application", - "applications.found": "{count} applications found", - "applications.totalSelected": "Total selected: {count}", - "applications": "Applications", + "applications.select": "Wybierz aplikację", + "applications.found": "Znaleziono {count} aplikacji", + "applications.totalSelected": "Łącznie wybrano: {count}", + "applications": "Aplikacje", "role-details.accordion-users.columns.fullName": "Nazwa", "role-details.accordion-users.columns.patronGroup": "Grupa czytelników", "policyDetails.columns.name": "Nazwa", @@ -54,18 +54,18 @@ "form.labels.sourceType.consortium": "Konsorcjum", "policy.edit": "Edit policy", "policy.create": "Create policy", - "policy.create.success": "Policy has been created successfully", - "policy.edit.success": "Policy has been updated successfully", - "policy.create.error": "Policy could not be created", - "policy.edit.error": "Policy could not be updated", - "form.unassignAllCapabilities": "Unassign all capabilities/sets", + "policy.create.success": "Reguła została pomyślnie utworzona", + "policy.edit.success": "Reguła została pomyślnie zaktualizowana", + "policy.create.error": "Nie można utworzyć reguły", + "policy.edit.error": "Nie można zaktualizować reguły", + "form.unassignAllCapabilities": "Anuluj przypisanie wszystkich możliwości/zestawów", "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", "keycloak.records.confirmationLabel": "Create user records in Keycloak", "keycloak.records.checking": "Checking Keycloak records for the selected users", "crud.duplicate": "Duplicate", "crud.duplicateRole": "Duplicate role?", "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", - "applications.notAvailable": "Select application is not available", + "applications.notAvailable": "Wybrana aplikacja jest niedostępna", "crud.duplicate.suffix": "(duplicate)", "details.centrallyManaged": "Centrally managed", "filter.true": "Tak", diff --git a/translations/stripes-authorization-components/uk.json b/translations/stripes-authorization-components/uk.json index 72aba0c..bb4cd34 100644 --- a/translations/stripes-authorization-components/uk.json +++ b/translations/stripes-authorization-components/uk.json @@ -13,8 +13,8 @@ "details.data": "Data", "details.settings": "Settings", "details.procedural": "Procedural", - "search": "Search", - "new": "New", + "search": "Пошук", + "new": "Нове", "columns.name": "Name", "columns.description": "Опис", "columns.updatedDate": "Updated", From 77a001346c86d9d38b8a6a6505ab95ae6269d5b9 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Fri, 6 Jun 2025 21:30:43 -0400 Subject: [PATCH 11/59] Update translation strings --- .../stripes-authorization-components/pl.json | 14 ++--- .../stripes-authorization-components/uk.json | 56 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/translations/stripes-authorization-components/pl.json b/translations/stripes-authorization-components/pl.json index b333e33..c8ff8c7 100644 --- a/translations/stripes-authorization-components/pl.json +++ b/translations/stripes-authorization-components/pl.json @@ -45,15 +45,15 @@ "policyDetails.columns.name": "Nazwa", "policyDetails.columns.description": "Opis", "form.labels.policyType": "Policy type", - "form.labels.sourceType": "Source type", + "form.labels.sourceType": "Typ zasobu", "form.labels.policyType.user": "Użytkownik", - "form.labels.policyType.time": "Time", + "form.labels.policyType.time": "Czas", "form.labels.policyType.role": "Rola", "form.labels.sourceType.user": "Użytkownik", "form.labels.sourceType.system": "System", "form.labels.sourceType.consortium": "Konsorcjum", - "policy.edit": "Edit policy", - "policy.create": "Create policy", + "policy.edit": "Edytuj regułę", + "policy.create": "Utwórz regułę", "policy.create.success": "Reguła została pomyślnie utworzona", "policy.edit.success": "Reguła została pomyślnie zaktualizowana", "policy.create.error": "Nie można utworzyć reguły", @@ -72,15 +72,15 @@ "filter.false": "Nie", "shareToAll": "Share to all", "keycloak.records.created": "Keycloak records created for the following users: {users}", - "policy.share.success": "Policy has been shared successfully", - "policy.share.error": "Policy could not be shared", + "policy.share.success": "Reguła została pomyślnie udostępniona", + "policy.share.error": "Nie można udostępnić reguły", "role.create.success": "Role has been created successfully", "role.create.error": "Role could not be created", "role.delete.success": "Role has been deleted successfully", "role.delete.error": "Role could not be deleted", "role.edit.error": "Role could not be updated", "role.edit.success": "Role has been updated successfully", - "role.share.error": "Role could not be shared", + "role.share.error": "Nie można udostępnić roli", "role.share.success": "Role has been shared successfully", "columns.type": "Rodzaj", "role.type.default": "Default", diff --git a/translations/stripes-authorization-components/uk.json b/translations/stripes-authorization-components/uk.json index bb4cd34..d78c0b9 100644 --- a/translations/stripes-authorization-components/uk.json +++ b/translations/stripes-authorization-components/uk.json @@ -19,9 +19,9 @@ "columns.description": "Опис", "columns.updatedDate": "Updated", "columns.updatedBy": "Оновлено за", - "assignedUsers": "Assigned users", - "assignUnassign": "Assign/Unassign", - "generalInformation": "General Information", + "assignedUsers": "Призначені користувачі", + "assignUnassign": "Призначити/Скасувати призначення", + "generalInformation": "Загальна інформація", "details.capabilities": "Capabilities", "details.capabilitySets": "Capability sets", "details.nonSinglePolicyText": "* indicates one or more policies are set for the action.", @@ -33,31 +33,31 @@ "crud.selectApplication": "Select application", "crud.edit": "Edit", "crud.delete": "Delete", - "form.labels.name": "Name", + "form.labels.name": "Ім'я/Назва", "form.labels.description": "Опис", - "form.labels.multiplePolicies": "Multiple policies on action {action}", - "applications.select": "Select application", - "applications.found": "{count} applications found", - "applications.totalSelected": "Total selected: {count}", - "applications": "Applications", + "form.labels.multiplePolicies": "Кілька політик щодо дії {action}", + "applications.select": "Вибрати додаток", + "applications.found": "{count} додатків знайдено", + "applications.totalSelected": "Всього вибрано: {count}", + "applications": "Додатки", "role-details.accordion-users.columns.fullName": "Name", "role-details.accordion-users.columns.patronGroup": "Patron group", - "policyDetails.columns.name": "Name", + "policyDetails.columns.name": "Ім'я/Назва", "policyDetails.columns.description": "Опис", - "form.labels.policyType": "Policy type", - "form.labels.sourceType": "Source type", - "form.labels.policyType.user": "User", - "form.labels.policyType.time": "Time", - "form.labels.policyType.role": "Role", - "form.labels.sourceType.user": "User", - "form.labels.sourceType.system": "System", - "form.labels.sourceType.consortium": "Consortium", - "policy.edit": "Edit policy", - "policy.create": "Create policy", - "policy.create.success": "Policy has been created successfully", - "policy.edit.success": "Policy has been updated successfully", - "policy.create.error": "Policy could not be created", - "policy.edit.error": "Policy could not be updated", + "form.labels.policyType": "Тип політики", + "form.labels.sourceType": "Тип джерела", + "form.labels.policyType.user": "Користувач", + "form.labels.policyType.time": "Час", + "form.labels.policyType.role": "Роль", + "form.labels.sourceType.user": "Користувач", + "form.labels.sourceType.system": "Система", + "form.labels.sourceType.consortium": "Консорціум", + "policy.edit": "Редагувати політику", + "policy.create": "Створити політику", + "policy.create.success": "Політику успішно створено", + "policy.edit.success": "Політику успішно оновлено", + "policy.create.error": "Не вдалося створити політику", + "policy.edit.error": "Не вдалося оновити політику", "form.unassignAllCapabilities": "Unassign all capabilities/sets", "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", "keycloak.records.confirmationLabel": "Create user records in Keycloak", @@ -65,15 +65,15 @@ "crud.duplicate": "Duplicate", "crud.duplicateRole": "Duplicate role?", "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", - "applications.notAvailable": "Select application is not available", + "applications.notAvailable": "Вибраний додаток недоступний", "crud.duplicate.suffix": "(duplicate)", "details.centrallyManaged": "Centrally managed", "filter.true": "Yes", "filter.false": "No", "shareToAll": "Share to all", "keycloak.records.created": "Keycloak records created for the following users: {users}", - "policy.share.success": "Policy has been shared successfully", - "policy.share.error": "Policy could not be shared", + "policy.share.success": "Політика була успішно поширена", + "policy.share.error": "Політика не може бути поширена", "role.create.success": "Role has been created successfully", "role.create.error": "Role could not be created", "role.delete.success": "Role has been deleted successfully", @@ -87,6 +87,6 @@ "role.type.support": "Support", "role.type.regular": "Regular", "role.type.consortium": "Consortium", - "assignUnassign.modal.label": "Select user ({tenant})", + "assignUnassign.modal.label": "Вибрати користувача ({tenant})", "columns.header.checkbox": "Select all {action} capabilities" } \ No newline at end of file From b88dd4b0b1a9cba29fdc1b18ed1506781e319f14 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Tue, 24 Jun 2025 14:46:01 -0400 Subject: [PATCH 12/59] Update translation strings --- .../stripes-authorization-components/uk.json | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/translations/stripes-authorization-components/uk.json b/translations/stripes-authorization-components/uk.json index d78c0b9..cf60885 100644 --- a/translations/stripes-authorization-components/uk.json +++ b/translations/stripes-authorization-components/uk.json @@ -1,38 +1,38 @@ { - "columns.application": "Application", - "columns.resource": "Resource", - "columns.view": "View", - "columns.edit": "Edit", - "columns.create": "Create", - "columns.delete": "Delete", - "columns.manage": "Manage", - "columns.policies": "Policies", - "columns.settings": "Settings/Settings sets", - "columns.procedure": "Procedure", - "columns.execute": "Execute", - "details.data": "Data", - "details.settings": "Settings", - "details.procedural": "Procedural", + "columns.application": "Додаток", + "columns.resource": "Ресурс", + "columns.view": "Переглянути", + "columns.edit": "Редагувати", + "columns.create": "Створити", + "columns.delete": "Видалити", + "columns.manage": "Керувати", + "columns.policies": "Політики", + "columns.settings": "Налаштування/Набори налаштувань", + "columns.procedure": "Процедура", + "columns.execute": "Виконати", + "details.data": "Дані", + "details.settings": "Налаштування", + "details.procedural": "Службове", "search": "Пошук", "new": "Нове", - "columns.name": "Name", + "columns.name": "Ім'я/Назва", "columns.description": "Опис", - "columns.updatedDate": "Updated", + "columns.updatedDate": "Оновлено", "columns.updatedBy": "Оновлено за", "assignedUsers": "Призначені користувачі", "assignUnassign": "Призначити/Скасувати призначення", "generalInformation": "Загальна інформація", - "details.capabilities": "Capabilities", - "details.capabilitySets": "Capability sets", - "details.nonSinglePolicyText": "* indicates one or more policies are set for the action.", - "crud.createRole": "Create role", - "crud.deleteRole": "Delete role", - "crud.deleteRoleConfirmation": "{rolename} will be deleted", + "details.capabilities": "Можливості", + "details.capabilitySets": "Набори можливостей", + "details.nonSinglePolicyText": "* вказує на те, що для дії встановлено одну або декілька політик.", + "crud.createRole": "Створити роль", + "crud.deleteRole": "Видалити роль", + "crud.deleteRoleConfirmation": "{rolename} буде видалено", "crud.editRole": "Edit role", - "crud.cancel": "Cancel", + "crud.cancel": "Скасувати", "crud.selectApplication": "Select application", "crud.edit": "Edit", - "crud.delete": "Delete", + "crud.delete": "Видалити", "form.labels.name": "Ім'я/Назва", "form.labels.description": "Опис", "form.labels.multiplePolicies": "Кілька політик щодо дії {action}", @@ -40,8 +40,8 @@ "applications.found": "{count} додатків знайдено", "applications.totalSelected": "Всього вибрано: {count}", "applications": "Додатки", - "role-details.accordion-users.columns.fullName": "Name", - "role-details.accordion-users.columns.patronGroup": "Patron group", + "role-details.accordion-users.columns.fullName": "Ім'я/Назва", + "role-details.accordion-users.columns.patronGroup": "Група користувача", "policyDetails.columns.name": "Ім'я/Назва", "policyDetails.columns.description": "Опис", "form.labels.policyType": "Тип політики", @@ -62,31 +62,31 @@ "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", "keycloak.records.confirmationLabel": "Create user records in Keycloak", "keycloak.records.checking": "Checking Keycloak records for the selected users", - "crud.duplicate": "Duplicate", + "crud.duplicate": "Дублювати", "crud.duplicateRole": "Duplicate role?", "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", "applications.notAvailable": "Вибраний додаток недоступний", - "crud.duplicate.suffix": "(duplicate)", - "details.centrallyManaged": "Centrally managed", + "crud.duplicate.suffix": "(дублювати)", + "details.centrallyManaged": "Централізоване управління", "filter.true": "Yes", "filter.false": "No", - "shareToAll": "Share to all", + "shareToAll": "Поділитися з усіма", "keycloak.records.created": "Keycloak records created for the following users: {users}", "policy.share.success": "Політика була успішно поширена", "policy.share.error": "Політика не може бути поширена", - "role.create.success": "Role has been created successfully", - "role.create.error": "Role could not be created", - "role.delete.success": "Role has been deleted successfully", - "role.delete.error": "Role could not be deleted", - "role.edit.error": "Role could not be updated", - "role.edit.success": "Role has been updated successfully", - "role.share.error": "Role could not be shared", - "role.share.success": "Role has been shared successfully", - "columns.type": "Type", - "role.type.default": "Default", - "role.type.support": "Support", - "role.type.regular": "Regular", - "role.type.consortium": "Consortium", + "role.create.success": "Роль успішно створена", + "role.create.error": "Неможливо створити роль", + "role.delete.success": "Роль успішно видалена", + "role.delete.error": "Роль не може бути видалена", + "role.edit.error": "Не вдалося оновити роль", + "role.edit.success": "Роль успішно оновлена", + "role.share.error": "Неможливо розподілити роль", + "role.share.success": "Роль була успішно розподілена", + "columns.type": "Тип", + "role.type.default": "За замовчуванням", + "role.type.support": "Підтримка", + "role.type.regular": "Звичайний", + "role.type.consortium": "Консорціум", "assignUnassign.modal.label": "Вибрати користувача ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Вибрати всі можливості {action}" } \ No newline at end of file From fe35b74cc43acc1c780c4f65e0a33cc6163dbecd Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:05:34 -0400 Subject: [PATCH 13/59] Update translation strings --- translations/stripes-authorization-components/uk.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/translations/stripes-authorization-components/uk.json b/translations/stripes-authorization-components/uk.json index cf60885..4cdb83b 100644 --- a/translations/stripes-authorization-components/uk.json +++ b/translations/stripes-authorization-components/uk.json @@ -28,10 +28,10 @@ "crud.createRole": "Створити роль", "crud.deleteRole": "Видалити роль", "crud.deleteRoleConfirmation": "{rolename} буде видалено", - "crud.editRole": "Edit role", + "crud.editRole": "Редагувати роль", "crud.cancel": "Скасувати", - "crud.selectApplication": "Select application", - "crud.edit": "Edit", + "crud.selectApplication": "Вибрати додаток", + "crud.edit": "Редагувати", "crud.delete": "Видалити", "form.labels.name": "Ім'я/Назва", "form.labels.description": "Опис", @@ -63,7 +63,7 @@ "keycloak.records.confirmationLabel": "Create user records in Keycloak", "keycloak.records.checking": "Checking Keycloak records for the selected users", "crud.duplicate": "Дублювати", - "crud.duplicateRole": "Duplicate role?", + "crud.duplicateRole": "Дублювати роль?", "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", "applications.notAvailable": "Вибраний додаток недоступний", "crud.duplicate.suffix": "(дублювати)", From 156a29060d5b2fababb03bf18f23a05bb3672805 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Thu, 17 Jul 2025 22:20:26 -0400 Subject: [PATCH 14/59] Update translation strings --- .../fr_FR.json | 4 ++-- .../stripes-authorization-components/pl.json | 22 +++++++++---------- .../stripes-authorization-components/uk.json | 6 ++--- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/translations/stripes-authorization-components/fr_FR.json b/translations/stripes-authorization-components/fr_FR.json index 4dbba77..f7436dd 100644 --- a/translations/stripes-authorization-components/fr_FR.json +++ b/translations/stripes-authorization-components/fr_FR.json @@ -46,10 +46,10 @@ "policyDetails.columns.description": "Description", "form.labels.policyType": "Policy type", "form.labels.sourceType": "Source type", - "form.labels.policyType.user": "User", + "form.labels.policyType.user": "Utilisateur", "form.labels.policyType.time": "Time", "form.labels.policyType.role": "Role", - "form.labels.sourceType.user": "User", + "form.labels.sourceType.user": "Utilisateur", "form.labels.sourceType.system": "System", "form.labels.sourceType.consortium": "Consortium", "policy.edit": "Edit policy", diff --git a/translations/stripes-authorization-components/pl.json b/translations/stripes-authorization-components/pl.json index c8ff8c7..c4b69da 100644 --- a/translations/stripes-authorization-components/pl.json +++ b/translations/stripes-authorization-components/pl.json @@ -11,8 +11,8 @@ "columns.procedure": "Procedure", "columns.execute": "Execute", "details.data": "Data", - "details.settings": "Settings", - "details.procedural": "Procedural", + "details.settings": "Ustawienia", + "details.procedural": "Proceduralny", "search": "Szukaj", "new": "Nowy", "columns.name": "Nazwa", @@ -22,8 +22,8 @@ "assignedUsers": "Przypisani użytkownicy", "assignUnassign": "Przypisz/Odpisz", "generalInformation": "Informacje ogólne", - "details.capabilities": "Możliwości", - "details.capabilitySets": "Zestawy możliwości", + "details.capabilities": "Uprawnienia", + "details.capabilitySets": "Zestawy uprawnień", "details.nonSinglePolicyText": "* oznacza, że dla akcji ustawiono jedną lub więcej reguł.", "crud.createRole": "Utwórz rolę", "crud.deleteRole": "Usuń rolę", @@ -38,7 +38,7 @@ "form.labels.multiplePolicies": "Wiele reguł dotyczących akcji {action}", "applications.select": "Wybierz aplikację", "applications.found": "Znaleziono {count} aplikacji", - "applications.totalSelected": "Łącznie wybrano: {count}", + "applications.totalSelected": "Wybrano łącznie: {count}", "applications": "Aplikacje", "role-details.accordion-users.columns.fullName": "Nazwa", "role-details.accordion-users.columns.patronGroup": "Grupa czytelników", @@ -62,15 +62,15 @@ "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", "keycloak.records.confirmationLabel": "Create user records in Keycloak", "keycloak.records.checking": "Checking Keycloak records for the selected users", - "crud.duplicate": "Duplicate", - "crud.duplicateRole": "Duplicate role?", - "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", + "crud.duplicate": "Powielić", + "crud.duplicateRole": "Powielić rolę?", + "crud.duplicateConfirmation": "Czy na pewno chcesz utworzyć duplikat tej roli {name}?", "applications.notAvailable": "Wybrana aplikacja jest niedostępna", - "crud.duplicate.suffix": "(duplicate)", - "details.centrallyManaged": "Centrally managed", + "crud.duplicate.suffix": "powielić", + "details.centrallyManaged": "Zarządzane centralnie", "filter.true": "Tak", "filter.false": "Nie", - "shareToAll": "Share to all", + "shareToAll": "Udostępnij wszystkim", "keycloak.records.created": "Keycloak records created for the following users: {users}", "policy.share.success": "Reguła została pomyślnie udostępniona", "policy.share.error": "Nie można udostępnić reguły", diff --git a/translations/stripes-authorization-components/uk.json b/translations/stripes-authorization-components/uk.json index 4cdb83b..1b82505 100644 --- a/translations/stripes-authorization-components/uk.json +++ b/translations/stripes-authorization-components/uk.json @@ -58,9 +58,9 @@ "policy.edit.success": "Політику успішно оновлено", "policy.create.error": "Не вдалося створити політику", "policy.edit.error": "Не вдалося оновити політику", - "form.unassignAllCapabilities": "Unassign all capabilities/sets", - "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", - "keycloak.records.confirmationLabel": "Create user records in Keycloak", + "form.unassignAllCapabilities": "Скасувати призначення всіх можливостей/наборів", + "keycloak.records.creation": "Ця операція створить нові записи в Keycloak для таких користувачів: {users}", + "keycloak.records.confirmationLabel": "Створити записи користувачів у Keycloak", "keycloak.records.checking": "Checking Keycloak records for the selected users", "crud.duplicate": "Дублювати", "crud.duplicateRole": "Дублювати роль?", From e95cdb419b7eb64f0f6259a7e534c71dc6e7dd59 Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Sat, 19 Jul 2025 18:22:03 +0400 Subject: [PATCH 15/59] UISAUTHCOM-65 Provide the ability to pass props to control whether certain actions can be performed (#101) * UISAUTHCOM-65 Provide the ability to pass props to control whether certain actions can be performed * update tests --- CHANGELOG.md | 1 + lib/RoleDetails/RoleDetails.js | 20 +++++++++++-- lib/RoleDetails/RoleDetails.test.js | 46 +++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1571d7c..8e0eec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * [UISAUTHCOM-60](https://folio-org.atlassian.net/browse/UISAUTHCOM-60) Add new `hideUserLink` prop to `RoleDetails` component that will display users in assigned users list as a text if enabled. * [UISAUTHCOM-59](https://folio-org.atlassian.net/browse/UISAUTHCOM-59) Increase request timeout in `useCreateRoleMutation`, `useEditRoleMutation` from default 30 seconds to 10 minutes. This can be decreased if back-end performance improves. +* [UISAUTHCOM-65](https://folio-org.atlassian.net/browse/UISAUTHCOM-65) Provide the ability to pass props to control whether certain actions can be performed. # [2.0.2](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.2) diff --git a/lib/RoleDetails/RoleDetails.js b/lib/RoleDetails/RoleDetails.js index b6dcce5..f7b0e1d 100644 --- a/lib/RoleDetails/RoleDetails.js +++ b/lib/RoleDetails/RoleDetails.js @@ -50,6 +50,9 @@ import { RoleDetailsGeneralInfoAccordion } from './RoleDetailsGeneralInfoAccordi import css from './style.css'; export const RoleDetails = ({ + canCreate = true, + canDelete = true, + canEdit = true, displayShareAction = false, hideUserLink, isLoading: isLoadingProp = false, @@ -108,9 +111,17 @@ export const RoleDetails = ({ || isRoleShared ); - const isEditAllowed = isRoleShared ? stripes.hasPerm('consortia.sharing-roles-all.item.post') : stripes.hasPerm('roles.item.put'); - const isCreateAllowed = stripes.hasPerm('roles.item.post'); - const isDeleteAllowed = isRoleShared ? stripes.hasPerm('consortia.sharing-roles-all.item.delete') : stripes.hasPerm('roles.item.delete'); + const isEditAllowed = canEdit && ( + isRoleShared + ? stripes.hasPerm('consortia.sharing-roles-all.item.post') + : stripes.hasPerm('roles.item.put') + ); + const isCreateAllowed = canCreate && stripes.hasPerm('roles.item.post'); + const isDeleteAllowed = canDelete && ( + isRoleShared + ? stripes.hasPerm('consortia.sharing-roles-all.item.delete') + : stripes.hasPerm('roles.item.delete') + ); const isActionMenuHidden = (isMutationsPrevented && isSharingPrevented) || (!isEditAllowed && !isCreateAllowed && !isDeleteAllowed); if (isActionMenuHidden) return null; @@ -282,6 +293,9 @@ export const RoleDetails = ({ }; RoleDetails.propTypes = { + canCreate: PropTypes.bool, + canDelete: PropTypes.bool, + canEdit: PropTypes.bool, displayShareAction: PropTypes.bool, hideUserLink: PropTypes.bool, isLoading: PropTypes.bool, diff --git a/lib/RoleDetails/RoleDetails.test.js b/lib/RoleDetails/RoleDetails.test.js index c311557..ba6b8e9 100644 --- a/lib/RoleDetails/RoleDetails.test.js +++ b/lib/RoleDetails/RoleDetails.test.js @@ -245,4 +245,50 @@ describe('RoleDetails component', () => { expect(results).toHaveNoViolations(); }); + + describe('RoleDetails permissions (canEdit / canDelete / canShare)', () => { + it('should hide edit button if canEdit is false', () => { + renderComponent({ canEdit: false }); + + expect(screen.queryByText('stripes-authorization-components.crud.edit')).not.toBeInTheDocument(); + }); + + it('should show edit button if canEdit is true', () => { + renderComponent({ canEdit: true }); + + expect(screen.getByText('stripes-authorization-components.crud.edit')).toBeInTheDocument(); + }); + + it('should hide delete button if canDelete is false', async () => { + renderComponent({ canDelete: false }); + + await userEvent.click(screen.getByRole('button', { name: 'stripes-components.paneMenuActionsToggleLabel' })); + + expect(screen.queryByRole('button', { name: 'stripes-authorization-components.crud.delete' })).not.toBeInTheDocument(); + }); + + it('should show delete button if canDelete is true', async () => { + renderComponent({ canDelete: true }); + + await userEvent.click(screen.getByRole('button', { name: 'stripes-components.paneMenuActionsToggleLabel' })); + + expect(screen.getByRole('button', { name: 'stripes-authorization-components.crud.delete' })).toBeInTheDocument(); + }); + + it('should hide duplicate button if canCreate is false', async () => { + renderComponent({ canCreate: false }); + + await userEvent.click(screen.getByRole('button', { name: 'stripes-components.paneMenuActionsToggleLabel' })); + + expect(screen.queryByText('stripes-authorization-components.crud.duplicate')).not.toBeInTheDocument(); + }); + + it('should show duplicate button if canCreate is true', async () => { + renderComponent({ canCreate: true }); + + await userEvent.click(screen.getByRole('button', { name: 'stripes-components.paneMenuActionsToggleLabel' })); + + expect(screen.getByText('stripes-authorization-components.crud.duplicate')).toBeInTheDocument(); + }); + }); }); From abef407608014b064ba7d8de3f13a55b353ad81d Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Tue, 29 Jul 2025 19:36:04 -0400 Subject: [PATCH 16/59] Update translation strings --- translations/stripes-authorization-components/uk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/stripes-authorization-components/uk.json b/translations/stripes-authorization-components/uk.json index 1b82505..66b7ee2 100644 --- a/translations/stripes-authorization-components/uk.json +++ b/translations/stripes-authorization-components/uk.json @@ -64,7 +64,7 @@ "keycloak.records.checking": "Checking Keycloak records for the selected users", "crud.duplicate": "Дублювати", "crud.duplicateRole": "Дублювати роль?", - "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", + "crud.duplicateConfirmation": "Ви впевнені, що хочете створити дублікат цієї ролі {name}?", "applications.notAvailable": "Вибраний додаток недоступний", "crud.duplicate.suffix": "(дублювати)", "details.centrallyManaged": "Централізоване управління", From fa613485017c12b40919c05861e5035d963a50c4 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Sat, 30 Aug 2025 09:23:45 -0400 Subject: [PATCH 17/59] Update translation strings --- .../stripes-authorization-components/ja.json | 2 +- .../stripes-authorization-components/pl.json | 18 ++-- .../stripes-authorization-components/tr.json | 92 +++++++++++++++++++ .../stripes-authorization-components/uk.json | 10 +- 4 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 translations/stripes-authorization-components/tr.json diff --git a/translations/stripes-authorization-components/ja.json b/translations/stripes-authorization-components/ja.json index 5605c41..a8dd77c 100644 --- a/translations/stripes-authorization-components/ja.json +++ b/translations/stripes-authorization-components/ja.json @@ -64,7 +64,7 @@ "keycloak.records.checking": "Checking Keycloak records for the selected users", "crud.duplicate": "Duplicate", "crud.duplicateRole": "Duplicate role?", - "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", + "crud.duplicateConfirmation": "本当にこのロール{name} の複製を作成しますか?", "applications.notAvailable": "選択したアプリケーションは利用できません", "crud.duplicate.suffix": "(duplicate)", "details.centrallyManaged": "Centrally managed", diff --git a/translations/stripes-authorization-components/pl.json b/translations/stripes-authorization-components/pl.json index c4b69da..fd174c1 100644 --- a/translations/stripes-authorization-components/pl.json +++ b/translations/stripes-authorization-components/pl.json @@ -6,11 +6,11 @@ "columns.create": "Utwórz", "columns.delete": "Usuń", "columns.manage": "Manage", - "columns.policies": "Policies", - "columns.settings": "Settings/Settings sets", - "columns.procedure": "Procedure", - "columns.execute": "Execute", - "details.data": "Data", + "columns.policies": "Reguły", + "columns.settings": "Ustawienia/Zestawy ustawień", + "columns.procedure": "Procedura", + "columns.execute": "Wykonać", + "details.data": "Dane", "details.settings": "Ustawienia", "details.procedural": "Proceduralny", "search": "Szukaj", @@ -44,10 +44,10 @@ "role-details.accordion-users.columns.patronGroup": "Grupa czytelników", "policyDetails.columns.name": "Nazwa", "policyDetails.columns.description": "Opis", - "form.labels.policyType": "Policy type", + "form.labels.policyType": "Typ reguły", "form.labels.sourceType": "Typ zasobu", "form.labels.policyType.user": "Użytkownik", - "form.labels.policyType.time": "Czas", + "form.labels.policyType.time": "Godzina", "form.labels.policyType.role": "Rola", "form.labels.sourceType.user": "Użytkownik", "form.labels.sourceType.system": "System", @@ -62,7 +62,7 @@ "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", "keycloak.records.confirmationLabel": "Create user records in Keycloak", "keycloak.records.checking": "Checking Keycloak records for the selected users", - "crud.duplicate": "Powielić", + "crud.duplicate": "Powiel", "crud.duplicateRole": "Powielić rolę?", "crud.duplicateConfirmation": "Czy na pewno chcesz utworzyć duplikat tej roli {name}?", "applications.notAvailable": "Wybrana aplikacja jest niedostępna", @@ -87,6 +87,6 @@ "role.type.support": "Support", "role.type.regular": "Regular", "role.type.consortium": "Konsorcjum", - "assignUnassign.modal.label": "Select user ({tenant})", + "assignUnassign.modal.label": "Wybierz użytkownika ({tenant})", "columns.header.checkbox": "Zaznacz wszystkie {action} możliwości" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/tr.json b/translations/stripes-authorization-components/tr.json new file mode 100644 index 0000000..28028d0 --- /dev/null +++ b/translations/stripes-authorization-components/tr.json @@ -0,0 +1,92 @@ +{ + "columns.application": "Application", + "columns.resource": "Resource", + "columns.view": "View", + "columns.edit": "Edit", + "columns.create": "Create", + "columns.delete": "Delete", + "columns.manage": "Manage", + "columns.policies": "Policies", + "columns.settings": "Settings/Settings sets", + "columns.procedure": "Procedure", + "columns.execute": "Execute", + "details.data": "Data", + "details.settings": "Settings", + "details.procedural": "Procedural", + "search": "Search", + "new": "New", + "columns.name": "Name", + "columns.description": "Description", + "columns.updatedDate": "Updated", + "columns.updatedBy": "Updated by", + "assignedUsers": "Assigned users", + "assignUnassign": "Assign/Unassign", + "generalInformation": "General Information", + "details.capabilities": "Capabilities", + "details.capabilitySets": "Capability sets", + "details.nonSinglePolicyText": "* indicates one or more policies are set for the action.", + "crud.createRole": "Create role", + "crud.deleteRole": "Delete role", + "crud.deleteRoleConfirmation": "{rolename} will be deleted", + "crud.editRole": "Edit role", + "crud.cancel": "Cancel", + "crud.selectApplication": "Select application", + "crud.edit": "Edit", + "crud.delete": "Delete", + "form.labels.name": "Name", + "form.labels.description": "Description", + "form.labels.multiplePolicies": "Multiple policies on action {action}", + "applications.select": "Select application", + "applications.found": "{count} applications found", + "applications.totalSelected": "Total selected: {count}", + "applications": "Applications", + "role-details.accordion-users.columns.fullName": "Name", + "role-details.accordion-users.columns.patronGroup": "Patron group", + "policyDetails.columns.name": "Name", + "policyDetails.columns.description": "Description", + "form.labels.policyType": "Policy type", + "form.labels.sourceType": "Source type", + "form.labels.policyType.user": "User", + "form.labels.policyType.time": "Time", + "form.labels.policyType.role": "Role", + "form.labels.sourceType.user": "User", + "form.labels.sourceType.system": "System", + "form.labels.sourceType.consortium": "Consortium", + "policy.edit": "Edit policy", + "policy.create": "Create policy", + "policy.create.success": "Policy has been created successfully", + "policy.edit.success": "Policy has been updated successfully", + "policy.create.error": "Policy could not be created", + "policy.edit.error": "Policy could not be updated", + "form.unassignAllCapabilities": "Unassign all capabilities/sets", + "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", + "keycloak.records.confirmationLabel": "Create user records in Keycloak", + "keycloak.records.checking": "Checking Keycloak records for the selected users", + "crud.duplicate": "Duplicate", + "crud.duplicateRole": "Duplicate role?", + "crud.duplicateConfirmation": "Are you sure you want to create a duplicate of this role {name}?", + "applications.notAvailable": "Select application is not available", + "crud.duplicate.suffix": "(duplicate)", + "details.centrallyManaged": "Centrally managed", + "filter.true": "Yes", + "filter.false": "No", + "shareToAll": "Share to all", + "keycloak.records.created": "Keycloak records created for the following users: {users}", + "policy.share.success": "Policy has been shared successfully", + "policy.share.error": "Policy could not be shared", + "role.create.success": "Role has been created successfully", + "role.create.error": "Role could not be created", + "role.delete.success": "Role has been deleted successfully", + "role.delete.error": "Role could not be deleted", + "role.edit.error": "Role could not be updated", + "role.edit.success": "Role has been updated successfully", + "role.share.error": "Role could not be shared", + "role.share.success": "Role has been shared successfully", + "columns.type": "Type", + "role.type.default": "Default", + "role.type.support": "Support", + "role.type.regular": "Regular", + "role.type.consortium": "Consortium", + "assignUnassign.modal.label": "Select user ({tenant})", + "columns.header.checkbox": "Select all {action} capabilities" +} \ No newline at end of file diff --git a/translations/stripes-authorization-components/uk.json b/translations/stripes-authorization-components/uk.json index 66b7ee2..7331cc3 100644 --- a/translations/stripes-authorization-components/uk.json +++ b/translations/stripes-authorization-components/uk.json @@ -61,17 +61,17 @@ "form.unassignAllCapabilities": "Скасувати призначення всіх можливостей/наборів", "keycloak.records.creation": "Ця операція створить нові записи в Keycloak для таких користувачів: {users}", "keycloak.records.confirmationLabel": "Створити записи користувачів у Keycloak", - "keycloak.records.checking": "Checking Keycloak records for the selected users", + "keycloak.records.checking": "Перевірка записів Keycloak для вибраних користувачів", "crud.duplicate": "Дублювати", "crud.duplicateRole": "Дублювати роль?", "crud.duplicateConfirmation": "Ви впевнені, що хочете створити дублікат цієї ролі {name}?", "applications.notAvailable": "Вибраний додаток недоступний", "crud.duplicate.suffix": "(дублювати)", "details.centrallyManaged": "Централізоване управління", - "filter.true": "Yes", - "filter.false": "No", + "filter.true": "Так", + "filter.false": "Ні", "shareToAll": "Поділитися з усіма", - "keycloak.records.created": "Keycloak records created for the following users: {users}", + "keycloak.records.created": "Записи Keycloak створено для таких користувачів: {users}", "policy.share.success": "Політика була успішно поширена", "policy.share.error": "Політика не може бути поширена", "role.create.success": "Роль успішно створена", @@ -85,7 +85,7 @@ "columns.type": "Тип", "role.type.default": "За замовчуванням", "role.type.support": "Підтримка", - "role.type.regular": "Звичайний", + "role.type.regular": "Регулярно", "role.type.consortium": "Консорціум", "assignUnassign.modal.label": "Вибрати користувача ({tenant})", "columns.header.checkbox": "Вибрати всі можливості {action}" From f60ae6953f056448e84da26c558398b7e29d5613 Mon Sep 17 00:00:00 2001 From: Ryan Berger Date: Thu, 4 Sep 2025 08:29:24 -0400 Subject: [PATCH 18/59] [UISAUTHCOM-66] Suppress edit and delete menu buttons for default roles. (#105) --- CHANGELOG.md | 1 + lib/RoleDetails/RoleDetails.js | 7 ++++--- lib/RoleDetails/RoleDetails.test.js | 13 +++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e0eec8..c4daf00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * [UISAUTHCOM-60](https://folio-org.atlassian.net/browse/UISAUTHCOM-60) Add new `hideUserLink` prop to `RoleDetails` component that will display users in assigned users list as a text if enabled. * [UISAUTHCOM-59](https://folio-org.atlassian.net/browse/UISAUTHCOM-59) Increase request timeout in `useCreateRoleMutation`, `useEditRoleMutation` from default 30 seconds to 10 minutes. This can be decreased if back-end performance improves. * [UISAUTHCOM-65](https://folio-org.atlassian.net/browse/UISAUTHCOM-65) Provide the ability to pass props to control whether certain actions can be performed. +* [UISAUTHCOM-66](https://folio-org.atlassian.net/browse/UISAUTHCOM-66) Suppress edit and delete menu buttons for default roles. # [2.0.2](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.2) diff --git a/lib/RoleDetails/RoleDetails.js b/lib/RoleDetails/RoleDetails.js index f7b0e1d..e0de4e9 100644 --- a/lib/RoleDetails/RoleDetails.js +++ b/lib/RoleDetails/RoleDetails.js @@ -29,7 +29,7 @@ import { useStripes, } from '@folio/stripes/core'; -import { MUTATION_ACTION_TYPE } from '../constants'; +import { MUTATION_ACTION_TYPE, ROLE_TYPE } from '../constants'; import { useDeleteRoleMutation, useInitialRoleSharing, @@ -93,6 +93,7 @@ export const RoleDetails = ({ isLoading: isSharedRoleDeleting, } = useRoleSharing(); + const isDefaultRecord = role?.type === ROLE_TYPE.default; const isRoleShared = Boolean(stripes.hasInterface('consortia') && isShared(role)); const targetTenantId = tenantId || stripes.okapi.tenant; const isTargetTenantCentral = isTenantConsortiumCentral(stripes, targetTenantId); @@ -111,13 +112,13 @@ export const RoleDetails = ({ || isRoleShared ); - const isEditAllowed = canEdit && ( + const isEditAllowed = canEdit && !isDefaultRecord && ( isRoleShared ? stripes.hasPerm('consortia.sharing-roles-all.item.post') : stripes.hasPerm('roles.item.put') ); const isCreateAllowed = canCreate && stripes.hasPerm('roles.item.post'); - const isDeleteAllowed = canDelete && ( + const isDeleteAllowed = canDelete && !isDefaultRecord && ( isRoleShared ? stripes.hasPerm('consortia.sharing-roles-all.item.delete') : stripes.hasPerm('roles.item.delete') diff --git a/lib/RoleDetails/RoleDetails.test.js b/lib/RoleDetails/RoleDetails.test.js index ba6b8e9..74ff3c9 100644 --- a/lib/RoleDetails/RoleDetails.test.js +++ b/lib/RoleDetails/RoleDetails.test.js @@ -290,5 +290,18 @@ describe('RoleDetails component', () => { expect(screen.getByText('stripes-authorization-components.crud.duplicate')).toBeInTheDocument(); }); + + it('should hide edit and delete buttons, but show duplicate button if role is default', () => { + useRoleById.mockReturnValueOnce({ + roleDetails: { ...defaultProps, ...getRoleData({ type: ROLE_TYPE.default }) }, + isRoleDetailsLoaded: true, + }); + + renderComponent({ canEdit: true }); + + expect(screen.queryByText('stripes-authorization-components.crud.edit')).not.toBeInTheDocument(); + expect(screen.queryByText('stripes-authorization-components.crud.delete')).not.toBeInTheDocument(); + expect(screen.queryByText('stripes-authorization-components.crud.duplicate')).toBeInTheDocument(); + }); }); }); From 8264963482985ee20f0c6f2a6977901ddbe779c1 Mon Sep 17 00:00:00 2001 From: Ryan Berger Date: Wed, 1 Oct 2025 08:06:58 -0400 Subject: [PATCH 19/59] [UISAUTHCOM-67] Match GET role capabilities query key to match edit request (#106) * [UISAUTHCOM-67] Match GET role capabilities query key to match edit request, so `react-query` `invalidateQueries()` is called and new data is fetched. * Undo not needed change --- CHANGELOG.md | 1 + lib/Role/RoleEdit/RoleEdit.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4daf00..340ccd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * [UISAUTHCOM-59](https://folio-org.atlassian.net/browse/UISAUTHCOM-59) Increase request timeout in `useCreateRoleMutation`, `useEditRoleMutation` from default 30 seconds to 10 minutes. This can be decreased if back-end performance improves. * [UISAUTHCOM-65](https://folio-org.atlassian.net/browse/UISAUTHCOM-65) Provide the ability to pass props to control whether certain actions can be performed. * [UISAUTHCOM-66](https://folio-org.atlassian.net/browse/UISAUTHCOM-66) Suppress edit and delete menu buttons for default roles. +* [UISAUTHCOM-67](https://folio-org.atlassian.net/browse/UISAUTHCOM-67) Match GET role capabilities query key to match edit request, so `react-query` `invalidateQueries()` is called and new data is fetched. # [2.0.2](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.2) diff --git a/lib/Role/RoleEdit/RoleEdit.js b/lib/Role/RoleEdit/RoleEdit.js index 32d428d..f73253c 100644 --- a/lib/Role/RoleEdit/RoleEdit.js +++ b/lib/Role/RoleEdit/RoleEdit.js @@ -40,7 +40,7 @@ export const RoleEdit = ({ path, tenantId }) => { initialRoleCapabilitiesSelectedMap, isSuccess: isInitialRoleCapabilitiesLoaded, capabilitiesAppIds - } = useRoleCapabilities(roleId, tenantId); + } = useRoleCapabilities(roleId, tenantId, true); const [checkedAppIdsMap, setCheckedAppIdsMap] = useState({}); const [disabledCapabilities, setDisabledCapabilities] = useState({}); @@ -50,7 +50,7 @@ export const RoleEdit = ({ path, tenantId }) => { capabilitySetsCapabilities, isSuccess: isInitialRoleCapabilitySetsLoaded, capabilitySetsAppIds, - } = useRoleCapabilitySets(roleId, tenantId); + } = useRoleCapabilitySets(roleId, tenantId, true); const { capabilities, From 28a3ed2f6bf1ad5e708a39289d4817b78ec048eb Mon Sep 17 00:00:00 2001 From: Ryan Berger Date: Tue, 14 Oct 2025 08:28:36 -0400 Subject: [PATCH 20/59] [UISAUTHCOM-68] Group keycloak create errors together and ensure all user records in batch are attempted (#107) * Group error API error messages together to report affected user IDs * Refactor into function * Lint fixes * Move function to utils and add tests * Move IF statement per PR comments * Update unit tests --- CHANGELOG.md | 1 + .../useAssignUsersFlow/useAssignUsersFlow.js | 40 ++++-- .../useAssignUsersFlow.test.js | 133 +++++++++++++++++- lib/utils/errorHandling.js | 16 +++ lib/utils/errorHandling.test.js | 107 ++++++++++++++ .../stripes-authorization-components/en.json | 3 +- 6 files changed, 287 insertions(+), 13 deletions(-) create mode 100644 lib/utils/errorHandling.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 340ccd1..96650db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * [UISAUTHCOM-65](https://folio-org.atlassian.net/browse/UISAUTHCOM-65) Provide the ability to pass props to control whether certain actions can be performed. * [UISAUTHCOM-66](https://folio-org.atlassian.net/browse/UISAUTHCOM-66) Suppress edit and delete menu buttons for default roles. * [UISAUTHCOM-67](https://folio-org.atlassian.net/browse/UISAUTHCOM-67) Match GET role capabilities query key to match edit request, so `react-query` `invalidateQueries()` is called and new data is fetched. +* [UISAUTHCOM-68](https://folio-org.atlassian.net/browse/UISAUTHCOM-68) Group keycloak create errors together and ensure all user records in batch are attempted. # [2.0.2](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.2) diff --git a/lib/hooks/useAssignUsersFlow/useAssignUsersFlow.js b/lib/hooks/useAssignUsersFlow/useAssignUsersFlow.js index b725bf3..ce62815 100644 --- a/lib/hooks/useAssignUsersFlow/useAssignUsersFlow.js +++ b/lib/hooks/useAssignUsersFlow/useAssignUsersFlow.js @@ -1,7 +1,9 @@ import { useMemo, useState } from 'react'; +import { useIntl } from 'react-intl'; import keyBy from 'lodash/keyBy'; import { useOkapiKy } from '@folio/stripes/core'; import { useQueryClient } from 'react-query'; + import { useUpdateUserRolesMutation } from '../useUpdateUserRolesMutation'; import { useAssignRolesToUserMutation } from '../useAssignRolesToUserMutation'; import { useDeleteUserRolesMutation } from '../useDeleteUserRolesMutation'; @@ -9,6 +11,7 @@ import { useErrorCallout } from '../useErrorCallout'; import { apiVerbs, createUserRolesRequests, getNonKeycloakUsers } from '../../RoleDetails/utils'; import { useCreateAuthUserKeycloak } from '../useCreateAuthUserKeycloak'; import { useShowCallout } from '../useShowCallout'; +import { getErrorMessagesFromResponse, reportCreateKeycloakUserErrors } from '../../utils'; /** * Custom hook to handle assigning users to roles, including interaction with Keycloak for user creation and validation. @@ -42,12 +45,13 @@ export function useAssignUsersFlow({ roleId, tenantId, users, refetch }) { const [isAuthUsersKeycloakConfirmationOpen, setIsAuthUsersKeycloakConfirmationOpen] = useState(false); const [checkingUsersInKeycloak, setCheckingUsersInKeycloak] = useState(false); + const intl = useIntl(); const { sendErrorCallout } = useErrorCallout(); const showCallout = useShowCallout(); const { mutateUpdateUserRoles } = useUpdateUserRolesMutation(sendErrorCallout, { tenantId }); const { mutateAssignRolesToUser } = useAssignRolesToUserMutation(sendErrorCallout, { tenantId }); const { mutateDeleteUserRoles } = useDeleteUserRolesMutation(sendErrorCallout, { tenantId }); - const { mutateAsync: createKeycloakUser } = useCreateAuthUserKeycloak(sendErrorCallout, { tenantId }); + const { mutateAsync: createKeycloakUser } = useCreateAuthUserKeycloak((error) => { return error; }, { tenantId }); const initialSelectedUsers = useMemo(() => keyBy(users, 'id'), [users]); @@ -107,15 +111,36 @@ export function useAssignUsersFlow({ roleId, tenantId, users, refetch }) { // 2. Assigns that users to the role; // 3. Refetch role users data. const onSubmitCreateKeycloakUsersConfirmation = async () => { + const recordsWithError = []; + const recordsCreated = []; setIsAuthUsersKeycloakConfirmationOpen(false); + for (const userId of nonKeycloakUsersList) { - await createKeycloakUser(userId); + try { + await createKeycloakUser(userId); + recordsCreated.push(userId); + } catch ({ name, message, response }) { + const errorMessage = await getErrorMessagesFromResponse({ response }); + + for (const msg of errorMessage) { + recordsWithError.push({ userId, error: msg }); + } + } + } + + if (recordsWithError.length) { + reportCreateKeycloakUserErrors(recordsWithError, + 'stripes-authorization-components.keycloak.records.create.error', + intl, sendErrorCallout); + } + + if (recordsCreated.length) { + showCallout({ + messageId: 'stripes-authorization-components.keycloak.records.created', + values: { users: recordsCreated.join(', ') } + }); + await assignUsers(recordsCreated, []); } - showCallout({ - messageId: 'stripes-authorization-components.keycloak.records.created', - values: { users: nonKeycloakUsersList.join(', ') } - }); - await assignUsers(nonKeycloakUsersList, []); setNonKeycloakUsersList([]); }; @@ -129,4 +154,3 @@ export function useAssignUsersFlow({ roleId, tenantId, users, refetch }) { setIsAuthUsersKeycloakConfirmationOpen, }; } - diff --git a/lib/hooks/useAssignUsersFlow/useAssignUsersFlow.test.js b/lib/hooks/useAssignUsersFlow/useAssignUsersFlow.test.js index 1032d89..3cffd34 100644 --- a/lib/hooks/useAssignUsersFlow/useAssignUsersFlow.test.js +++ b/lib/hooks/useAssignUsersFlow/useAssignUsersFlow.test.js @@ -1,7 +1,47 @@ -import { useOkapiKy } from '@folio/stripes/core'; import { QueryClient, QueryClientProvider } from 'react-query'; -import { renderHook } from '@folio/jest-config-stripes/testing-library/react'; + +import { useOkapiKy } from '@folio/stripes/core'; +import { act, renderHook } from '@folio/jest-config-stripes/testing-library/react'; + import { useAssignUsersFlow } from './useAssignUsersFlow'; +import { useAssignRolesToUserMutation } from '../useAssignRolesToUserMutation'; +import { useCreateAuthUserKeycloak } from '../useCreateAuthUserKeycloak'; +import { getNonKeycloakUsers, createUserRolesRequests } from '../../RoleDetails/utils'; +import { getErrorMessagesFromResponse, reportCreateKeycloakUserErrors } from '../../utils'; + +jest.mock('../useCreateAuthUserKeycloak', () => ({ + useCreateAuthUserKeycloak: jest.fn().mockReturnValue({ + mutateAsync: jest.fn(), + }), +})); +jest.mock('../useUpdateUserRolesMutation', () => ({ + useUpdateUserRolesMutation: jest.fn().mockReturnValue({ + mutateUpdateUserRoles: jest.fn(), + }), +})); +jest.mock('../useAssignRolesToUserMutation', () => ({ + useAssignRolesToUserMutation: jest.fn().mockReturnValue({ + mutateAssignRolesToUser: jest.fn().mockResolvedValue(), + }), +})); +jest.mock('../useShowCallout', () => ({ + useShowCallout: jest.fn().mockReturnValue({ + showCallout: jest.fn(), + }), +})); +jest.mock('../../RoleDetails/utils', () => ({ + apiVerbs: { PUT: 'PUT', POST: 'POST', DELETE: 'DELETE' }, + createUserRolesRequests: jest.fn().mockResolvedValue([ + { apiVerb: 'POST', userId: '1', roleIds: ['role1'] }, + { apiVerb: 'PUT', userId: '2', roleIds: ['role1'] }, + { apiVerb: 'DELETE', userId: '3', roleIds: [] }, + ]), + getNonKeycloakUsers: jest.fn().mockResolvedValue([]), +})); +jest.mock('../../utils', () => ({ + getErrorMessagesFromResponse: jest.fn(), + reportCreateKeycloakUserErrors: jest.fn(), +})); const queryClient = new QueryClient(); @@ -28,7 +68,7 @@ describe('useAssignUsersFlow', () => { it('renders hook correctly', async () => { const { result } = renderHook(() => useAssignUsersFlow({ roleId: '1', - tenantId: 'dikku2', + tenantId: 'diku2', users: [{ id: '1' }], refetch: jest.fn() }), { wrapper }); @@ -44,7 +84,7 @@ describe('useAssignUsersFlow', () => { it('calls onSubmit selected users', async () => { const { result } = renderHook(() => useAssignUsersFlow({ roleId: '1', - tenantId: 'dikku2', + tenantId: 'diku2', users: [{ id: '2' }], refetch: jest.fn() }), { wrapper }); @@ -52,4 +92,89 @@ describe('useAssignUsersFlow', () => { result.current.onSubmitSelectedUsers([{ id: '1' }, { id: '3' }]); result.current.onSubmitCreateKeycloakUsersConfirmation(); }); + + it('calls onSubmitSelectedUsers and assigns users if all are keycloak users', async () => { + getNonKeycloakUsers.mockResolvedValue([]); + createUserRolesRequests.mockResolvedValue([ + { apiVerb: 'POST', userId: '1', roleIds: ['1'] } + ]); + const assignRolesMock = jest.fn().mockResolvedValue(); + useAssignRolesToUserMutation.mockReturnValue({ mutateAssignRolesToUser: assignRolesMock }); + + const { result } = renderHook(() => useAssignUsersFlow({ + roleId: '1', + tenantId: 'diku2', + users: [{ id: '2' }], + refetch: jest.fn() + }), { wrapper }); + + await act(async () => { + await result.current.onSubmitSelectedUsers([{ id: '1' }, { id: '3' }]); + }); + + expect(result.current.isAuthUsersKeycloakConfirmationOpen).toBe(false); + expect(assignRolesMock).toHaveBeenCalled(); + }); + + it('calls onSubmitSelectedUsers and sets nonKeycloakUsersList if some users are not keycloak users', async () => { + getNonKeycloakUsers.mockResolvedValue(['3']); + createUserRolesRequests.mockResolvedValue([ + { apiVerb: 'POST', userId: '1', roleIds: ['1'] } + ]); + const assignRolesMock = jest.fn().mockResolvedValue(); + useAssignRolesToUserMutation.mockReturnValue({ mutateAssignRolesToUser: assignRolesMock }); + + const { result } = renderHook(() => useAssignUsersFlow({ + roleId: '1', + tenantId: 'diku2', + users: [{ id: '2' }], + refetch: jest.fn() + }), { wrapper }); + + await act(async () => { + await result.current.onSubmitSelectedUsers([{ id: '1' }, { id: '3' }]); + }); + + expect(result.current.isAuthUsersKeycloakConfirmationOpen).toBe(true); + expect(result.current.nonKeycloakUsersList).toEqual(['3']); + expect(result.current.checkingUsersInKeycloak).toBe(false); + expect(assignRolesMock).toHaveBeenCalled(); + }); + + it('handles errors during keycloak user creation', async () => { + const errorResponse = { name: 'Error', message: 'fail', response: {} }; + const createKeycloakUserMock = jest.fn().mockImplementation(() => { + throw errorResponse; + }); + useCreateAuthUserKeycloak.mockReturnValue({ mutateAsync: createKeycloakUserMock }); + getErrorMessagesFromResponse.mockResolvedValue(['errorMsg']); + reportCreateKeycloakUserErrors.mockImplementation(jest.fn()); + + const { result } = renderHook(() => useAssignUsersFlow({ + roleId: '1', + tenantId: 'diku2', + users: [{ id: '2' }], + refetch: jest.fn() + }), { wrapper }); + + act(() => { + result.current.setIsAuthUsersKeycloakConfirmationOpen(true); + }); + act(() => { + result.current.nonKeycloakUsersList.push('3'); + }); + + await act(async () => { + await result.current.onSubmitCreateKeycloakUsersConfirmation(); + }); + + expect(reportCreateKeycloakUserErrors).toHaveBeenCalledWith( + [{ userId: '3', error: 'errorMsg' }], + 'stripes-authorization-components.keycloak.records.create.error', + expect.anything(), + expect.anything() + ); + expect(result.current.nonKeycloakUsersList).toEqual([]); + expect(result.current.isAuthUsersKeycloakConfirmationOpen).toBe(false); + }); }); diff --git a/lib/utils/errorHandling.js b/lib/utils/errorHandling.js index 3f88c8b..df7a1ef 100644 --- a/lib/utils/errorHandling.js +++ b/lib/utils/errorHandling.js @@ -29,3 +29,19 @@ export const getErrorMessagesFromResponse = async (response) => { )) .catch(() => []); }; + +export const reportCreateKeycloakUserErrors = (recordsWithError, messageId, intl, sendErrorCallout) => { + const errorMessages = {}; + // Group same error messages together like { 'error message': [userId1, userId2], ... } + for (const { error, userId } of recordsWithError) { + errorMessages[error] ??= []; + errorMessages[error].push(userId); + } + + for (const [error, userIds] of Object.entries(errorMessages)) { + const errorMessage = intl.formatMessage({ + id: messageId + }, { users: userIds.join(', '), error }); + sendErrorCallout(errorMessage); + } +}; diff --git a/lib/utils/errorHandling.test.js b/lib/utils/errorHandling.test.js new file mode 100644 index 0000000..75c6d80 --- /dev/null +++ b/lib/utils/errorHandling.test.js @@ -0,0 +1,107 @@ +import { + getErrorsFromResponse, + getErrorCodesFromResponse, + getErrorMessagesFromResponse, + reportCreateKeycloakUserErrors +} from './errorHandling'; + +describe('errorHandling', () => { + describe('getErrorsFromResponse', () => { + it('should return errors from response json', async () => { + const response = { + json: jest.fn().mockResolvedValue({ errors: [{ code: 'ERR1', message: 'Error 1' }] }) + }; + const result = await getErrorsFromResponse({ response }); + expect(result).toEqual([{ code: 'ERR1', message: 'Error 1' }]); + }); + + it('should return empty array if response json fails', async () => { + const response = { + json: jest.fn().mockRejectedValue(new Error('fail')) + }; + const result = await getErrorsFromResponse({ response }); + expect(result).toEqual([]); + }); + }); + + describe('getErrorCodesFromResponse', () => { + it('should return error codes from response', async () => { + const response = { + json: jest.fn().mockResolvedValue({ errors: [{ code: 'ERR1' }, { code: 'ERR2' }] }) + }; + const result = await getErrorCodesFromResponse({ response }); + expect(result).toEqual(['ERR1', 'ERR2']); + }); + + it('should filter out falsy codes', async () => { + const response = { + json: jest.fn().mockResolvedValue({ errors: [{ code: 'ERR1' }, { code: null }] }) + }; + const result = await getErrorCodesFromResponse({ response }); + expect(result).toEqual(['ERR1']); + }); + }); + + describe('getErrorMessagesFromResponse', () => { + it('should return error messages from response', async () => { + const response = { + json: jest.fn().mockResolvedValue({ errors: [{ message: 'Error 1' }, { message: 'Error 2' }] }) + }; + const result = await getErrorMessagesFromResponse({ response }); + expect(result).toEqual(['Error 1', 'Error 2']); + }); + + it('should filter out falsy messages', async () => { + const response = { + json: jest.fn().mockResolvedValue({ errors: [{ message: 'Error 1' }, { message: '' }] }) + }; + const result = await getErrorMessagesFromResponse({ response }); + expect(result).toEqual(['Error 1']); + }); + + it('should return empty array on failure', async () => { + const response = { + json: jest.fn().mockRejectedValue(new Error('fail')) + }; + const result = await getErrorMessagesFromResponse({ response }); + expect(result).toEqual([]); + }); + }); + + describe('reportCreateKeycloakUserErrors', () => { + const intl = { + formatMessage: jest.fn(({ id }, values) => `${id}: ${values.users} - ${values.error}`) + }; + const sendErrorCallout = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should group errors and call sendErrorCallout for each error', () => { + const recordsWithError = [ + { error: 'Error A', userId: 'user1' }, + { error: 'Error B', userId: 'user2' }, + { error: 'Error A', userId: 'user3' } + ]; + reportCreateKeycloakUserErrors(recordsWithError, 'msgId', intl, sendErrorCallout); + + expect(intl.formatMessage).toHaveBeenCalledWith( + { id: 'msgId' }, + { users: 'user1, user3', error: 'Error A' } + ); + expect(intl.formatMessage).toHaveBeenCalledWith( + { id: 'msgId' }, + { users: 'user2', error: 'Error B' } + ); + expect(sendErrorCallout).toHaveBeenCalledTimes(2); + expect(sendErrorCallout).toHaveBeenCalledWith('msgId: user1, user3 - Error A'); + expect(sendErrorCallout).toHaveBeenCalledWith('msgId: user2 - Error B'); + }); + + it('should not call sendErrorCallout if recordsWithError is empty', () => { + reportCreateKeycloakUserErrors([], 'msgId', intl, sendErrorCallout); + expect(sendErrorCallout).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/translations/stripes-authorization-components/en.json b/translations/stripes-authorization-components/en.json index a37d6e1..6c9402f 100644 --- a/translations/stripes-authorization-components/en.json +++ b/translations/stripes-authorization-components/en.json @@ -90,5 +90,6 @@ "keycloak.records.creation": "This operation will create new records in Keycloak for the following users: {users}", "keycloak.records.confirmationLabel": "Create user records in Keycloak", "keycloak.records.checking": "Checking Keycloak records for the selected users", - "keycloak.records.created": "Keycloak records created for the following users: {users}" + "keycloak.records.created": "Keycloak records created for the following users: {users}", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } From 99b77f4d9c0c8645a1465b12fd3d5424c2454fd8 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Wed, 15 Oct 2025 17:38:35 -0400 Subject: [PATCH 21/59] Update translation strings --- translations/stripes-authorization-components/ar.json | 3 ++- translations/stripes-authorization-components/ber.json | 3 ++- translations/stripes-authorization-components/ca.json | 3 ++- translations/stripes-authorization-components/cs_CZ.json | 3 ++- translations/stripes-authorization-components/da.json | 3 ++- translations/stripes-authorization-components/de.json | 3 ++- translations/stripes-authorization-components/en_GB.json | 3 ++- translations/stripes-authorization-components/en_SE.json | 3 ++- translations/stripes-authorization-components/en_US.json | 3 ++- translations/stripes-authorization-components/es.json | 3 ++- translations/stripes-authorization-components/es_419.json | 3 ++- translations/stripes-authorization-components/es_ES.json | 3 ++- translations/stripes-authorization-components/fr.json | 3 ++- translations/stripes-authorization-components/fr_FR.json | 3 ++- translations/stripes-authorization-components/he.json | 3 ++- translations/stripes-authorization-components/hi_IN.json | 3 ++- translations/stripes-authorization-components/hu.json | 3 ++- translations/stripes-authorization-components/it_IT.json | 3 ++- translations/stripes-authorization-components/ja.json | 3 ++- translations/stripes-authorization-components/ko.json | 3 ++- translations/stripes-authorization-components/nb.json | 3 ++- translations/stripes-authorization-components/nl.json | 3 ++- translations/stripes-authorization-components/nn.json | 3 ++- translations/stripes-authorization-components/pl.json | 3 ++- translations/stripes-authorization-components/pt_BR.json | 3 ++- translations/stripes-authorization-components/pt_PT.json | 3 ++- translations/stripes-authorization-components/ru.json | 3 ++- translations/stripes-authorization-components/sk.json | 3 ++- translations/stripes-authorization-components/sv.json | 3 ++- translations/stripes-authorization-components/tr.json | 3 ++- translations/stripes-authorization-components/uk.json | 3 ++- translations/stripes-authorization-components/ur.json | 3 ++- translations/stripes-authorization-components/zh_CN.json | 3 ++- translations/stripes-authorization-components/zh_TW.json | 3 ++- translations/stripes-authorization-components/zu.json | 3 ++- 35 files changed, 70 insertions(+), 35 deletions(-) diff --git a/translations/stripes-authorization-components/ar.json b/translations/stripes-authorization-components/ar.json index 6fa7a1f..13b2f40 100644 --- a/translations/stripes-authorization-components/ar.json +++ b/translations/stripes-authorization-components/ar.json @@ -88,5 +88,6 @@ "role.type.regular": "منتظم", "role.type.consortium": "اتحاد المكتبات", "assignUnassign.modal.label": "حدد المستخدم ( {tenant} )", - "columns.header.checkbox": "حدد كل إمكانيات {action} " + "columns.header.checkbox": "حدد كل إمكانيات {action} ", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/ber.json b/translations/stripes-authorization-components/ber.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/ber.json +++ b/translations/stripes-authorization-components/ber.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/ca.json b/translations/stripes-authorization-components/ca.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/ca.json +++ b/translations/stripes-authorization-components/ca.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/cs_CZ.json b/translations/stripes-authorization-components/cs_CZ.json index 6a239a8..a76d5e8 100644 --- a/translations/stripes-authorization-components/cs_CZ.json +++ b/translations/stripes-authorization-components/cs_CZ.json @@ -88,5 +88,6 @@ "role.type.regular": "Pravidelně", "role.type.consortium": "Konsorcium", "assignUnassign.modal.label": "Vybrat uživatele ({tenant})", - "columns.header.checkbox": "Vybrat všechny možnosti {action}" + "columns.header.checkbox": "Vybrat všechny možnosti {action}", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/da.json b/translations/stripes-authorization-components/da.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/da.json +++ b/translations/stripes-authorization-components/da.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/de.json b/translations/stripes-authorization-components/de.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/de.json +++ b/translations/stripes-authorization-components/de.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/en_GB.json b/translations/stripes-authorization-components/en_GB.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/en_GB.json +++ b/translations/stripes-authorization-components/en_GB.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/en_SE.json b/translations/stripes-authorization-components/en_SE.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/en_SE.json +++ b/translations/stripes-authorization-components/en_SE.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/en_US.json b/translations/stripes-authorization-components/en_US.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/en_US.json +++ b/translations/stripes-authorization-components/en_US.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/es.json b/translations/stripes-authorization-components/es.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/es.json +++ b/translations/stripes-authorization-components/es.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/es_419.json b/translations/stripes-authorization-components/es_419.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/es_419.json +++ b/translations/stripes-authorization-components/es_419.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/es_ES.json b/translations/stripes-authorization-components/es_ES.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/es_ES.json +++ b/translations/stripes-authorization-components/es_ES.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/fr.json b/translations/stripes-authorization-components/fr.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/fr.json +++ b/translations/stripes-authorization-components/fr.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/fr_FR.json b/translations/stripes-authorization-components/fr_FR.json index f7436dd..3582847 100644 --- a/translations/stripes-authorization-components/fr_FR.json +++ b/translations/stripes-authorization-components/fr_FR.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/he.json b/translations/stripes-authorization-components/he.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/he.json +++ b/translations/stripes-authorization-components/he.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/hi_IN.json b/translations/stripes-authorization-components/hi_IN.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/hi_IN.json +++ b/translations/stripes-authorization-components/hi_IN.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/hu.json b/translations/stripes-authorization-components/hu.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/hu.json +++ b/translations/stripes-authorization-components/hu.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/it_IT.json b/translations/stripes-authorization-components/it_IT.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/it_IT.json +++ b/translations/stripes-authorization-components/it_IT.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/ja.json b/translations/stripes-authorization-components/ja.json index a8dd77c..2f976fe 100644 --- a/translations/stripes-authorization-components/ja.json +++ b/translations/stripes-authorization-components/ja.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "ユーザーの選択({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/ko.json b/translations/stripes-authorization-components/ko.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/ko.json +++ b/translations/stripes-authorization-components/ko.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/nb.json b/translations/stripes-authorization-components/nb.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/nb.json +++ b/translations/stripes-authorization-components/nb.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/nl.json b/translations/stripes-authorization-components/nl.json index c0095b9..104d6ae 100644 --- a/translations/stripes-authorization-components/nl.json +++ b/translations/stripes-authorization-components/nl.json @@ -88,5 +88,6 @@ "role.type.regular": "Regelmatig", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Gebruiker selecteren ({tenant})", - "columns.header.checkbox": "Alles selecteren {action} mogelijkheden" + "columns.header.checkbox": "Alles selecteren {action} mogelijkheden", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/nn.json b/translations/stripes-authorization-components/nn.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/nn.json +++ b/translations/stripes-authorization-components/nn.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/pl.json b/translations/stripes-authorization-components/pl.json index fd174c1..67cc997 100644 --- a/translations/stripes-authorization-components/pl.json +++ b/translations/stripes-authorization-components/pl.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Konsorcjum", "assignUnassign.modal.label": "Wybierz użytkownika ({tenant})", - "columns.header.checkbox": "Zaznacz wszystkie {action} możliwości" + "columns.header.checkbox": "Zaznacz wszystkie {action} możliwości", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/pt_BR.json b/translations/stripes-authorization-components/pt_BR.json index 8abb308..d38a564 100644 --- a/translations/stripes-authorization-components/pt_BR.json +++ b/translations/stripes-authorization-components/pt_BR.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consórcio", "assignUnassign.modal.label": "Selecione o usuário ({tenant})", - "columns.header.checkbox": "Selecionar todas {action} de capacidades" + "columns.header.checkbox": "Selecionar todas {action} de capacidades", + "keycloak.records.create.error": "Falha ao criar registros de usuário no keycloak para usuários: {users} : {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/pt_PT.json b/translations/stripes-authorization-components/pt_PT.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/pt_PT.json +++ b/translations/stripes-authorization-components/pt_PT.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/ru.json b/translations/stripes-authorization-components/ru.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/ru.json +++ b/translations/stripes-authorization-components/ru.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/sk.json b/translations/stripes-authorization-components/sk.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/sk.json +++ b/translations/stripes-authorization-components/sk.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/sv.json b/translations/stripes-authorization-components/sv.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/sv.json +++ b/translations/stripes-authorization-components/sv.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/tr.json b/translations/stripes-authorization-components/tr.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/tr.json +++ b/translations/stripes-authorization-components/tr.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/uk.json b/translations/stripes-authorization-components/uk.json index 7331cc3..1228168 100644 --- a/translations/stripes-authorization-components/uk.json +++ b/translations/stripes-authorization-components/uk.json @@ -88,5 +88,6 @@ "role.type.regular": "Регулярно", "role.type.consortium": "Консорціум", "assignUnassign.modal.label": "Вибрати користувача ({tenant})", - "columns.header.checkbox": "Вибрати всі можливості {action}" + "columns.header.checkbox": "Вибрати всі можливості {action}", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/ur.json b/translations/stripes-authorization-components/ur.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/ur.json +++ b/translations/stripes-authorization-components/ur.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/zh_CN.json b/translations/stripes-authorization-components/zh_CN.json index 3d3fd59..1b7334d 100644 --- a/translations/stripes-authorization-components/zh_CN.json +++ b/translations/stripes-authorization-components/zh_CN.json @@ -88,5 +88,6 @@ "role.type.regular": "定期", "role.type.consortium": "联盟", "assignUnassign.modal.label": "选择用户 ( {tenant} )", - "columns.header.checkbox": "选择所有{action}功能" + "columns.header.checkbox": "选择所有{action}功能", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/zh_TW.json b/translations/stripes-authorization-components/zh_TW.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/zh_TW.json +++ b/translations/stripes-authorization-components/zh_TW.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file diff --git a/translations/stripes-authorization-components/zu.json b/translations/stripes-authorization-components/zu.json index 28028d0..53b2575 100644 --- a/translations/stripes-authorization-components/zu.json +++ b/translations/stripes-authorization-components/zu.json @@ -88,5 +88,6 @@ "role.type.regular": "Regular", "role.type.consortium": "Consortium", "assignUnassign.modal.label": "Select user ({tenant})", - "columns.header.checkbox": "Select all {action} capabilities" + "columns.header.checkbox": "Select all {action} capabilities", + "keycloak.records.create.error": "Failed to create user records in keycloak for users: {users}: {error}" } \ No newline at end of file From 8530f02caefafa13860136ab60e512045a1c474a Mon Sep 17 00:00:00 2001 From: Ryan Berger Date: Thu, 23 Oct 2025 08:29:31 -0400 Subject: [PATCH 22/59] [UISAUTHCOM-69] Display warning and require confirmation before unchecking an application. (#109) * Initial implementation * Further progress * Working modal * Fix logic flow and add tests/comments * Lint fixes * PR feedback * PR feedback * Remove extra space. * Fix test --- CHANGELOG.md | 1 + lib/Role/RoleEdit/RoleEdit.js | 39 ++++++-- lib/Role/RoleForm/RoleForm.js | 27 +++++- lib/Role/utils.js | 30 ++++++ lib/Role/utils.test.js | 97 ++++++++++++++++++- lib/utils/grouping.js | 4 +- lib/utils/helpers.js | 10 +- .../stripes-authorization-components/en.json | 2 + 8 files changed, 195 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96650db..d809170 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * [UISAUTHCOM-66](https://folio-org.atlassian.net/browse/UISAUTHCOM-66) Suppress edit and delete menu buttons for default roles. * [UISAUTHCOM-67](https://folio-org.atlassian.net/browse/UISAUTHCOM-67) Match GET role capabilities query key to match edit request, so `react-query` `invalidateQueries()` is called and new data is fetched. * [UISAUTHCOM-68](https://folio-org.atlassian.net/browse/UISAUTHCOM-68) Group keycloak create errors together and ensure all user records in batch are attempted. +* [UISAUTHCOM-69](https://folio-org.atlassian.net/browse/UISAUTHCOM-69) Display warning and require confirmation before unchecking an application. # [2.0.2](https://github.com/folio-org/stripes-authorization-components/tree/v2.0.2) diff --git a/lib/Role/RoleEdit/RoleEdit.js b/lib/Role/RoleEdit/RoleEdit.js index f73253c..dff7816 100644 --- a/lib/Role/RoleEdit/RoleEdit.js +++ b/lib/Role/RoleEdit/RoleEdit.js @@ -1,5 +1,4 @@ -import isEmpty from 'lodash/isEmpty'; -import isEqual from 'lodash/isEqual'; +import { isEmpty, isEqual, difference } from 'lodash'; import PropTypes from 'prop-types'; import { useEffect, useState } from 'react'; import { useQueryClient } from 'react-query'; @@ -21,7 +20,7 @@ import { } from '../../hooks'; import { isShared } from '../../utils'; import { RoleForm } from '../RoleForm'; -import { getCheckboxHandlers } from '../utils'; +import { getCheckboxHandlers, getUnselectedItemCount } from '../utils'; export const RoleEdit = ({ path, tenantId }) => { const history = useHistory(); @@ -44,6 +43,8 @@ export const RoleEdit = ({ path, tenantId }) => { const [checkedAppIdsMap, setCheckedAppIdsMap] = useState({}); const [disabledCapabilities, setDisabledCapabilities] = useState({}); + const [isUnselectApplicationConfirmationOpen, setIsUnselectApplicationConfirmationOpen] = useState(false); + const [unselectedItemsInfo, setUnselectedItemsInfo] = useState({}); const { initialRoleCapabilitySetsSelectedMap, @@ -84,16 +85,36 @@ export const RoleEdit = ({ path, tenantId }) => { setDisabledCapabilities({}); }; - const onSubmitSelectApplications = (appIds, onCloseHandler) => { - if (onCloseHandler) { - onCloseHandler(); - } + const applyAppIdsChanges = (appIds) => { if (isEmpty(appIds)) { unselectAllCapabilitiesAndSets(); } setCheckedAppIdsMap(appIds); }; + const onSubmitSelectApplications = (appIds, onCloseHandler) => { + let isConfirmationNeeded = false; + const unselectedAppIds = difference(Object.keys(checkedAppIdsMap), Object.keys(appIds)); + + if (unselectedAppIds.length) { + const unselectedCapabilityCount = getUnselectedItemCount(unselectedAppIds, capabilities, selectedCapabilitiesMap); + const unselectedCapabilitySetCount = getUnselectedItemCount(unselectedAppIds, capabilitySets, selectedCapabilitySetsMap); + + // If there are any capabilities or capability sets that would be unselected, show confirmation modal + if (unselectedCapabilityCount > 0 || unselectedCapabilitySetCount > 0) { + isConfirmationNeeded = true; + setUnselectedItemsInfo({ selectedAppIds: appIds, unselectedAppIds, unselectedCapabilityCount, unselectedCapabilitySetCount, onCloseHandler }); + setIsUnselectApplicationConfirmationOpen(true); + } + } + + // Changes can be applied directly if no capabilities or capability sets would be unselected + if (!isConfirmationNeeded && onCloseHandler) { + applyAppIdsChanges(appIds); + onCloseHandler(); + } + }; + const isRoleShared = Boolean(stripes.hasInterface('consortia') && isShared(roleDetails)); const shouldUpdateCapabilities = !isEqual(initialRoleCapabilitiesSelectedMap, selectedCapabilitiesMap); const shouldUpdateCapabilitySets = !isEqual(initialRoleCapabilitySetsSelectedMap, selectedCapabilitySetsMap); @@ -233,6 +254,10 @@ export const RoleEdit = ({ path, tenantId }) => { isAllActionCapabilitiesSelected={isAllActionCapabilitiesSelected} toggleCapabilitySetsHeaderCheckbox={toggleCapabilitySetsHeaderCheckbox} isAllActionCapabilitySetsSelected={isAllActionCapabilitySetsSelected} + isUnselectApplicationConfirmationOpen={isUnselectApplicationConfirmationOpen} + setIsUnselectApplicationConfirmationOpen={setIsUnselectApplicationConfirmationOpen} + unselectedItemsInfo={unselectedItemsInfo} + applyAppIdsChanges={applyAppIdsChanges} /> ); }; diff --git a/lib/Role/RoleForm/RoleForm.js b/lib/Role/RoleForm/RoleForm.js index 60fbfd4..57d005f 100644 --- a/lib/Role/RoleForm/RoleForm.js +++ b/lib/Role/RoleForm/RoleForm.js @@ -5,6 +5,7 @@ import { Accordion, AccordionSet, AccordionStatus, Button, + ConfirmationModal, ExpandAllButton, Layer, Pane, @@ -48,7 +49,11 @@ export const RoleForm = ({ toggleCapabilitiesHeaderCheckbox, isAllActionCapabilitiesSelected, toggleCapabilitySetsHeaderCheckbox, - isAllActionCapabilitySetsSelected + isAllActionCapabilitySetsSelected, + isUnselectApplicationConfirmationOpen, + setIsUnselectApplicationConfirmationOpen, + unselectedItemsInfo, + applyAppIdsChanges }) => { const paneFooterRenderStart = (