Skip to content

Commit 78421c8

Browse files
authored
feat: Multi account/rename account (#3711)
1 parent 60250f8 commit 78421c8

File tree

5 files changed

+153
-24
lines changed

5 files changed

+153
-24
lines changed

cypress/e2e/cloud/userAccounts.test.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('Account Page; user with 4 accounts', () => {
2424
it('can change the default account, and then see that it changed on the switch dialog; also checks that the switch button is enabled and disabled correctly', () => {
2525
cy.getByTestID('account-settings--header').should('be.visible')
2626
cy.getByTestID('user-account-switch-btn').should('be.visible')
27-
cy.getByTestID('account-active-name--block').contains('Influx')
27+
cy.getByTestID('input--active-account-name').should('have.value', 'Influx')
2828

2929
cy.getByTestID('user-account-switch-btn').click()
3030

@@ -98,6 +98,41 @@ describe('Account Page; user with one account', () => {
9898
cy.getByTestID('account-settings--header').should('be.visible')
9999
cy.getByTestID('user-account-switch-btn').should('not.exist')
100100

101-
cy.getByTestID('account-active-name--block').contains('Veganomicon')
101+
cy.getByTestID('input--active-account-name').should(
102+
'have.value',
103+
'Veganomicon'
104+
)
105+
})
106+
})
107+
108+
describe('Account Page; user with two accounts', () => {
109+
beforeEach(() => cy.flush().then(() => doSetup(cy, 2)))
110+
111+
it('can get to the account page and rename the active account', () => {
112+
cy.getByTestID('account-settings--header').should('be.visible')
113+
114+
cy.getByTestID('input--active-account-name').should('have.value', 'Influx')
115+
116+
// what can I say? i am a fan
117+
const newName = 'Bruno-no-no-no'
118+
119+
cy.getByTestID('input--active-account-name')
120+
.clear()
121+
.type(newName)
122+
cy.getByTestID('rename-account--button').click()
123+
124+
// test that the notification is up:
125+
cy.getByTestID('notification-success').should('be.visible')
126+
127+
// now; bring up the dialog again, the active name should be changed:
128+
cy.getByTestID('user-account-switch-btn').click()
129+
130+
const prefix = 'accountSwitch-toggle-choice'
131+
132+
cy.getByTestID('switch-account--dialog').within(() => {
133+
cy.getByTestID(`${prefix}-0-ID`).should('be.visible')
134+
cy.getByTestID(`${prefix}-0-ID`).contains(newName)
135+
cy.getByTestID(`${prefix}-0-ID--input`).should('be.checked')
136+
})
102137
})
103138
})

src/accounts/AccountPage.tsx

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
// Libraries
2-
import React, {FC, useContext, useState} from 'react'
3-
import {Button, IconFont, Overlay, Page} from '@influxdata/clockface'
2+
import React, {FC, useContext, useEffect, useState} from 'react'
3+
import {
4+
Button,
5+
ComponentSize,
6+
FlexBox,
7+
FlexDirection,
8+
IconFont,
9+
Input,
10+
InputType,
11+
Overlay,
12+
Page,
13+
} from '@influxdata/clockface'
414

515
// Utils
616
import {pageTitleSuffixer} from 'src/shared/utils/pageTitles'
7-
import {UserAccountProvider} from 'src/accounts/context/userAccount'
17+
import {
18+
UserAccountContext,
19+
UserAccountProvider,
20+
} from 'src/accounts/context/userAccount'
821
import AccountTabContainer from 'src/accounts/AccountTabContainer'
922

10-
import {UserAccountContext} from 'src/accounts/context/userAccount'
11-
1223
import {SwitchAccountOverlay} from 'src/accounts/SwitchAccountOverlay'
1324

1425
const AccountAboutPage: FC = () => {
15-
const {userAccounts} = useContext(UserAccountContext)
26+
const {userAccounts, handleRenameActiveAccount} = useContext(
27+
UserAccountContext
28+
)
1629
const [isSwitchAccountVisible, setSwitchAccountVisible] = useState(false)
1730

1831
/**
@@ -22,10 +35,22 @@ const AccountAboutPage: FC = () => {
2235
*
2336
* and one of the accounts has to be active (the one that the user currently
2437
* is logged in as)
38+
*
39+
* but note that at first load, the accounts may not be loaded yet. hence, the useEffect
40+
* to re-initialize the activeAcctName
2541
*/
42+
const activeAccount =
43+
userAccounts && userAccounts.filter(acct => acct.isActive)[0]
44+
const [activeAcctName, setActiveAcctName] = useState(activeAccount?.name)
2645

27-
const activeAcctName =
28-
userAccounts && userAccounts.filter(acct => acct.isActive)[0].name
46+
// needed b/c the context updates the page once the active accts are loaded
47+
useEffect(() => {
48+
setActiveAcctName(activeAccount?.name)
49+
}, [activeAccount])
50+
51+
const updateAcctName = evt => {
52+
setActiveAcctName(evt.target.value)
53+
}
2954

3055
const showSwitchAccountDialog = () => {
3156
setSwitchAccountVisible(true)
@@ -35,24 +60,45 @@ const AccountAboutPage: FC = () => {
3560
setSwitchAccountVisible(false)
3661
}
3762

63+
const inputStyle = {width: 250}
64+
const labelStyle = {marginBottom: 8}
65+
3866
return (
3967
<Page titleTag={pageTitleSuffixer(['About', 'Account'])}>
4068
<AccountTabContainer activeTab="about">
4169
<>
4270
{userAccounts && userAccounts.length >= 2 && (
43-
<Button
44-
text="Switch Account"
45-
icon={IconFont.Switch_New}
46-
onClick={showSwitchAccountDialog}
47-
testID="user-account-switch-btn"
48-
/>
71+
<React.Fragment>
72+
<Button
73+
text="Switch Account"
74+
icon={IconFont.Switch_New}
75+
onClick={showSwitchAccountDialog}
76+
testID="user-account-switch-btn"
77+
/>
78+
<hr />
79+
</React.Fragment>
4980
)}
50-
<hr />
51-
<h2 data-testid="account-settings--header"> Account Details </h2>
52-
<div data-testid="account-active-name--block">
53-
Currently logged in Active Account: {activeAcctName}
54-
</div>
5581

82+
<h2 data-testid="account-settings--header"> Account Details </h2>
83+
<div style={labelStyle}>Account Name</div>
84+
<FlexBox direction={FlexDirection.Row} margin={ComponentSize.Medium}>
85+
<Input
86+
name="accountName"
87+
testID="input--active-account-name"
88+
type={InputType.Text}
89+
value={activeAcctName}
90+
onChange={updateAcctName}
91+
size={ComponentSize.Medium}
92+
style={inputStyle}
93+
/>
94+
<Button
95+
onClick={() =>
96+
handleRenameActiveAccount(activeAccount.id, activeAcctName)
97+
}
98+
testID="rename-account--button"
99+
text="Save"
100+
/>
101+
</FlexBox>
56102
<Overlay visible={isSwitchAccountVisible}>
57103
<SwitchAccountOverlay onDismissOverlay={closeSwitchAccountDialog} />
58104
</Overlay>

src/accounts/SwitchAccountOverlay.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ export const SwitchAccountOverlay: FC<Props> = ({onDismissOverlay}) => {
101101
const doSwitchAccount = () => {
102102
onDismissOverlay()
103103
event('multiAccount.switchAccount')
104-
105104
window.location.href = `${CLOUD_URL}/accounts/${newAccountId}`
106105
}
107106

@@ -134,7 +133,11 @@ export const SwitchAccountOverlay: FC<Props> = ({onDismissOverlay}) => {
134133

135134
return (
136135
<Overlay.Container maxWidth={630} testID="switch-account--dialog">
137-
<Overlay.Header title="Switch Account" wrapText={true} />
136+
<Overlay.Header
137+
title="Switch Account"
138+
wrapText={true}
139+
onDismiss={onDismissOverlay}
140+
/>
138141
<Overlay.Body>
139142
<ToggleGroup onClickAcct={setNewAccountId} />
140143
</Overlay.Body>

src/accounts/context/userAccount.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ import {Account as UserAccount} from 'src/client/unityRoutes'
77
import {
88
accountDefaultSettingError,
99
accountDefaultSettingSuccess,
10+
accountRenameError,
11+
accountRenameSuccess,
1012
} from 'src/shared/copy/notifications'
1113

1214
// Utils
13-
import {getAccounts, putAccountsDefault} from 'src/client/unityRoutes'
15+
import {
16+
getAccounts,
17+
putAccountsDefault,
18+
patchAccount,
19+
} from 'src/client/unityRoutes'
1420

1521
import {notify} from 'src/shared/actions/notifications'
1622

@@ -25,6 +31,7 @@ export interface UserAccountContextType {
2531
userAccounts: UserAccount[]
2632
handleGetAccounts: () => void
2733
handleSetDefaultAccount: (newId: number) => void
34+
handleRenameActiveAccount: (accountId: number, newName: string) => void
2835
defaultAccountId: number
2936
activeAccountId: number
3037
}
@@ -35,6 +42,7 @@ export const DEFAULT_CONTEXT: UserAccountContextType = {
3542
activeAccountId: -1,
3643
handleGetAccounts: () => {},
3744
handleSetDefaultAccount: () => {},
45+
handleRenameActiveAccount: () => {},
3846
}
3947

4048
export const UserAccountContext = React.createContext<UserAccountContextType>(
@@ -108,6 +116,29 @@ export const UserAccountProvider: FC<Props> = React.memo(({children}) => {
108116
}
109117
}
110118

119+
async function handleRenameActiveAccount(accountId, newName) {
120+
const isActiveAcct = acct => acct.isActive
121+
const activeIndex = userAccounts.findIndex(isActiveAcct)
122+
const oldName = userAccounts[activeIndex].name
123+
124+
try {
125+
const resp = await patchAccount({accountId, data: {name: newName}})
126+
127+
if (resp.status !== 200) {
128+
dispatch(notify(accountRenameError(oldName)))
129+
} else {
130+
dispatch(notify(accountRenameSuccess(oldName, newName)))
131+
event('multiAccount.renameAccount')
132+
133+
// change the name, and reset the active accts:
134+
userAccounts[activeIndex].name = newName
135+
setUserAccounts(userAccounts)
136+
}
137+
} catch (error) {
138+
dispatch(notify(accountRenameError(oldName)))
139+
}
140+
}
141+
111142
useEffect(() => {
112143
handleGetAccounts()
113144
}, [handleGetAccounts, defaultAccountId, activeAccountId])
@@ -120,6 +151,7 @@ export const UserAccountProvider: FC<Props> = React.memo(({children}) => {
120151
activeAccountId,
121152
handleGetAccounts,
122153
handleSetDefaultAccount,
154+
handleRenameActiveAccount,
123155
}}
124156
>
125157
{children}

src/shared/copy/notifications.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,19 @@ export const accountDefaultSettingError = (
839839
message: `Account "${accountName}" was not set as the default account; the default is unchanged`,
840840
})
841841

842+
export const accountRenameSuccess = (
843+
oldAccountName: string,
844+
newAccountName: string
845+
): Notification => ({
846+
...defaultSuccessNotification,
847+
message: `Account "${oldAccountName}" was successfully renamed to "${newAccountName}"`,
848+
})
849+
850+
export const accountRenameError = (accountName: string): Notification => ({
851+
...defaultErrorNotification,
852+
message: `Account "${accountName}" was not renamed; the rename update failed`,
853+
})
854+
842855
export const measurementSchemaAdditionSuccessful = (
843856
bucketName: string,
844857
schemaName: string

0 commit comments

Comments
 (0)