Skip to content

Commit efeda96

Browse files
authored
Integrate "change email" into account settings page (#8840)
This PR moves the standalone "Change Email" view into the new account settings akin to passwords. <img width="1007" height="586" alt="Screenshot 2025-08-07 at 10 14 56" src="https://github.com/user-attachments/assets/4563e87a-350a-4f01-88c8-62171c665ed9" /> ### Steps to test: - Go to Account Settings - Click on Edit icon next to "Email" - Change Email to confirm that the form is still working - https://changeemailview.webknossos.xyz/ ### Issues: - Follow up to #8672 and #2494 ------ (Please delete unneeded items, merge only when none are left open) - [x] Added changelog entry (create a `$PR_NUMBER.md` file in `unreleased_changes` or use `./tools/create-changelog-entry.py`) - [ ] Added migration guide entry if applicable (edit the same file as for the changelog) - [ ] Updated [documentation](../blob/master/docs) if applicable - [ ] Adapted [wk-libs python client](https://github.com/scalableminds/webknossos-libs/tree/master/webknossos/webknossos/client) if relevant API parts change - [ ] Removed dev-only changes like prints and application.conf edits - [x] Considered [common edge cases](../blob/master/.github/common_edge_cases.md) - [ ] Needs datastore update after deployment
1 parent 16af6c8 commit efeda96

File tree

6 files changed

+125
-130
lines changed

6 files changed

+125
-130
lines changed

frontend/javascripts/admin/account/account_password_view.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ function AccountPasswordView() {
152152
}
153153

154154
function handleResetPassword() {
155-
setResetPasswordVisible(true);
155+
setResetPasswordVisible(!isResetPasswordVisible);
156156
}
157157

158158
const passKeyList: SettingsCardProps[] = [

frontend/javascripts/admin/account/account_profile_view.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { CheckOutlined, DownOutlined } from "@ant-design/icons";
1+
import { CheckOutlined, DownOutlined, EditOutlined } from "@ant-design/icons";
2+
import ChangeEmailView from "admin/auth/change_email_view";
23
import { updateSelectedThemeOfUser } from "admin/rest_api";
3-
import { Col, Dropdown, Row } from "antd";
4+
import { Button, Col, Dropdown, Row } from "antd";
45
import { useWkSelector } from "libs/react_hooks";
56
import * as Utils from "libs/utils";
7+
import { useState } from "react";
68
import { getSystemColorTheme } from "theme";
79
import type { APIUserTheme } from "types/api_types";
810
import { formatUserName } from "viewer/model/accessors/user_accessor";
@@ -16,7 +18,7 @@ function AccountProfileView() {
1618
const activeUser = useWkSelector((state) => state.activeUser);
1719
const activeOrganization = useWkSelector((state) => state.activeOrganization);
1820
const { selectedTheme } = activeUser || { selectedTheme: "auto" };
19-
21+
const [isChangeEmailVisible, setChangeEmailVisible] = useState(false);
2022
if (!activeUser) return null;
2123

2224
const role = Utils.isUserAdmin(activeUser)
@@ -63,7 +65,20 @@ function AccountProfileView() {
6365
},
6466
{
6567
title: "Email",
66-
content: activeUser.email,
68+
content: isChangeEmailVisible ? (
69+
<ChangeEmailView onCancel={() => setChangeEmailVisible(false)} />
70+
) : (
71+
activeUser.email
72+
),
73+
action: (
74+
<Button
75+
type="default"
76+
shape="circle"
77+
icon={<EditOutlined />}
78+
size="small"
79+
onClick={() => setChangeEmailVisible(!isChangeEmailVisible)}
80+
/>
81+
),
6782
},
6883
{
6984
title: "Organization",
@@ -95,7 +110,12 @@ function AccountProfileView() {
95110
<Row gutter={[24, 24]} style={{ marginBottom: 24 }}>
96111
{profileItems.map((item) => (
97112
<Col span={12} key={item.title}>
98-
<SettingsCard title={item.title} content={item.content} tooltip={item.tooltip} />
113+
<SettingsCard
114+
title={item.title}
115+
content={item.content}
116+
tooltip={item.tooltip}
117+
action={item.action}
118+
/>
99119
</Col>
100120
))}
101121
</Row>

frontend/javascripts/admin/auth/change_email_view.tsx

Lines changed: 97 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { LockOutlined, MailOutlined } from "@ant-design/icons";
22
import { logoutUser, updateUser } from "admin/rest_api";
3-
import { Alert, Button, Col, Form, Input, Row } from "antd";
3+
import { Alert, Button, Form, Input, Space } from "antd";
44
import { useWkSelector } from "libs/react_hooks";
55
import Toast from "libs/toast";
66
import { logoutUserAction } from "viewer/model/actions/user_actions";
@@ -15,7 +15,7 @@ const NEW_EMAIL_FIELD_KEY = "newEmail";
1515
const CONFIRM_NEW_EMAIL_FIELD_KEY = "confirmNewEmail";
1616
const PASSWORD_FIELD_KEY = "password";
1717

18-
function ChangeEmailView() {
18+
function ChangeEmailView({ onCancel }: { onCancel: () => void }) {
1919
const [form] = Form.useForm();
2020
const activeUser = useWkSelector((state) => state.activeUser);
2121
useNavigate();
@@ -31,6 +31,7 @@ function ChangeEmailView() {
3131
function onFinish() {
3232
const newEmail = form.getFieldValue(NEW_EMAIL_FIELD_KEY);
3333
const password = form.getFieldValue(PASSWORD_FIELD_KEY);
34+
3435
changeEmail(newEmail, password)
3536
.then(async () => {
3637
handleResendVerificationEmail();
@@ -69,119 +70,104 @@ function ChangeEmailView() {
6970
}
7071

7172
return (
72-
<Row
73-
justify="center"
74-
align="middle"
75-
style={{
76-
padding: 50,
77-
}}
78-
>
79-
<Col span={8}>
80-
<h3>Change Email</h3>
81-
<Alert
82-
type="info"
83-
message="You will be logged out after changing your email address."
84-
showIcon
85-
style={{
86-
marginBottom: 24,
87-
}}
88-
/>
89-
<Form onFinish={onFinish} form={form}>
90-
<FormItem
91-
name={PASSWORD_FIELD_KEY}
92-
rules={[
93-
{
94-
required: true,
95-
message: "Please enter your password for verification",
96-
},
97-
]}
98-
>
99-
<Input.Password
100-
prefix={
101-
<LockOutlined
102-
style={{
103-
fontSize: 13,
104-
}}
105-
/>
106-
}
107-
placeholder="Your Password"
108-
/>
109-
</FormItem>
110-
<FormItem
111-
hasFeedback
112-
name={NEW_EMAIL_FIELD_KEY}
113-
rules={[
114-
{
115-
required: true,
116-
message: "Please enter your new email address",
117-
},
118-
{
119-
type: "email",
120-
message: "Please enter a valid email address",
121-
},
122-
{
123-
validator: (_, value: string) =>
124-
checkEmailsAreMatching(value, [CONFIRM_NEW_EMAIL_FIELD_KEY]),
125-
},
126-
{
127-
validator: (_, value: string) => checkEmailIsDifferent(value),
128-
},
129-
]}
130-
>
131-
<Input
132-
prefix={
133-
<MailOutlined
134-
style={{
135-
fontSize: 13,
136-
}}
137-
/>
138-
}
139-
placeholder="New Email Address"
73+
<Form onFinish={onFinish} form={form}>
74+
<FormItem
75+
hasFeedback
76+
name={NEW_EMAIL_FIELD_KEY}
77+
rules={[
78+
{
79+
required: true,
80+
message: "Please enter your new email address",
81+
},
82+
{
83+
type: "email",
84+
message: "Please enter a valid email address",
85+
},
86+
{
87+
validator: (_, value: string) =>
88+
checkEmailsAreMatching(value, [CONFIRM_NEW_EMAIL_FIELD_KEY]),
89+
},
90+
{
91+
validator: (_, value: string) => checkEmailIsDifferent(value),
92+
},
93+
]}
94+
>
95+
<Input
96+
prefix={
97+
<MailOutlined
98+
style={{
99+
fontSize: 13,
100+
}}
140101
/>
141-
</FormItem>
142-
<FormItem
143-
hasFeedback
144-
name={CONFIRM_NEW_EMAIL_FIELD_KEY}
145-
rules={[
146-
{
147-
required: true,
148-
message: "Please confirm your new email address",
149-
},
150-
{
151-
type: "email",
152-
message: "Please enter a valid email address",
153-
},
154-
{
155-
validator: (_, value: string) =>
156-
checkEmailsAreMatching(value, [NEW_EMAIL_FIELD_KEY]),
157-
},
158-
]}
159-
>
160-
<Input
161-
prefix={
162-
<MailOutlined
163-
style={{
164-
fontSize: 13,
165-
}}
166-
/>
167-
}
168-
placeholder="Confirm New Email Address"
102+
}
103+
placeholder="New Email Address"
104+
/>
105+
</FormItem>
106+
<FormItem
107+
hasFeedback
108+
name={CONFIRM_NEW_EMAIL_FIELD_KEY}
109+
rules={[
110+
{
111+
required: true,
112+
message: "Please confirm your new email address",
113+
},
114+
{
115+
type: "email",
116+
message: "Please enter a valid email address",
117+
},
118+
{
119+
validator: (_, value: string) => checkEmailsAreMatching(value, [NEW_EMAIL_FIELD_KEY]),
120+
},
121+
]}
122+
>
123+
<Input
124+
prefix={
125+
<MailOutlined
126+
style={{
127+
fontSize: 13,
128+
}}
169129
/>
170-
</FormItem>
171-
<FormItem>
172-
<Button
173-
type="primary"
174-
htmlType="submit"
130+
}
131+
placeholder="Confirm New Email Address"
132+
/>
133+
</FormItem>
134+
<FormItem
135+
name={PASSWORD_FIELD_KEY}
136+
rules={[
137+
{
138+
required: true,
139+
message: "Please enter your password for verification",
140+
},
141+
]}
142+
>
143+
<Input.Password
144+
prefix={
145+
<LockOutlined
175146
style={{
176-
width: "100%",
147+
fontSize: 13,
177148
}}
178-
>
179-
Change Email
180-
</Button>
181-
</FormItem>
182-
</Form>
183-
</Col>
184-
</Row>
149+
/>
150+
}
151+
placeholder="Your Password"
152+
/>
153+
</FormItem>
154+
<Alert
155+
type="info"
156+
message="You will be logged out after successfully changing your email address."
157+
showIcon
158+
style={{
159+
marginBottom: 24,
160+
}}
161+
/>
162+
<FormItem>
163+
<Space>
164+
<Button onClick={onCancel}>Cancel</Button>
165+
<Button type="primary" htmlType="submit">
166+
Change Email
167+
</Button>
168+
</Space>
169+
</FormItem>
170+
</Form>
185171
);
186172
}
187173

frontend/javascripts/navbar.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -616,10 +616,6 @@ function LoggedInAvatar({
616616
{
617617
type: "divider",
618618
},
619-
{
620-
key: "changeEmail",
621-
label: <Link to="/auth/changeEmail">Change Email</Link>,
622-
},
623619
{
624620
key: "account",
625621
label: <Link to="/account">Account Settings</Link>,

frontend/javascripts/router/router.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ const { Content } = Layout;
4848
import AccountAuthTokenView from "admin/account/account_auth_token_view";
4949
import AccountPasswordView from "admin/account/account_password_view";
5050
import AccountProfileView from "admin/account/account_profile_view";
51-
import ChangeEmailView from "admin/auth/change_email_view";
5251
import { OrganizationDangerZoneView } from "admin/organization/organization_danger_zone_view";
5352
import { OrganizationNotificationsView } from "admin/organization/organization_notifications_view";
5453
import { OrganizationOverviewView } from "admin/organization/organization_overview_view";
@@ -393,14 +392,6 @@ const routes = createRoutesFromElements(
393392

394393
<Route path="/auth/resetPassword" element={<StartResetPasswordView />} />
395394
<Route path="/auth/finishResetPassword" element={<FinishResetPasswordView />} />
396-
<Route
397-
path="/auth/changeEmail"
398-
element={
399-
<SecuredRoute>
400-
<ChangeEmailView />
401-
</SecuredRoute>
402-
}
403-
/>
404395
{/* legacy view mode route */}
405396
<Route
406397
path="/datasets/:organizationId/:datasetName/view"

unreleased_changes/8840.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
### Changed
2+
- Integrated "Change Email" functionality into Account Settings

0 commit comments

Comments
 (0)