From fa6b3ade65a1de10091a70aeb0c9ad7cb283e590 Mon Sep 17 00:00:00 2001
From: Ian Liu <81595625+ianliuwk1019@users.noreply.github.com>
Date: Thu, 12 Sep 2024 15:05:35 -0700
Subject: [PATCH] feat: #1542 User details link and breadcrumb change. (#1589)
---
frontend/src/alltypes.d.ts | 1 +
frontend/src/components/common/Icon.vue | 7 +++-
frontend/src/components/common/SideNav.vue | 7 ++--
.../managePermissions/table/UserDataTable.vue | 42 ++++++++++++-------
.../userDetails/UserDetails.vue | 12 ++++++
frontend/src/layouts/ProtectedLayout.vue | 31 ++++++++------
frontend/src/router/index.ts | 25 ++++++++++-
frontend/src/router/routeHandlers.ts | 10 ++---
frontend/src/router/routeItem.ts | 9 +++-
frontend/src/store/BreadcrumbState.ts | 19 +++++++++
.../src/tests/GrantApplicationAdmin.spec.ts | 12 +++---
11 files changed, 128 insertions(+), 47 deletions(-)
create mode 100644 frontend/src/components/managePermissions/userDetails/UserDetails.vue
diff --git a/frontend/src/alltypes.d.ts b/frontend/src/alltypes.d.ts
index 2b5e70330..e4b798685 100644
--- a/frontend/src/alltypes.d.ts
+++ b/frontend/src/alltypes.d.ts
@@ -20,6 +20,7 @@ declare module '@carbon/icons-vue/es/group--access/16';
declare module '@carbon/icons-vue/es/enterprise/16'
declare module '@carbon/icons-vue/es/user--profile/16'
declare module '@carbon/icons-vue/es/document/16'
+declare module '@carbon/icons-vue/es/recently-viewed/16';
// medium
declare module '@carbon/icons-vue/es/login/20';
diff --git a/frontend/src/components/common/Icon.vue b/frontend/src/components/common/Icon.vue
index 74b05cb8c..3f61be20e 100644
--- a/frontend/src/components/common/Icon.vue
+++ b/frontend/src/components/common/Icon.vue
@@ -1,7 +1,7 @@
+
+
+ Under Construction...
+
+
+
diff --git a/frontend/src/layouts/ProtectedLayout.vue b/frontend/src/layouts/ProtectedLayout.vue
index d8678e59f..e092ae681 100644
--- a/frontend/src/layouts/ProtectedLayout.vue
+++ b/frontend/src/layouts/ProtectedLayout.vue
@@ -1,20 +1,21 @@
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index c42bcc11f..a808e67ea 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -1,6 +1,7 @@
import { createRouter, createWebHistory } from 'vue-router';
import AuthCallback from '@/components/AuthCallbackHandler.vue';
+import UserDetails from '@/components/managePermissions/userDetails/UserDetails.vue';
import NotFound from '@/components/NotFound.vue';
import {
beforeEachRouteHandler,
@@ -9,11 +10,11 @@ import {
import { routeItems } from '@/router/routeItem';
import GrantAccessView from '@/views/GrantAccessView.vue';
import GrantApplicationAdminView from '@/views/GrantApplicationAdminView.vue';
+import GrantDelegatedAdminView from '@/views/GrantDelegatedAdminView.vue';
import LandingView from '@/views/LandingView.vue';
import ManagePermissionsView from '@/views/ManagePermissionsView.vue';
-import { AdminRoleAuthGroup } from 'fam-admin-mgmt-api/model';
-import GrantDelegatedAdminView from '@/views/GrantDelegatedAdminView.vue';
import MyPermissionsView from '@/views/MyPermissionsView.vue';
+import { AdminRoleAuthGroup } from 'fam-admin-mgmt-api/model';
// WARNING: any components referenced below that themselves reference the router cannot be automatically hot-reloaded in local development due to circular dependency
// See vitejs issue https://github.com/vitejs/vite/issues/3033 for discussion.
@@ -114,6 +115,26 @@ const routes = [
component: GrantDelegatedAdminView,
beforeEnter: beforeEnterHandlers[routeItems.grantDelegatedAdmin.name],
},
+ {
+ path: routeItems.userDetails.path,
+ name: routeItems.userDetails.name,
+ meta: {
+ requiresAuth: true,
+ requiresAppSelected: true,
+ title: routeItems.userDetails.label,
+ layout: 'ProtectedLayout',
+ hasBreadcrumb: true,
+ },
+ component: UserDetails,
+
+ /* TODO: 'beforeEnter' placeholder to fetch data from backend*/
+ // beforeEnter: beforeEnterHandlers[routeItems.userDetails.name],
+ // props: (route: any) => {
+ // return {
+ // // TODO: placeholder here to supply props for the component.
+ // };
+ // },
+ },
{
path: routeItems.myPermissions.path,
name: routeItems.myPermissions.name,
diff --git a/frontend/src/router/routeHandlers.ts b/frontend/src/router/routeHandlers.ts
index 089b2e84e..ce5abfa62 100644
--- a/frontend/src/router/routeHandlers.ts
+++ b/frontend/src/router/routeHandlers.ts
@@ -1,10 +1,11 @@
import { FamRouteError, RouteErrorName } from '@/errors/FamCustomError';
import { routeItems } from '@/router/routeItem';
import AuthService from '@/services/AuthService';
+import { EnvironmentSettings } from '@/services/EnvironmentSettings';
import {
fetchApplicationAdmins,
- fetchUserRoleAssignments,
fetchDelegatedAdmins,
+ fetchUserRoleAssignments,
} from '@/services/fetchData';
import { asyncWrap } from '@/services/utils';
import {
@@ -17,7 +18,6 @@ import LoginUserState from '@/store/FamLoginUserState';
import { setRouteToastError as emitRouteToastError } from '@/store/ToastState';
import { AdminRoleAuthGroup } from 'fam-admin-mgmt-api/model';
import type { RouteLocationNormalized } from 'vue-router';
-import { EnvironmentSettings } from '@/services/EnvironmentSettings';
const environmentSettings = new EnvironmentSettings();
const isDevEnvironment = environmentSettings.isDevEnvironment();
@@ -84,7 +84,7 @@ const beforeEnterGrantUserPermissionRoute = async (
return { path: routeItems.dashboard.path };
}
- populateBreadcrumb([routeItems.dashboard, routeItems.grantUserPermission]);
+ populateBreadcrumb([routeItems.dashboard]);
return true;
};
@@ -96,7 +96,7 @@ const beforeEnterGrantApplicationAdminRoute = async (
emitRouteToastError(ACCESS_RESTRICTED_ERROR);
return { path: routeItems.dashboard.path };
}
- populateBreadcrumb([routeItems.dashboard, routeItems.grantAppAdmin]);
+ populateBreadcrumb([routeItems.dashboard]);
return true;
};
@@ -107,7 +107,7 @@ const beforeEnterGrantDelegationAdminRoute = async (
emitRouteToastError(ACCESS_RESTRICTED_ERROR);
return { path: routeItems.dashboard.path };
}
- populateBreadcrumb([routeItems.dashboard, routeItems.grantDelegatedAdmin]);
+ populateBreadcrumb([routeItems.dashboard]);
return true;
};
diff --git a/frontend/src/router/routeItem.ts b/frontend/src/router/routeItem.ts
index 5f454355e..c132c9566 100644
--- a/frontend/src/router/routeItem.ts
+++ b/frontend/src/router/routeItem.ts
@@ -1,5 +1,5 @@
export interface IRouteInfo {
- label: string;
+ label?: string;
path: string;
name: string;
}
@@ -38,5 +38,10 @@ export const routeItems = {
name: 'myPermissions',
path: '/my-permissions',
label: 'Check my permissions',
- }
+ },
+ userDetails: {
+ name: 'viewUserDetails',
+ path: '/user-details/users/:userId/applications/:applicationId',
+ label: 'User details',
+ },
} as RouteItems;
\ No newline at end of file
diff --git a/frontend/src/store/BreadcrumbState.ts b/frontend/src/store/BreadcrumbState.ts
index b63b178e1..8a68beb5a 100644
--- a/frontend/src/store/BreadcrumbState.ts
+++ b/frontend/src/store/BreadcrumbState.ts
@@ -3,6 +3,25 @@ import { ref } from 'vue';
export const breadcrumbState = ref();
+// This is a special item to fit the limitation of "PrimeVue" Breadcrum that
+// the very last item for breadcrum is not rendering as a link. It will be append
+// at the end of the `breadcrumbItem` array
+const crumbEndItem = {
+ name: 'endCrumb',
+ path: "", // deliberately empty.
+ label: undefined // deliberately undefined.
+}
+
+/**
+ * 'breadcrumbItem' items to display for current routed component.
+ * Note:
+ * - We don't need to show the current page crumb item.
+ * - PrmeVue has limitation that the last crumb item will not be rendered as a link.
+ * So `crumbEndItem` is always appended at the end to make first item always is
+ * a link for convenience.
+ * @param breadcrumbItem
+ */
export const populateBreadcrumb = (breadcrumbItem: IRouteInfo[]) => {
+ breadcrumbItem.push(crumbEndItem);
breadcrumbState.value = breadcrumbItem;
};
\ No newline at end of file
diff --git a/frontend/src/tests/GrantApplicationAdmin.spec.ts b/frontend/src/tests/GrantApplicationAdmin.spec.ts
index 916ca54ed..d5051df88 100644
--- a/frontend/src/tests/GrantApplicationAdmin.spec.ts
+++ b/frontend/src/tests/GrantApplicationAdmin.spec.ts
@@ -1,11 +1,11 @@
+import GrantApplicationAdmin from '@/components/grantaccess/GrantApplicationAdmin.vue';
import router, { routes } from '@/router';
-import { mount, VueWrapper } from '@vue/test-utils';
-import { it, describe, beforeEach, expect, afterEach, vi } from 'vitest';
import { routeItems } from '@/router/routeItem';
-import { fixJsdomCssErr } from './common/fixJsdomCssErr';
-import GrantApplicationAdmin from '@/components/grantaccess/GrantApplicationAdmin.vue';
-import waitForExpect from 'wait-for-expect';
import { populateBreadcrumb } from '@/store/BreadcrumbState';
+import { mount, VueWrapper } from '@vue/test-utils';
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+import waitForExpect from 'wait-for-expect';
+import { fixJsdomCssErr } from './common/fixJsdomCssErr';
fixJsdomCssErr();
vi.mock('vue-router', async () => {
@@ -25,7 +25,7 @@ describe('GrantApplicationAdmin', () => {
const routerPushSpy = vi.spyOn(router, 'push');
//populate the breadcrumbState
- const breadcrumbItems = [routeItems.dashboard, routeItems.grantAppAdmin];
+ const breadcrumbItems = [routeItems.dashboard];
populateBreadcrumb(breadcrumbItems);
beforeEach(async () => {
wrapper = mount(GrantApplicationAdmin, {