From 6841c3f4d834ef9d4af6c6d36d6511e51299a756 Mon Sep 17 00:00:00 2001 From: Michael Abebe Date: Thu, 28 May 2026 10:57:20 +0300 Subject: [PATCH] feat(freight:backoffice): group roles by apps --- .../user-management/PermissionsPage.tsx | 91 ++++++++++++++++--- 1 file changed, 77 insertions(+), 14 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/pages/dashboard/user-management/PermissionsPage.tsx b/apps/edr-freight-web/backoffice/src/pages/dashboard/user-management/PermissionsPage.tsx index d0d9a7867..10c5aba25 100644 --- a/apps/edr-freight-web/backoffice/src/pages/dashboard/user-management/PermissionsPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/dashboard/user-management/PermissionsPage.tsx @@ -1,5 +1,12 @@ import { useEffect, useState } from "react"; import { isAxiosError } from "axios"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@edr/ui-common"; import { api } from "@/auth/http"; @@ -12,6 +19,13 @@ interface PermissionRecord { id: string; key: string; name?: LocaleText; + applicationId?: string | null; +} + +interface ApplicationRecord { + id: string; + key: string; + name?: LocaleText; } interface ListResponse { @@ -21,6 +35,8 @@ interface ListResponse { } const PAGE_SIZE = 2000; +const ALL_APPLICATIONS_VALUE = "all"; +const SYSTEM_APPLICATION_VALUE = "system"; const getLocaleLabel = (value?: LocaleText | null, fallback = "Unnamed") => value?.en ?? value?.am ?? fallback; @@ -46,6 +62,8 @@ const sortPermissions = (items: PermissionRecord[]) => const PermissionsPage = () => { const [permissions, setPermissions] = useState([]); + const [applications, setApplications] = useState([]); + const [selectedApplication, setSelectedApplication] = useState(ALL_APPLICATIONS_VALUE); const [count, setCount] = useState(0); const [loading, setLoading] = useState(true); const [loadingMore, setLoadingMore] = useState(false); @@ -54,26 +72,35 @@ const PermissionsPage = () => { useEffect(() => { let isMounted = true; - const loadPermissions = async () => { + const loadPageData = async () => { setLoading(true); setErrorMessage(null); try { - const response = await api.get>("/permissions", { - params: { - skip: 0, - take: PAGE_SIZE, - }, - }); + const [permissionsResponse, applicationsResponse] = await Promise.all([ + api.get>("/permissions", { + params: { + skip: 0, + take: PAGE_SIZE, + }, + }), + api.get>("/applications"), + ]); if (!isMounted) { return; } - const items = sortPermissions(getItems(response.data)); + const items = sortPermissions(getItems(permissionsResponse.data)); + const applicationItems = [...getItems(applicationsResponse.data)].sort((left, right) => + getLocaleLabel(left.name, left.key).localeCompare( + getLocaleLabel(right.name, right.key), + ), + ); setPermissions(items); - setCount(response.data.count ?? items.length); + setApplications(applicationItems); + setCount(permissionsResponse.data.count ?? items.length); } catch (error) { if (!isMounted) { return; @@ -91,7 +118,7 @@ const PermissionsPage = () => { } }; - void loadPermissions(); + void loadPageData(); return () => { isMounted = false; @@ -99,6 +126,17 @@ const PermissionsPage = () => { }, []); const hasMore = count > permissions.length; + const filteredPermissions = permissions.filter((permission) => { + if (selectedApplication === ALL_APPLICATIONS_VALUE) { + return true; + } + + if (selectedApplication === SYSTEM_APPLICATION_VALUE) { + return !permission.applicationId; + } + + return permission.applicationId === selectedApplication; + }); const handleLoadMore = async () => { setLoadingMore(true); @@ -142,11 +180,36 @@ const PermissionsPage = () => {

- {count || permissions.length} permissions + {filteredPermissions.length} permissions
+
+
+

+ Application +

+ +
+

+ Filter the IAM permission catalog by application, or view the shared system permissions that do not belong to any application. +

+
+ {loading ? (
Loading permissions... @@ -155,9 +218,9 @@ const PermissionsPage = () => {
{errorMessage}
- ) : permissions.length ? ( + ) : filteredPermissions.length ? (
- {permissions.map((permission) => ( + {filteredPermissions.map((permission) => (
{
) : (
- No permissions are available in the system. + No permissions match the selected application.
)}