Skip to content

Add Account Settings page #8672

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 60 commits into from
Jun 27, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
8f2428c
Update navbar links and add account settings route
hotzenklotz Jun 4, 2025
58c2d49
Refactor account settings view and add profile management
hotzenklotz Jun 4, 2025
9367076
WIP account setting sub page styling
hotzenklotz Jun 4, 2025
33b0b1a
formatting
hotzenklotz Jun 5, 2025
4a4c460
fix typeing
hotzenklotz Jun 5, 2025
8e18e2a
added breadcrumbs
hotzenklotz Jun 5, 2025
77e602a
added redirects to old routes
hotzenklotz Jun 5, 2025
09d50ea
apply feedback
hotzenklotz Jun 5, 2025
14e624b
fix typo
hotzenklotz Jun 5, 2025
cb70499
move account related views into separate directory
hotzenklotz Jun 6, 2025
dd6c8e6
style background
hotzenklotz Jun 6, 2025
4cd2ecd
refactor orga settings in new style
hotzenklotz Jun 6, 2025
6a4b559
Merge branch 'master' of github.com:scalableminds/webknossos into acc…
hotzenklotz Jun 16, 2025
d022413
Merge branch 'account-settings-page' into orga-settings-page
hotzenklotz Jun 16, 2025
82fd616
Refactor account settings components and update organization navigation
hotzenklotz Jun 16, 2025
aa95b90
Enhance organization management UI and functionality
hotzenklotz Jun 16, 2025
a2002cb
formatting
hotzenklotz Jun 16, 2025
b7543ea
Enhance organization-related components and UI
hotzenklotz Jun 16, 2025
749861a
fix setting cards buttons
hotzenklotz Jun 16, 2025
44e0966
changelog
hotzenklotz Jun 16, 2025
76616d0
Merge branch 'master' of github.com:scalableminds/webknossos into acc…
hotzenklotz Jun 17, 2025
8716091
Merge branch 'account-settings-page' into orga-settings-page
hotzenklotz Jun 17, 2025
55bf833
Update frontend/javascripts/admin/organization/pricing_plan_utils.ts
hotzenklotz Jun 18, 2025
513011b
applied PR feedback
hotzenklotz Jun 18, 2025
01ae877
Merge branch 'orga-settings-page' of github.com:scalableminds/webknos…
hotzenklotz Jun 18, 2025
67e8819
Show owner email in notifications
hotzenklotz Jun 18, 2025
121c91a
fix pricing plan label
hotzenklotz Jun 18, 2025
9ee0b24
Merge branch 'master' of github.com:scalableminds/webknossos into acc…
hotzenklotz Jun 23, 2025
203ae75
Merge branch 'account-settings-page' into orga-settings-page
hotzenklotz Jun 23, 2025
df8e81d
do not show upgrade options for custom plans yet
hotzenklotz Jun 23, 2025
b094806
update the account settings page with card style
hotzenklotz Jun 23, 2025
a91f88b
Update frontend/javascripts/router.tsx
hotzenklotz Jun 24, 2025
38c426e
Update frontend/javascripts/admin/account/account_password_view.tsx
hotzenklotz Jun 24, 2025
511c4fb
Merge branch 'master' into account-settings-page
hotzenklotz Jun 24, 2025
d0cd5b3
more stuff
hotzenklotz Jun 24, 2025
66086aa
fix null / infinity on update
hotzenklotz Jun 24, 2025
1903218
appled PR feedback
hotzenklotz Jun 24, 2025
d61a522
apply PR feedback
hotzenklotz Jun 24, 2025
f2ee79d
fix linitng
hotzenklotz Jun 24, 2025
2ed707a
Merge branch 'account-settings-page' into orga-settings-page
hotzenklotz Jun 24, 2025
1f74811
Merge branch 'orga-settings-page' into account-settings-page-cards
hotzenklotz Jun 24, 2025
125ed0f
formatting
hotzenklotz Jun 24, 2025
e489f9d
Rework organization page (#8679)
hotzenklotz Jun 25, 2025
c8e6a4e
Merge branch 'account-settings-page-cards' into account-settings-page
hotzenklotz Jun 25, 2025
a84f9fc
Merge branch 'master' of github.com:scalableminds/webknossos into acc…
hotzenklotz Jun 25, 2025
0418bbf
refinement
hotzenklotz Jun 25, 2025
c12b0a3
changelog
hotzenklotz Jun 25, 2025
b3ef603
Merge branch 'master' of github.com:scalableminds/webknossos into acc…
hotzenklotz Jun 25, 2025
58af2a4
apply coderabbit feedback
hotzenklotz Jun 25, 2025
52728d5
redirect after orga removal
hotzenklotz Jun 25, 2025
235be66
apply feedback
hotzenklotz Jun 25, 2025
f734e92
Update unreleased_changes/8672.md
hotzenklotz Jun 25, 2025
0260a0e
Merge branch 'master' of github.com:scalableminds/webknossos into acc…
hotzenklotz Jun 25, 2025
8b8fb57
apply PR feedback
hotzenklotz Jun 25, 2025
0bb572d
Update unreleased_changes/8679.md
hotzenklotz Jun 25, 2025
6762eee
Merge branch 'master' of github.com:scalableminds/webknossos into acc…
hotzenklotz Jun 26, 2025
e6a69d5
Merge branch 'account-settings-page' of github.com:scalableminds/webk…
hotzenklotz Jun 26, 2025
6e8e483
add theme switching to command palette
philippotto Jun 27, 2025
c3be491
add toast.error
philippotto Jun 27, 2025
7d98ca1
sort imports
philippotto Jun 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:

- name: Refresh datasets
run: |
curl -X POST --fail https://${{ env.SUBDOMAIN }}.webknossos.xyz/data/triggers/checkInboxBlocking?token=${{ secrets.WK_AUTH_TOKEN }}
curl -X POST --fail https://${{ env.SUBDOMAIN }}.webknossos.xyz/data/triggers/checkInboxBlocking?token=${{ secrets.WK_DEV_AUTH_TOKEN }}

- name: Run screenshot tests
run: |
Expand All @@ -62,7 +62,7 @@ jobs:
yarn test-screenshot
env:
URL: https://${{ env.SUBDOMAIN }}.webknossos.xyz/
WK_AUTH_TOKEN: ${{ secrets.WK_AUTH_TOKEN }}
WK_AUTH_TOKEN: ${{ secrets.WK_DEV_AUTH_TOKEN }}
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/wkorg-nightly.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
yarn test-wkorg-screenshot
env:
URL: https://webknossos.org/
WK_AUTH_TOKEN: ${{ secrets.WK_AUTH_TOKEN }}
WK_AUTH_TOKEN: ${{ secrets.WK_ORG_AUTH_TOKEN }}
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY : ${{ secrets.BROWSERSTACK_ACCESS_KEY }}

Expand Down Expand Up @@ -57,4 +57,4 @@ jobs:
--data-raw '[{"position":[2752,4320,1728],"additionalCoordinates":[],"mag":[1,1,1],"cubeSize":32,"fourBit":false}]' \
'https://data-humerus.webknossos.org/data/datasets/scalable_minds/l4dense_motta_et_al_demo/layers/segmentation/data?token=' \
| grep -q "content-encoding: gzip"
echo Success.
echo Success.
11 changes: 8 additions & 3 deletions frontend/javascripts/admin/account/account_auth_token_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ function AccountAuthTokenView() {
}, []);

