From b2db804ae2479cdec5f0213a27149ca4e403366d Mon Sep 17 00:00:00 2001 From: jazzgrewal Date: Thu, 31 Aug 2023 12:13:36 -0700 Subject: [PATCH 1/5] Added the User Profile sidebar with test data and cleaned up the Dashboard --- .../components/AvatarImage/AvatarImage.scss | 13 +++ frontend/src/components/AvatarImage/index.tsx | 18 +++++ .../src/components/BCHeaderwSide/index.tsx | 57 +------------ .../src/components/MyProfile/MyProfile.scss | 29 +++++++ frontend/src/components/MyProfile/index.tsx | 79 +++++++++++++++++++ frontend/src/screens/Dashboard/index.tsx | 31 +------- 6 files changed, 142 insertions(+), 85 deletions(-) create mode 100644 frontend/src/components/AvatarImage/AvatarImage.scss create mode 100644 frontend/src/components/AvatarImage/index.tsx create mode 100644 frontend/src/components/MyProfile/MyProfile.scss create mode 100644 frontend/src/components/MyProfile/index.tsx diff --git a/frontend/src/components/AvatarImage/AvatarImage.scss b/frontend/src/components/AvatarImage/AvatarImage.scss new file mode 100644 index 00000000..e48ff6d4 --- /dev/null +++ b/frontend/src/components/AvatarImage/AvatarImage.scss @@ -0,0 +1,13 @@ +.profile-image { + border-radius: 50%; +} + +.small { + width: 1.5rem; + height: 1.5rem; +} + +.large { + width: 4rem; + height: 4rem; +} \ No newline at end of file diff --git a/frontend/src/components/AvatarImage/index.tsx b/frontend/src/components/AvatarImage/index.tsx new file mode 100644 index 00000000..a1e79c19 --- /dev/null +++ b/frontend/src/components/AvatarImage/index.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import emptyUser from '../../assets/img/emptyUser.jpg'; +import './AvatarImage.scss'; + +type Size = 'small' | 'large'; + +interface AvatarImageProps { + userName: string; + source?: string; + size: Size; +} + +const AvatarImage = ({ userName, source, size }: AvatarImageProps) => ( + {`${userName}`} +); + +export default AvatarImage; diff --git a/frontend/src/components/BCHeaderwSide/index.tsx b/frontend/src/components/BCHeaderwSide/index.tsx index 9df1cd73..f2da0ec3 100644 --- a/frontend/src/components/BCHeaderwSide/index.tsx +++ b/frontend/src/components/BCHeaderwSide/index.tsx @@ -21,6 +21,7 @@ import './BCHeaderwSide.scss'; import RightPanelTitle from '../RightPanelTitle'; import { env } from '../../env'; import ThemeToggle from '../ThemeToggle'; +import MyProfile from '../MyProfile'; interface ListItem { name: string; @@ -43,60 +44,6 @@ const listItems = [ link: '/dashboard', disabled: false }, - { - name: 'Seedlots', - icon: 'SoilMoistureField', - link: '/seedlot', - disabled: true - }, - { - name: 'Seedlings', - icon: 'CropGrowth', - link: '#', - disabled: true - }, - { - name: 'Nurseries', - icon: 'CropHealth', - link: '#', - disabled: true - }, - { - name: 'Orchards', - icon: 'MapBoundaryVegetation', - link: '#', - disabled: true - }, - { - name: 'Reports', - icon: 'Report', - link: '/reports', - disabled: true - }, - { - name: 'Tests', - icon: 'Chemistry', - link: '#', - disabled: true - }, - { - name: 'Parent tree', - icon: 'Tree', - link: '#', - disabled: true - }, - { - name: 'Tree seed center', - icon: 'Enterprise', - link: '#', - disabled: true - }, - { - name: 'Financial', - icon: 'Money', - link: '#', - disabled: true - } ] }, { @@ -211,7 +158,7 @@ const BCHeaderwSide = () => { title="My Profile" closeFn={closeMyProfilePanel} /> - + diff --git a/frontend/src/components/MyProfile/MyProfile.scss b/frontend/src/components/MyProfile/MyProfile.scss new file mode 100644 index 00000000..cc131573 --- /dev/null +++ b/frontend/src/components/MyProfile/MyProfile.scss @@ -0,0 +1,29 @@ +@use '@bcgov-nr/nr-fsa-theme/design-tokens/variables.scss' as vars; + +.user-info-section { + padding: 1rem; + display: flex; +} + +.user-data { + margin-left: 2rem; +} + +.user-name { + font-weight: bold; +} + +.divisory { + background-color: var(--#{vars.$bcgov-prefix}-border-subtle-01); + border: 0; + height: 1px; +} + +.account-nav a { + width: 100%; + font-weight: bold; +} + +.cursor-pointer { + cursor: pointer; +} diff --git a/frontend/src/components/MyProfile/index.tsx b/frontend/src/components/MyProfile/index.tsx new file mode 100644 index 00000000..ab46f1ba --- /dev/null +++ b/frontend/src/components/MyProfile/index.tsx @@ -0,0 +1,79 @@ +import React, { useCallback, useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { + SideNavLink +} from '@carbon/react'; +import * as Icons from '@carbon/icons-react'; + + +import AvatarImage from '../AvatarImage'; + +import { useThemePreference } from '../../utils/ThemePreference'; + +import './MyProfile.scss'; +import { logout } from '../../services/AuthService'; + +const MyProfile = () => { + const { theme, setTheme } = useThemePreference(); + const userData = {firstName:'Jazz', lastName:"Grewal", idirUsername:"Jasgrewal", email:"jazz@test.com"}; + + const [goToURL, setGoToURL] = useState(''); + const [goTo, setGoTo] = useState(false); + + const navigate = useNavigate(); + + const changeTheme = () => { + if (theme === 'g10') { + setTheme('g100'); + localStorage.setItem('mode', 'dark'); + } + if (theme === 'g100') { + setTheme('g10'); + localStorage.setItem('mode', 'light'); + } + }; + + useEffect(() => { + if (goTo) { + setGoTo(false); + navigate(goToURL); + } + }, [goTo]); + + return ( + <> +
+
+ +
+
+

{`${userData.firstName} ${userData.lastName}`}

+

{`IDIR: ${userData.idirUsername}`}

+

{userData.email}

+
+
+
+ + + ); +}; + +export default MyProfile; diff --git a/frontend/src/screens/Dashboard/index.tsx b/frontend/src/screens/Dashboard/index.tsx index 2c6798dd..f6b00b30 100644 --- a/frontend/src/screens/Dashboard/index.tsx +++ b/frontend/src/screens/Dashboard/index.tsx @@ -1,9 +1,5 @@ import React from "react"; -import { logout } from "../../services/AuthService"; -import { Button } from "@carbon/react"; -import { Asleep, Light } from '@carbon/icons-react'; import { useThemePreference } from "../../utils/ThemePreference"; -import { toggleTheme } from "../../utils/ThemeFunction"; const Dashboard: React.FC = () => { @@ -14,35 +10,10 @@ const Dashboard: React.FC = () => { <>
-
-
Hi There
-
-
Welcome to the Dashboard Page
-
-
- -
-
- Column 1 -
-
- Column 2 -
-
- Column 3 +
Hi There,
Welcome to Main Screen
-
-
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Qui cupiditate nulla dolor debitis harum dignissimos velit, molestias cumque magni laborum explicabo eaque minima. Quos incidunt cupiditate id eveniet sed. Assumenda quia eveniet animi maiores harum.
-
- -
- -
-
- -
); From d972846864ce6ace6fc691b12e6d61816cef3d83 Mon Sep 17 00:00:00 2001 From: jazzgrewal Date: Fri, 1 Sep 2023 14:16:14 -0700 Subject: [PATCH 2/5] added the User initial object and also extracted the user info into Redux --- frontend/src/actions/userAction.ts | 39 +++++++++++-------- .../components/AvatarImage/AvatarImage.scss | 9 ++++- frontend/src/components/AvatarImage/index.tsx | 25 +++++++++--- frontend/src/components/MyProfile/index.tsx | 2 +- frontend/src/services/AuthService.ts | 20 ++++++++-- 5 files changed, 66 insertions(+), 29 deletions(-) diff --git a/frontend/src/actions/userAction.ts b/frontend/src/actions/userAction.ts index cdba5978..8c001543 100644 --- a/frontend/src/actions/userAction.ts +++ b/frontend/src/actions/userAction.ts @@ -1,21 +1,26 @@ import { USER_DETAILS_REQUEST, USER_DETAILS_SUCCESS, USER_DETAILS_FAIL } from '../constants/userConstants'; import { isCurrentAuthUser } from "../services/AuthService"; -export const getUserDetails = () => async (dispatch:any) => { - try { - dispatch({ - type: USER_DETAILS_REQUEST, - }) - //readt the localStorage Here - const data = await isCurrentAuthUser(); - dispatch({ - type: USER_DETAILS_SUCCESS, - payload: {TestUser:'Jazz as always',isLoggedIn:data}, - }) - } catch (error) { - dispatch({ - type: USER_DETAILS_FAIL, - payload: {error:error}, - }) - } +const FAM_LOGIN_USER = 'famLoginUser'; +export const getUserDetails = () => async (dispatch: any) => { + try { + dispatch({ + type: USER_DETAILS_REQUEST, + }); + + const userJSON = localStorage.getItem(FAM_LOGIN_USER); // Retrieve the JSON string from local storage + const user = userJSON ? JSON.parse(userJSON) : null; // Parse the JSON string to a JavaScript object + + const data = await isCurrentAuthUser(); + + dispatch({ + type: USER_DETAILS_SUCCESS, + payload: { user: user, isLoggedIn: data }, + }); + } catch (error) { + dispatch({ + type: USER_DETAILS_FAIL, + payload: { error: error }, + }); } +}; diff --git a/frontend/src/components/AvatarImage/AvatarImage.scss b/frontend/src/components/AvatarImage/AvatarImage.scss index e48ff6d4..f2f2d131 100644 --- a/frontend/src/components/AvatarImage/AvatarImage.scss +++ b/frontend/src/components/AvatarImage/AvatarImage.scss @@ -1,5 +1,12 @@ .profile-image { border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + font-weight: bold; + background-color: #c7d4e7; + color: #072251; + font-size: 1.4rem; } .small { @@ -10,4 +17,4 @@ .large { width: 4rem; height: 4rem; -} \ No newline at end of file +} diff --git a/frontend/src/components/AvatarImage/index.tsx b/frontend/src/components/AvatarImage/index.tsx index a1e79c19..52dc9c83 100644 --- a/frontend/src/components/AvatarImage/index.tsx +++ b/frontend/src/components/AvatarImage/index.tsx @@ -1,18 +1,31 @@ import React from 'react'; - -import emptyUser from '../../assets/img/emptyUser.jpg'; import './AvatarImage.scss'; type Size = 'small' | 'large'; interface AvatarImageProps { userName: string; - source?: string; size: Size; } -const AvatarImage = ({ userName, source, size }: AvatarImageProps) => ( - {`${userName}`} -); +const getInitials = (userName: string) => { + const nameParts = userName.split(' '); + if (nameParts.length >= 2) { + return nameParts[0][0] + nameParts[1][0]; + } else if (nameParts.length === 1) { + return nameParts[0][0]; + } + return ''; +}; + +const AvatarImage = ({ userName, size }: AvatarImageProps) => { + const initials = getInitials(userName); + + return ( +
+
{initials}
+
+ ); +}; export default AvatarImage; diff --git a/frontend/src/components/MyProfile/index.tsx b/frontend/src/components/MyProfile/index.tsx index ab46f1ba..26122f1b 100644 --- a/frontend/src/components/MyProfile/index.tsx +++ b/frontend/src/components/MyProfile/index.tsx @@ -16,7 +16,7 @@ import { logout } from '../../services/AuthService'; const MyProfile = () => { const { theme, setTheme } = useThemePreference(); - const userData = {firstName:'Jazz', lastName:"Grewal", idirUsername:"Jasgrewal", email:"jazz@test.com"}; + const userData = {firstName:'Catherine', lastName:"Meng", idirUsername:"Jasgrewal", email:"jazz@test.com"}; const [goToURL, setGoToURL] = useState(''); const [goTo, setGoTo] = useState(false); diff --git a/frontend/src/services/AuthService.ts b/frontend/src/services/AuthService.ts index f2cc0607..5a1b23a3 100644 --- a/frontend/src/services/AuthService.ts +++ b/frontend/src/services/AuthService.ts @@ -90,15 +90,27 @@ async function refreshToken(): Promise { function parseToken(authToken: CognitoUserSession): FamLoginUser { const decodedIdToken = authToken.getIdToken().decodePayload(); const decodedAccessToken = authToken.getAccessToken().decodePayload(); + + // Extract the first name and last name from the displayName and remove unwanted part + const displayName = decodedIdToken['custom:idp_display_name']; + const [lastName, firstName] = displayName.split(', '); + const sanitizedFirstName = firstName.split(' ')[0].trim(); // Remove unwanted part + const famLoginUser = { - username: decodedIdToken['custom:idp_username'], - idpProvider: decodedIdToken['identities']['providerName'], - roles: decodedAccessToken['cognito:groups'], - authToken: authToken, + userName: decodedIdToken['custom:idp_username'], + displayName, + email: decodedIdToken['email'], + idpProvider: decodedIdToken['identities']['providerName'], + roles: decodedAccessToken['cognito:groups'], + authToken: authToken, + firstName: sanitizedFirstName, + lastName, // Add lastName field }; + return famLoginUser; } + function removeFamUser() { storeFamUser(undefined); // clean up local storage for selected application From ff1a9cfc1f26ebb3d5a61faa9a39baf7acd27953 Mon Sep 17 00:00:00 2001 From: jazzgrewal Date: Tue, 5 Sep 2023 11:48:11 -0700 Subject: [PATCH 3/5] Linked the userDetails in the Redux, and now displaying the data in sidebar --- frontend/src/actions/userAction.ts | 2 +- frontend/src/components/MyProfile/index.tsx | 12 ++++++------ frontend/src/services/AuthService.ts | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/frontend/src/actions/userAction.ts b/frontend/src/actions/userAction.ts index 8c001543..f438df4b 100644 --- a/frontend/src/actions/userAction.ts +++ b/frontend/src/actions/userAction.ts @@ -15,7 +15,7 @@ export const getUserDetails = () => async (dispatch: any) => { dispatch({ type: USER_DETAILS_SUCCESS, - payload: { user: user, isLoggedIn: data }, + payload: { ...user, isLoggedIn: data }, }); } catch (error) { dispatch({ diff --git a/frontend/src/components/MyProfile/index.tsx b/frontend/src/components/MyProfile/index.tsx index 26122f1b..ef49a56f 100644 --- a/frontend/src/components/MyProfile/index.tsx +++ b/frontend/src/components/MyProfile/index.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; - +import { useSelector } from 'react-redux'; import { SideNavLink } from '@carbon/react'; @@ -17,7 +17,7 @@ import { logout } from '../../services/AuthService'; const MyProfile = () => { const { theme, setTheme } = useThemePreference(); const userData = {firstName:'Catherine', lastName:"Meng", idirUsername:"Jasgrewal", email:"jazz@test.com"}; - + const userDetails = useSelector((state:any) => state.userDetails) const [goToURL, setGoToURL] = useState(''); const [goTo, setGoTo] = useState(false); @@ -45,12 +45,12 @@ const MyProfile = () => { <>
- +
-

{`${userData.firstName} ${userData.lastName}`}

-

{`IDIR: ${userData.idirUsername}`}

-

{userData.email}

+

{`${userDetails.user.firstName} ${userDetails.user.lastName}`}

+

{`IDIR: ${userDetails.user.userName}`}

+

{`Email:${userDetails.user.email}`}


diff --git a/frontend/src/services/AuthService.ts b/frontend/src/services/AuthService.ts index 5a1b23a3..800bbb0e 100644 --- a/frontend/src/services/AuthService.ts +++ b/frontend/src/services/AuthService.ts @@ -95,7 +95,6 @@ function parseToken(authToken: CognitoUserSession): FamLoginUser { const displayName = decodedIdToken['custom:idp_display_name']; const [lastName, firstName] = displayName.split(', '); const sanitizedFirstName = firstName.split(' ')[0].trim(); // Remove unwanted part - const famLoginUser = { userName: decodedIdToken['custom:idp_username'], displayName, From 35e7236c480ada38cfaa6bc26a1a68a2bbec4a23 Mon Sep 17 00:00:00 2001 From: jazzgrewal Date: Wed, 6 Sep 2023 10:08:47 -0700 Subject: [PATCH 4/5] Fixed the login info issue, rendering the userInfo successfully --- frontend/src/actions/userAction.ts | 6 +++--- frontend/src/services/AuthService.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/actions/userAction.ts b/frontend/src/actions/userAction.ts index f438df4b..717ecaad 100644 --- a/frontend/src/actions/userAction.ts +++ b/frontend/src/actions/userAction.ts @@ -7,15 +7,15 @@ export const getUserDetails = () => async (dispatch: any) => { dispatch({ type: USER_DETAILS_REQUEST, }); + //first call the isCurrent and only after that extract the JSON + const data = await isCurrentAuthUser(); const userJSON = localStorage.getItem(FAM_LOGIN_USER); // Retrieve the JSON string from local storage const user = userJSON ? JSON.parse(userJSON) : null; // Parse the JSON string to a JavaScript object - const data = await isCurrentAuthUser(); - dispatch({ type: USER_DETAILS_SUCCESS, - payload: { ...user, isLoggedIn: data }, + payload: { ...user, isLoggedIn: data}, }); } catch (error) { dispatch({ diff --git a/frontend/src/services/AuthService.ts b/frontend/src/services/AuthService.ts index 800bbb0e..8c141e59 100644 --- a/frontend/src/services/AuthService.ts +++ b/frontend/src/services/AuthService.ts @@ -69,7 +69,7 @@ async function refreshToken(): Promise { console.log('currentAuthToken: ', currentAuthToken); const famLoginUser = parseToken(currentAuthToken); - storeFamUser(famLoginUser); + await storeFamUser(famLoginUser); return famLoginUser; } catch (error) { console.error( From bc1278df1a20219a89ce0c2acb6949922443e1f0 Mon Sep 17 00:00:00 2001 From: jazzgrewal Date: Thu, 7 Sep 2023 19:27:37 -0700 Subject: [PATCH 5/5] Switched to older version of nr-fsa-theme --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- frontend/src/index.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 61aa914c..65688afb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@aws-amplify/ui-react": "^5.0.0", - "@bcgov-nr/nr-fsa-theme": "^1.0.7", + "@bcgov-nr/nr-fsa-theme": "^1.0.10", "@carbon/react": "^1.27.0", "@testing-library/jest-dom": "^6.1.2", "@testing-library/react": "^14.0.0", @@ -7767,9 +7767,9 @@ } }, "node_modules/@bcgov-nr/nr-fsa-theme": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@bcgov-nr/nr-fsa-theme/-/nr-fsa-theme-1.1.5.tgz", - "integrity": "sha512-gQ7k76PXcTlWDOo6lrYNujgc5t7cY4r6gVS8m4bvkIbbgKQNux4kV5BuEtx616fubl61FZp+zDxRSMfrZz1Dog==" + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@bcgov-nr/nr-fsa-theme/-/nr-fsa-theme-1.0.10.tgz", + "integrity": "sha512-3YGie8ITDyCUNWRMJhJNNEa5W61WotxjaeMLiBXCsNwrWh+9BeBvlLBN8etxqnCyCbx1QkUAlADGZf05gSYwnQ==" }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", diff --git a/frontend/package.json b/frontend/package.json index 173cd333..6cc122b3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@aws-amplify/ui-react": "^5.0.0", - "@bcgov-nr/nr-fsa-theme": "^1.0.7", + "@bcgov-nr/nr-fsa-theme": "^1.0.10", "@carbon/react": "^1.27.0", "@testing-library/jest-dom": "^6.1.2", "@testing-library/react": "^14.0.0", diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 9c276de4..aad7aa40 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -12,7 +12,7 @@ const root = createRoot(container); root.render( - +