Skip to content

Commit 3399475

Browse files
authored
feat: add user profile page (#5260)
1 parent 6b1551a commit 3399475

File tree

17 files changed

+613
-24
lines changed

17 files changed

+613
-24
lines changed

cypress/e2e/cloud/deepLinks.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ describe('Deep linking', () => {
6060
cy.visit('/me/notebooks')
6161
cy.location('pathname').should('eq', `/orgs/${org.id}/notebooks`)
6262

63+
cy.visit('/me/profile')
64+
cy.location('pathname').should('eq', `/orgs/${org.id}/user/profile`)
65+
6366
cy.visit('/me/pythonclient')
6467
cy.location('pathname').should(
6568
'eq',

src/accounts/context/userAccount.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,18 @@ import {updateDefaultQuartzAccount} from 'src/identity/apis/auth'
3131
export type Props = {
3232
children: JSX.Element
3333
}
34+
35+
interface SetDefaultAccountOptions {
36+
disablePopUps: boolean
37+
}
38+
3439
export interface UserAccountContextType {
3540
userAccounts: UserAccount[]
3641
handleGetAccounts: () => void
37-
handleSetDefaultAccount: (newId: number) => void
42+
handleSetDefaultAccount: (
43+
newId: number,
44+
options?: SetDefaultAccountOptions
45+
) => void
3846
handleRenameActiveAccount: (accountId: number, newName: string) => void
3947
defaultAccountId: number
4048
activeAccountId: number
@@ -103,15 +111,25 @@ export const UserAccountProvider: FC<Props> = React.memo(({children}) => {
103111
}
104112
}, [dispatch, defaultAccountId])
105113

106-
async function handleSetDefaultAccount(newDefaultAcctId) {
114+
async function handleSetDefaultAccount(
115+
newDefaultAcctId: number,
116+
setDefaultAccountOptions?: SetDefaultAccountOptions
117+
) {
107118
const accountName = getAccountNameById(newDefaultAcctId)
108119

109120
try {
110121
await updateDefaultQuartzAccount(newDefaultAcctId)
111122
setDefaultAccountId(newDefaultAcctId)
112-
dispatch(notify(accountDefaultSettingSuccess(accountName)))
123+
124+
if (!setDefaultAccountOptions?.disablePopUps) {
125+
dispatch(notify(accountDefaultSettingSuccess(accountName)))
126+
}
113127
} catch (error) {
114-
dispatch(notify(accountDefaultSettingError(accountName)))
128+
if (!setDefaultAccountOptions?.disablePopUps) {
129+
dispatch(notify(accountDefaultSettingError(accountName)))
130+
} else {
131+
throw Error('Failed to update default account.')
132+
}
115133
}
116134
}
117135

src/identity/components/GlobalHeader/GlobalHeader.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from '@influxdata/clockface'
1212

1313
// Selectors and Context
14+
import {getOrg} from 'src/organizations/selectors'
1415
import {selectQuartzIdentity} from 'src/identity/selectors'
1516
import {UserAccountContext} from 'src/accounts/context/userAccount'
1617

@@ -34,6 +35,9 @@ import IdentityUserAvatar from 'src/identity/components/GlobalHeader/IdentityUse
3435
export const GlobalHeader: FC = () => {
3536
const dispatch = useDispatch()
3637
const identity = useSelector(selectQuartzIdentity)
38+
const {user} = identity.currentIdentity
39+
const org = useSelector(getOrg)
40+
3741
const orgsList = identity.quartzOrganizations.orgs
3842
const {userAccounts} = useContext(UserAccountContext)
3943

@@ -94,7 +98,12 @@ export const GlobalHeader: FC = () => {
9498
</>
9599
)}
96100
</FlexBox>
97-
<IdentityUserAvatar user={identity.currentIdentity.user} />
101+
<IdentityUserAvatar
102+
firstName={user.firstName}
103+
lastName={user.lastName}
104+
email={user.email}
105+
orgId={org.id}
106+
/>
98107
</FlexBox>
99108
)
100109
}

src/identity/components/GlobalHeader/GlobalHeaderDropdown/GlobalHeaderDropdown.scss

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,29 @@
1616
z-index: 0;
1717
}
1818

19+
.global-header--list {
20+
/* Works on Firefox */
21+
scrollbar-width: thin;
22+
}
23+
24+
.global-header--list::-webkit-scrollbar {
25+
width: 4px !important;
26+
}
27+
28+
.global-header--list::-webkit-scrollbar-track {
29+
background-color: rgba($cf-grey-95, 0.15);
30+
border-radius: 20px;
31+
}
32+
33+
.global-header--list::-webkit-scrollbar-thumb {
34+
background: linear-gradient(
35+
to right,
36+
rgba(255, 255, 255, 0.25) 0%,
37+
rgba(255, 255, 255, 0.25) 100%
38+
);
39+
border-radius: 20px;
40+
}
41+
1942
.button-icon {
2043
color: $cf-grey-75;
2144
margin-right: 8px;

src/identity/components/GlobalHeader/GlobalHeaderDropdown/GlobalHeaderTypeAheadMenu.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export class GlobalHeaderTypeAheadMenu extends React.Component<Props, State> {
109109
width="100%"
110110
layout="vertical"
111111
itemData={queryResults}
112+
className="global-header--list"
112113
>
113114
{({data, index, style}) => {
114115
const value = data[index]

src/identity/components/GlobalHeader/IdentityUserAvatar.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React from 'react'
2-
import {IdentityUser} from 'src/client/unityRoutes'
32
import classnames from 'classnames'
43
import {
54
Button,
@@ -16,7 +15,10 @@ import './UserPopoverStyles.scss'
1615
import {Link} from 'react-router-dom'
1716

1817
type Props = {
19-
user: IdentityUser
18+
firstName: string
19+
lastName: string
20+
email: string
21+
orgId: string
2022
}
2123

2224
type State = {
@@ -32,9 +34,7 @@ class IdentityUserAvatar extends React.Component<Props, State> {
3234
}
3335

3436
private getInitials = (): string => {
35-
const {user} = this.props
36-
const firstName = user.firstName
37-
const lastName = user.lastName
37+
const {firstName, lastName} = this.props
3838
const initials = `${firstName.charAt(0)}${lastName.charAt(0)}`
3939
return initials
4040
}
@@ -49,18 +49,22 @@ class IdentityUserAvatar extends React.Component<Props, State> {
4949
}
5050

5151
private getUserPopoverContents = () => {
52-
const {user} = this.props
52+
const {firstName, lastName, email, orgId} = this.props
53+
5354
return (
5455
<>
5556
<div className="user-popover-header">
5657
<div className="user-popover-header-name">
57-
{user.firstName} {user.lastName}
58+
{firstName} {lastName}
5859
</div>
59-
<div className="user-popover-header-email">{user.email}</div>
60+
<div className="user-popover-header-email">{email}</div>
6061
<hr />
6162
</div>
6263
<div className="user-popover-footer">
63-
<Link className="user-popover-footer--button" to="/">
64+
<Link
65+
className="user-popover-footer--button"
66+
to={`/orgs/${orgId}/user/profile`}
67+
>
6468
<Icon
6569
glyph={IconFont.User}
6670
className="user-popover-footer--button-icon"
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Libraries
2+
import React, {FC, SetStateAction} from 'react'
3+
import {
4+
AlignItems,
5+
FlexBox,
6+
FlexDirection,
7+
FontWeight,
8+
Heading,
9+
HeadingElement,
10+
JustifyContent,
11+
} from '@influxdata/clockface'
12+
13+
// Components
14+
import {
15+
DefaultDropdown,
16+
EntityLabel,
17+
} from 'src/identity/components/userprofile/DefaultDropdown'
18+
19+
// Types
20+
import {UserAccount} from 'src/client/unityRoutes'
21+
22+
interface Props {
23+
accounts: UserAccount[]
24+
selectedAccount: UserAccount
25+
setSelectedAccount: (action: SetStateAction<any>) => void
26+
}
27+
28+
export const DefaultAccountForm: FC<Props> = ({
29+
accounts,
30+
selectedAccount,
31+
setSelectedAccount,
32+
}) => {
33+
return (
34+
<FlexBox
35+
direction={FlexDirection.Column}
36+
alignItems={AlignItems.FlexStart}
37+
justifyContent={JustifyContent.FlexStart}
38+
>
39+
<Heading
40+
weight={FontWeight.Bold}
41+
element={HeadingElement.H4}
42+
className="change-default-account-org--header"
43+
>
44+
Default Account
45+
</Heading>
46+
<div className="change-default-account-org--text">
47+
Select the account you want to see when you first log in
48+
</div>
49+
{accounts && (
50+
<DefaultDropdown
51+
entityLabel={EntityLabel.DefaultAccount}
52+
defaultEntity={selectedAccount}
53+
entityList={accounts}
54+
changeSelectedEntity={setSelectedAccount}
55+
/>
56+
)}
57+
</FlexBox>
58+
)
59+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Libraries
2+
import React, {FC, SetStateAction} from 'react'
3+
import {
4+
AlignItems,
5+
ComponentSize,
6+
FlexBox,
7+
FlexDirection,
8+
Form,
9+
} from '@influxdata/clockface'
10+
11+
// Components
12+
import {GlobalHeaderDropdown} from 'src/identity/components/GlobalHeader/GlobalHeaderDropdown'
13+
14+
// Types
15+
import {OrganizationSummaries, UserAccount} from 'src/client/unityRoutes'
16+
type Entity = OrganizationSummaries[number] | UserAccount
17+
18+
export enum EntityLabel {
19+
DefaultAccount = 'Account',
20+
DefaultOrg = ' Organization',
21+
}
22+
23+
// Styles
24+
import 'src/identity/components/userprofile/UserProfile.scss'
25+
26+
const globalHeaderStyle = {width: '368px', backgroundColor: '#232533'}
27+
28+
interface Props {
29+
entityLabel: string
30+
defaultEntity: Entity
31+
entityList: Entity[]
32+
changeSelectedEntity: (action: SetStateAction<any>) => void
33+
}
34+
35+
export const DefaultDropdown: FC<Props> = ({
36+
entityLabel,
37+
entityList,
38+
changeSelectedEntity,
39+
defaultEntity,
40+
}) => {
41+
return (
42+
<FlexBox
43+
direction={FlexDirection.Column}
44+
margin={ComponentSize.Large}
45+
alignItems={AlignItems.FlexStart}
46+
className="change-default-account-org--dropdown-flexbox"
47+
>
48+
<Form.Element
49+
label={`Default ${entityLabel}`}
50+
className="user-profile-page--form-element"
51+
>
52+
<GlobalHeaderDropdown
53+
mainMenuOptions={[]}
54+
onlyRenderSubmenu={true}
55+
typeAheadMenuOptions={entityList}
56+
typeAheadInputPlaceholder={`Search ${entityLabel}s ...`}
57+
typeAheadSelectedOption={defaultEntity}
58+
typeAheadOnSelectOption={changeSelectedEntity}
59+
style={globalHeaderStyle}
60+
/>
61+
</Form.Element>
62+
</FlexBox>
63+
)
64+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Libraries
2+
import React, {FC, SetStateAction} from 'react'
3+
import {
4+
AlignItems,
5+
ComponentStatus,
6+
FlexBox,
7+
FlexDirection,
8+
FontWeight,
9+
Heading,
10+
HeadingElement,
11+
JustifyContent,
12+
} from '@influxdata/clockface'
13+
14+
// Components
15+
import {
16+
DefaultDropdown,
17+
EntityLabel,
18+
} from 'src/identity/components/userprofile/DefaultDropdown'
19+
import {UserProfileInput} from 'src/identity/components/userprofile/UserProfileInput'
20+
21+
// Types
22+
import {CurrentAccount} from 'src/identity/apis/auth'
23+
import {OrganizationSummaries, UserAccount} from 'src/client/unityRoutes'
24+
25+
interface Props {
26+
accounts: UserAccount[]
27+
loggedInAccount: CurrentAccount
28+
orgs: OrganizationSummaries
29+
selectedOrg: OrganizationSummaries[number]
30+
setSelectedOrg: (action: SetStateAction<any>) => void
31+
}
32+
33+
export const DefaultOrgForm: FC<Props> = ({
34+
accounts,
35+
loggedInAccount,
36+
orgs,
37+
selectedOrg,
38+
setSelectedOrg,
39+
}) => {
40+
return (
41+
<>
42+
<FlexBox
43+
direction={FlexDirection.Column}
44+
alignItems={AlignItems.FlexStart}
45+
justifyContent={JustifyContent.FlexStart}
46+
>
47+
<Heading
48+
weight={FontWeight.Bold}
49+
element={HeadingElement.H4}
50+
className="change-default-account-org--header"
51+
>
52+
Default Organization
53+
</Heading>
54+
<div className="change-default-account-org--text">
55+
Select the organization you want to see by default when logging into
56+
your current account
57+
</div>
58+
</FlexBox>
59+
60+
<FlexBox
61+
direction={FlexDirection.Row}
62+
alignItems={AlignItems.FlexStart}
63+
justifyContent={JustifyContent.FlexStart}
64+
>
65+
{accounts && (
66+
<UserProfileInput
67+
status={ComponentStatus.Disabled}
68+
header="Account"
69+
text={loggedInAccount.name}
70+
/>
71+
)}
72+
{orgs && (
73+
<DefaultDropdown
74+
entityLabel={EntityLabel.DefaultOrg}
75+
defaultEntity={selectedOrg}
76+
entityList={orgs}
77+
changeSelectedEntity={setSelectedOrg}
78+
/>
79+
)}
80+
</FlexBox>
81+
</>
82+
)
83+
}

0 commit comments

Comments
 (0)