async function fetchData(): Promise<void> {
const token = await getAuthToken();
setCurrentToken(token);
setIsLoading(false);
try {
const token = await getAuthToken();
setCurrentToken(token);
} catch (error) {
console.error("Failed to fetch auth token:", error);
} finally {
setIsLoading(false);
}
}

const handleRevokeToken = async (): Promise<void> => {
Expand Down
26 changes: 11 additions & 15 deletions frontend/javascripts/admin/account/account_password_view.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,29 @@
import { DeleteOutlined, LockOutlined, ReloadOutlined } from "@ant-design/icons";
import { EditOutlined, LockOutlined } from "@ant-design/icons";
import { changePassword, logoutUser } from "admin/rest_api";
import { Alert, Button, Col, Form, Input, Row, Space } from "antd";
import Request from "libs/request";
import Toast from "libs/toast";
import messages from "messages";
import { useState } from "react";
import { type RouteComponentProps, withRouter } from "react-router-dom";
import { useHistory } from "react-router-dom";
import { logoutUserAction } from "viewer/model/actions/user_actions";
import Store from "viewer/store";
import { SettingsCard } from "./helpers/settings_card";
import { SettingsTitle } from "./helpers/settings_title";
const FormItem = Form.Item;
const { Password } = Input;

type Props = {
history: RouteComponentProps["history"];
};

const MIN_PASSWORD_LENGTH = 8;

function AccountPasswordView({ history }: Props) {
function AccountPasswordView() {
const history = useHistory();
const [form] = Form.useForm();
const [isResetPasswordVisible, setResetPasswordVisible] = useState(false);

function onFinish(formValues: Record<string, any>) {
Request.sendJSONReceiveJSON("/api/auth/changePassword", {
data: formValues,
})
changePassword(formValues)
.then(async () => {
Toast.success(messages["auth.reset_pw_confirmation"]);
await Request.receiveJSON("/api/auth/logout");
await logoutUser();
Store.dispatch(logoutUserAction());
history.push("/auth/login");
})
Expand Down Expand Up @@ -164,7 +159,8 @@ function AccountPasswordView({ history }: Props) {
{
title: "Coming soon",
value: "Passwordless login with passkeys is coming soon",
action: <Button type="default" shape="circle" icon={<DeleteOutlined />} size="small" />,
// action: <Button type="default" shape="circle" icon={<DeleteOutlined />} size="small" />,
action: undefined,
},
];

Expand All @@ -180,7 +176,7 @@ function AccountPasswordView({ history }: Props) {
<Button
type="default"
shape="circle"
icon={<ReloadOutlined />}
icon={<EditOutlined />}
size="small"
onClick={handleResetPassword}
/>
Expand All @@ -201,4 +197,4 @@ function AccountPasswordView({ history }: Props) {
);
}

export default withRouter<RouteComponentProps, any>(AccountPasswordView);
export default AccountPasswordView;
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const MENU_ITEMS: MenuItemGroupType[] = [
function AccountSettingsView() {
const location = useLocation();
const history = useHistory();
const selectedKey = location.pathname.split("/").pop() || "profile";
const selectedKey = location.pathname.split("/").filter(Boolean).pop() || "profile";

const breadcrumbItems = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import { Button, Card, Col, Form, Input, Row } from "antd";
import Request from "libs/request";
import Toast from "libs/toast";
import messages from "messages";
import { type RouteComponentProps, withRouter } from "react-router-dom";
import { useHistory } from "react-router-dom";
const FormItem = Form.Item;
const { Password } = Input;
type Props = {
history: RouteComponentProps["history"];
resetToken: string;
};

function FinishResetPasswordView(props: Props) {
const [form] = Form.useForm();
const history = useHistory();

function onFinish(formValues: Record<string, any>) {
const data = formValues;
Expand All @@ -27,7 +27,7 @@ function FinishResetPasswordView(props: Props) {
data,
}).then(() => {
Toast.success(messages["auth.reset_pw_confirmation"]);
props.history.push("/auth/login");
history.push("/auth/login");
});
}

Expand Down Expand Up @@ -128,4 +128,4 @@ function FinishResetPasswordView(props: Props) {
);
}

export default withRouter<RouteComponentProps & Props, any>(FinishResetPasswordView);
export default FinishResetPasswordView;
9 changes: 4 additions & 5 deletions frontend/javascripts/admin/auth/login_view.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { Card, Col, Row } from "antd";
import * as Utils from "libs/utils";
import window from "libs/window";
import type { RouteComponentProps } from "react-router-dom";
import { withRouter } from "react-router-dom";
import { useHistory } from "react-router-dom";
import LoginForm from "./login_form";

type Props = {
history: RouteComponentProps["history"];
redirect?: string;
};

function LoginView({ history, redirect }: Props) {
function LoginView({ redirect }: Props) {
const history = useHistory();
const onLoggedIn = () => {
if (!Utils.hasUrlParam("redirectPage")) {
if (redirect) {
Expand Down Expand Up @@ -38,4 +37,4 @@ function LoginView({ history, redirect }: Props) {
);
}

export default withRouter<RouteComponentProps & Props, any>(LoginView);
export default LoginView;
10 changes: 4 additions & 6 deletions frontend/javascripts/admin/auth/start_reset_password_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ import { Button, Card, Col, Form, Input, Row } from "antd";
import Request from "libs/request";
import Toast from "libs/toast";
import messages from "messages";
import { Link, type RouteComponentProps, withRouter } from "react-router-dom";
import { Link, useHistory } from "react-router-dom";
const FormItem = Form.Item;
type Props = {
history: RouteComponentProps["history"];
};

function StartResetPasswordView({ history }: Props) {
function StartResetPasswordView() {
const [form] = Form.useForm();
const history = useHistory();

const onFinish = (formValues: Record<string, any>) => {
Request.sendJSONReceiveJSON("/api/auth/startResetPassword", {
Expand Down Expand Up @@ -67,4 +65,4 @@ function StartResetPasswordView({ history }: Props) {
);
}

export default withRouter<RouteComponentProps & Props, any>(StartResetPasswordView);
export default StartResetPasswordView;
11 changes: 5 additions & 6 deletions frontend/javascripts/admin/dataset/dataset_add_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import DatasetUploadView from "admin/dataset/dataset_upload_view";
import { getDatastores } from "admin/rest_api";
import { Button, Layout, Modal, Tabs, type TabsProps } from "antd";
import features from "features";
import type { History } from "history";
import { useFetch } from "libs/react_helpers";
import { useWkSelector } from "libs/react_hooks";
import React, { useState } from "react";
import { connect } from "react-redux";
import type { RouteComponentProps } from "react-router-dom";
import { withRouter } from "react-router-dom";
import { useHistory } from "react-router-dom";
import type { APIDataStore } from "types/api_types";
import { getReadableURLPart } from "viewer/model/accessors/dataset_accessor";
import { enforceActiveUser } from "viewer/model/accessors/user_accessor";
Expand All @@ -33,7 +31,8 @@ const addTypeToVerb: Record<DatasetAddType, string> = {
compose: "created",
};

function DatasetAddView({ history }: RouteComponentProps) {
function DatasetAddView() {
const history = useHistory();
const datastores = useFetch<APIDataStore[]>(getDatastores, [], []);
const [datasetId, setDatasetId] = useState("");
const [uploadedDatasetName, setUploadedDatasetName] = useState("");
Expand Down Expand Up @@ -267,15 +266,15 @@ const mapStateToProps = (state: WebknossosState) => ({
});

const connector = connect(mapStateToProps);
export default connector(withRouter(DatasetAddView));
export default connector(DatasetAddView);

const getPostUploadModal = (
datasetNeedsConversion: boolean,
datasetAddType: DatasetAddType,
datasetId: string,
uploadedDatasetName: string,
setDatasetId: (arg0: string) => void,
history: History<unknown>,
history: ReturnType<typeof useHistory>,
) => {
return (
<Modal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const OrganizationView = () => {
);
const location = useLocation();
const history = useHistory();
const selectedKey = location.pathname.split("/").pop() || "overview";
const selectedKey = location.pathname.split("/").filter(Boolean).pop() || "overview";

const breadcrumbItems = [
{
Expand Down
6 changes: 2 additions & 4 deletions frontend/javascripts/admin/project/project_create_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ import { enforceActiveUser } from "viewer/model/accessors/user_accessor";
import { FormItemWithInfo } from "../../dashboard/dataset/helper_components";

const FormItem = Form.Item;
type OwnProps = {
type Props = {
projectId?: string | null | undefined;
};
type Props = OwnProps;
type PropsWithRouter = Props;

function ProjectCreateView({ projectId }: PropsWithRouter) {
function ProjectCreateView({ projectId }: Props) {
const [teams, setTeams] = useState<APITeam[]>([]);
const [users, setUsers] = useState<APIUser[]>([]);
const [isFetchingData, setIsFetchingData] = useState<boolean>(false);
Expand Down
10 changes: 10 additions & 0 deletions frontend/javascripts/admin/rest_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ export async function loginUser(formValues: {
return [activeUser, organization];
}

export async function logoutUser(): Promise<void> {
await Request.receiveJSON("/api/auth/logout");
}

export async function getUsers(): Promise<Array<APIUser>> {
const users = await Request.receiveJSON("/api/users");
assertResponseLimit(users);
Expand Down Expand Up @@ -237,6 +241,12 @@ export async function revokeAuthToken(): Promise<void> {
});
}

export async function changePassword(data: Record<string, string>): Promise<void> {
await Request.sendJSONReceiveJSON("/api/auth/changePassword", {
data: data,
});
}

// Used only by the webknossos-libs python client, but tested here in the snapshot tests.
export async function getLoggedTimes(userID: string): Promise<Array<APITimeInterval>> {
const url = `/api/users/${userID}/loggedTime`;
Expand Down
11 changes: 4 additions & 7 deletions frontend/javascripts/admin/scripts/script_create_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { createScript, getScript, getTeamManagerOrAdminUsers, updateScript } fro
import { Button, Card, Form, Input, Select } from "antd";
import { useEffect, useState } from "react";
import { connect } from "react-redux";
import type { RouteComponentProps } from "react-router-dom";
import { withRouter } from "react-router-dom";
import { useHistory } from "react-router-dom";
import type { APIUser } from "types/api_types";
import { enforceActiveUser } from "viewer/model/accessors/user_accessor";
import type { WebknossosState } from "viewer/store";
Expand All @@ -16,11 +15,9 @@ type StateProps = {
activeUser: APIUser;
};
type Props = OwnProps & StateProps;
type PropsWithRouter = Props & {
history: RouteComponentProps["history"];
};

function ScriptCreateView({ scriptId, activeUser, history }: PropsWithRouter) {
function ScriptCreateView({ scriptId, activeUser }: Props) {
const history = useHistory();
const [users, setUsers] = useState<APIUser[]>([]);
const [isFetchingData, setIsFetchingData] = useState<boolean>(false);
const [form] = Form.useForm();
Expand Down Expand Up @@ -135,4 +132,4 @@ const mapStateToProps = (state: WebknossosState): StateProps => ({
});

const connector = connect(mapStateToProps);
export default connector(withRouter<RouteComponentProps & Props, any>(ScriptCreateView));
export default connector(ScriptCreateView);
9 changes: 4 additions & 5 deletions frontend/javascripts/admin/task/task_create_form_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ import { Vector3Input, Vector6Input } from "libs/vector_input";
import _ from "lodash";
import messages from "messages";
import React, { useEffect, useState } from "react";
import type { RouteComponentProps } from "react-router-dom";
import { withRouter } from "react-router-dom";
import { useHistory } from "react-router-dom";
import type { APIDataset, APIProject, APIScript, APITask, APITaskType } from "types/api_types";
import type { Vector3, Vector6 } from "viewer/constants";
import type { BoundingBoxObject } from "viewer/store";
Expand Down Expand Up @@ -291,7 +290,6 @@ export function ReloadResourceButton({

type Props = {
taskId: string | null | undefined;
history: RouteComponentProps["history"];
};

type FormValues = {
Expand All @@ -309,7 +307,8 @@ type FormValues = {
neededExperience: NewTask["neededExperience"];
};

function TaskCreateFormView({ taskId, history }: Props) {
function TaskCreateFormView({ taskId }: Props) {
const history = useHistory();
const { modal } = App.useApp();
const [form] = Form.useForm<FormValues>();

Expand Down Expand Up @@ -811,4 +810,4 @@ function TaskCreateFormView({ taskId, history }: Props) {
);
}

export default withRouter<RouteComponentProps & Props, any>(TaskCreateFormView);
export default TaskCreateFormView;
Loading