diff --git a/frontend/javascripts/admin/onboarding.tsx b/frontend/javascripts/admin/onboarding.tsx index 91786313fa..71d8d50c4f 100644 --- a/frontend/javascripts/admin/onboarding.tsx +++ b/frontend/javascripts/admin/onboarding.tsx @@ -21,12 +21,12 @@ import CreditsFooter from "components/credits_footer"; import LinkButton from "components/link_button"; import DatasetSettingsView from "dashboard/dataset/dataset_settings_view"; import features from "features"; +import { useWkSelector } from "libs/react_hooks"; import Toast from "libs/toast"; -import React, { useState } from "react"; -import { connect } from "react-redux"; +import type React from "react"; +import { Fragment, useEffect, useState } from "react"; import { Link, type RouteComponentProps, withRouter } from "react-router-dom"; import type { APIDataStore, APIUser } from "types/api_types"; -import type { WebknossosState } from "viewer/store"; import Store from "viewer/store"; const { Step } = Steps; @@ -35,14 +35,6 @@ type StateProps = { activeUser: APIUser | null | undefined; }; type Props = StateProps & RouteComponentProps; -type State = { - currentStep: number; - datastores: Array; - organizationId: string; - datasetIdToImport: string | null | undefined; - isDatasetUploadModalVisible: boolean; - isInviteModalVisible: boolean; -}; function StepHeader({ header, @@ -272,7 +264,7 @@ export function InviteUsersModal({ ) : null; return ( - +

Send an email to invite your colleagues and collaboration partners to your organization. Share datasets, collaboratively work on annotations, and organize complex analysis @@ -302,7 +294,7 @@ export function InviteUsersModal({ placeholder={"jane@example.com\njoe@example.com"} defaultValue={inviteesString} /> - + ); } @@ -396,142 +388,130 @@ const OrganizationForm = ({ onComplete }: { onComplete: (args: any) => void }) = ); }; -class OnboardingView extends React.PureComponent { - state: State = { - currentStep: 0, - datastores: [], - organizationId: "", - isDatasetUploadModalVisible: false, - isInviteModalVisible: false, - datasetIdToImport: null, - }; +function OnboardingView(props: Props) { + const activeUser = useWkSelector((state) => state.activeUser); + const [currentStep, setCurrentStep] = useState(0); + const [datastores, setDatastores] = useState([]); + const [organizationId, setOrganizationId] = useState(""); + const [isDatasetUploadModalVisible, setIsDatasetUploadModalVisible] = useState(false); + const [isInviteModalVisible, setIsInviteModalVisible] = useState(false); + const [datasetIdToImport, setDatasetIdToImport] = useState(null); - componentDidMount() { - if (this.props.activeUser != null) { - this.props.history.push("/dashboard"); + useEffect(() => { + if (activeUser != null) { + props.history.push("/dashboard"); } - } + }, [activeUser, props.history]); - async fetchDatastores() { - const datastores = await getDatastores(); - this.setState({ - datastores, - }); - } + const fetchDatastores = async () => { + const fetchedDatastores = await getDatastores(); + setDatastores(fetchedDatastores); + }; - advanceStep = () => { - this.setState((prevState) => ({ - currentStep: prevState.currentStep + 1, - isDatasetUploadModalVisible: false, - isInviteModalVisible: false, - datasetIdToImport: null, - })); + const advanceStep = () => { + setCurrentStep((prevStep) => prevStep + 1); + setIsDatasetUploadModalVisible(false); + setIsInviteModalVisible(false); + setDatasetIdToImport(null); }; - renderCreateOrganization = () => ( + + const renderCreateOrganization = () => ( + Welcome to WEBKNOSSOS! This guide will help you get started.
Setup your organization to manage users and datasets. Example names: “University of Springfield”, “Simpsons Lab”, “Neuroscience Department” - +
} icon={} > { - this.setState({ - organizationId, - }); - this.advanceStep(); + onComplete={(orgId) => { + setOrganizationId(orgId); + advanceStep(); }} />
); - renderCreateAccount = () => ( + + const renderCreateAccount = () => ( + This will be the first user account in your organization. It will be equipped with admin privileges in order to confirm user registrations, define teams, create tasks and much more. - + } icon={} > { // Update the entered organization to the normalized name of the organization received by the backend. // This is needed for further requests. const { activeUser } = Store.getState(); if (activeUser) { - this.setState({ - organizationId: activeUser.organization, - }); + setOrganizationId(activeUser.organization); // A user can only see the available datastores when he is logged in. // Thus we can fetch the datastores only after the registration. - this.fetchDatastores(); + fetchDatastores(); } - this.advanceStep(); + advanceStep(); }} confirmLabel="Create account" tryAutoLogin /> ); - renderUploadDatasets = () => ( + + const renderUploadDatasets = () => ( } > - {this.state.isDatasetUploadModalVisible && ( + {isDatasetUploadModalVisible && ( - this.setState({ - isDatasetUploadModalVisible: false, - }) - } + onCancel={() => setIsDatasetUploadModalVisible(false)} > { - this.setState({ - datasetIdToImport: uploadedDatasetId, - isDatasetUploadModalVisible: false, - }); + setDatasetIdToImport(uploadedDatasetId); + setIsDatasetUploadModalVisible(false); if (needsConversion) { // If the dataset needs a conversion, the settings cannot be shown. Thus we skip the settings step. - this.advanceStep(); + advanceStep(); } }} withoutCard /> )} - {this.state.datasetIdToImport != null && ( - + {datasetIdToImport != null && ( + )} @@ -540,13 +520,7 @@ class OnboardingView extends React.PureComponent { header="Upload Dataset" icon={} action={ - } @@ -560,7 +534,7 @@ class OnboardingView extends React.PureComponent { } - action={Skip this step} + action={Skip this step} height={250} > You can always do this later! @@ -568,16 +542,17 @@ class OnboardingView extends React.PureComponent { ); - renderWhatsNext = () => ( + + const renderWhatsNext = () => ( + You've completed the initial setup.
Start to explore and annotate your data now or learn more about the features and concepts of WEBKNOSSOS. - +
} icon={} > @@ -592,23 +567,13 @@ class OnboardingView extends React.PureComponent { formats and upload processes WEBKNOSSOS supports. }> - - this.setState({ - isInviteModalVisible: true, - }) - } - > + setIsInviteModalVisible(true)}> Invite users to work collaboratively {" "} - this.setState({ - isInviteModalVisible, - }) - } + organizationId={organizationId} + isOpen={isInviteModalVisible} + handleVisibleChange={setIsInviteModalVisible} /> and assign them to teams. Teams can be used to define dataset permissions and task assignments. @@ -636,101 +601,95 @@ class OnboardingView extends React.PureComponent {
); - getAvailableSteps() { + const getAvailableSteps = () => { if (features().isWkorgInstance) { return [ { title: "Create Organization", - component: this.renderCreateOrganization, + component: renderCreateOrganization, }, { title: "Create Account", - component: this.renderCreateAccount, + component: renderCreateAccount, }, { title: "What's Next?", - component: this.renderWhatsNext, + component: renderWhatsNext, }, ]; } else { return [ { title: "Create Organization", - component: this.renderCreateOrganization, + component: renderCreateOrganization, }, { title: "Create Account", - component: this.renderCreateAccount, + component: renderCreateAccount, }, { title: "Add Dataset", - component: this.renderUploadDatasets, + component: renderUploadDatasets, }, { title: "What's Next?", - component: this.renderWhatsNext, + component: renderWhatsNext, }, ]; } - } + }; - render() { - const availableSteps = this.getAvailableSteps(); - const currentStepContent = availableSteps[this.state.currentStep].component(); - return ( - <> -

+ const availableSteps = getAvailableSteps(); + const currentStepContent = availableSteps[currentStep].component(); + + return ( + <> +
+ + + + {availableSteps.map(({ title }) => ( + + ))} + + + +
- - {availableSteps.map(({ title }) => ( - - ))} - + + {currentStepContent} + -
- - - - {currentStepContent} - - - -
- - - ); - } +
+ + + ); } -const mapStateToProps = (state: WebknossosState): StateProps => ({ - activeUser: state.activeUser, -}); - -const connector = connect(mapStateToProps); -export default connector(withRouter(OnboardingView)); +export default withRouter(OnboardingView); diff --git a/frontend/javascripts/admin/welcome_ui.tsx b/frontend/javascripts/admin/welcome_ui.tsx index e215ad8157..8c0bfb513d 100644 --- a/frontend/javascripts/admin/welcome_ui.tsx +++ b/frontend/javascripts/admin/welcome_ui.tsx @@ -7,6 +7,7 @@ import { isUserAdminOrDatasetManager, isUserAdminOrTeamManager } from "libs/util import * as React from "react"; import { Link } from "react-router-dom"; import type { APIUser } from "types/api_types"; + type WhatsNextActionProps = { title: string; description: string; @@ -119,4 +120,3 @@ export const WhatsNextHeader = ({ activeUser, onDismiss }: WhatsNextHeaderProps)
); -export default {};