diff --git a/src/features/login/EnforcePolicies.tsx b/src/features/login/EnforcePolicies.tsx
index caa35720..de196255 100644
--- a/src/features/login/EnforcePolicies.tsx
+++ b/src/features/login/EnforcePolicies.tsx
@@ -4,7 +4,7 @@ import { Alert, Button, Container, Paper } from '@mui/material';
import { Stack } from '@mui/system';
import { useState } from 'react';
import classes from '../signup/SignUp.module.scss';
-import { kbasePolicies, PolicyViewer } from '../auth/Policies';
+import { kbasePolicies, PolicyViewer } from './Policies';
export const EnforcePolicies = ({
policyIds,
@@ -13,16 +13,16 @@ export const EnforcePolicies = ({
policyIds: string[];
onAccept: (versionedPolicyIds: string[]) => void;
}) => {
+ // Get policy information
const targetPolicies = policyIds.map((id) => kbasePolicies[id]);
-
const [accepted, setAccepted] = useState<{
[k in typeof targetPolicies[number]['id']]?: boolean;
}>({});
-
const allAccepted = targetPolicies.every(
(policy) => accepted[policy.id] === true
);
+ // Message to user, uses a special message when agreeing to kbase-user.2
let message =
'To continue to your account, you must agree to the following KBase use policies.'; // Default message
if (
diff --git a/src/features/login/LogIn.tsx b/src/features/login/LogIn.tsx
index c551850c..a01eba07 100644
--- a/src/features/login/LogIn.tsx
+++ b/src/features/login/LogIn.tsx
@@ -75,7 +75,7 @@ export const LogIn: FC = () => {
const nextRequest = useAppParam('nextRequest');
useCheckLoggedIn(nextRequest);
const { loginActionUrl, loginRedirectUrl, loginOrigin } =
- makelLoginURLs(nextRequest);
+ makeLoginURLs(nextRequest);
return (
@@ -146,7 +146,7 @@ export const LogIn: FC = () => {
);
};
-export const makelLoginURLs = (nextRequest?: string) => {
+export const makeLoginURLs = (nextRequest?: string) => {
// OAuth Login wont work in dev mode, but send dev users to CI so they can grab their token
const loginOrigin =
process.env.NODE_ENV === 'development'
diff --git a/src/features/login/LogInContinue.test.tsx b/src/features/login/LogInContinue.test.tsx
index 2b5279fe..e0a8b2a6 100644
--- a/src/features/login/LogInContinue.test.tsx
+++ b/src/features/login/LogInContinue.test.tsx
@@ -8,7 +8,7 @@ import { LogInContinue } from './LogInContinue';
import fetchMock from 'jest-fetch-mock';
import { toast } from 'react-hot-toast';
import { noOp } from '../common';
-import { kbasePolicies } from '../auth/Policies';
+import { kbasePolicies } from './Policies';
jest.mock('react-hot-toast', () => ({
toast: jest.fn(),
diff --git a/src/features/login/LogInContinue.tsx b/src/features/login/LogInContinue.tsx
index a69e0219..2b58d3e6 100644
--- a/src/features/login/LogInContinue.tsx
+++ b/src/features/login/LogInContinue.tsx
@@ -11,7 +11,7 @@ import { useNavigate } from 'react-router-dom';
import { LOGIN_ROUTE } from '../../app/Routes';
import { useAppDispatch } from '../../common/hooks';
import { setLoginData } from '../signup/SignupSlice';
-import { kbasePolicies } from '../auth/Policies';
+import { kbasePolicies } from './Policies';
import { EnforcePolicies } from './EnforcePolicies';
export const LogInContinue: FC = () => {
diff --git a/src/features/auth/Policies.tsx b/src/features/login/Policies.tsx
similarity index 100%
rename from src/features/auth/Policies.tsx
rename to src/features/login/Policies.tsx
diff --git a/src/features/auth/PolicyViewer.module.scss b/src/features/login/PolicyViewer.module.scss
similarity index 100%
rename from src/features/auth/PolicyViewer.module.scss
rename to src/features/login/PolicyViewer.module.scss
diff --git a/src/features/signup/AccountInformation.tsx b/src/features/signup/AccountInformation.tsx
index 50b766a3..28895be7 100644
--- a/src/features/signup/AccountInformation.tsx
+++ b/src/features/signup/AccountInformation.tsx
@@ -17,7 +17,7 @@ import {
TextField,
Typography,
} from '@mui/material';
-import { FC, useState } from 'react';
+import { FC, useEffect, useState } from 'react';
import { toast } from 'react-hot-toast';
import { useNavigate } from 'react-router-dom';
import { useAppDispatch, useAppSelector } from '../../common/hooks';
@@ -27,40 +27,32 @@ import { loginUsernameSuggest } from '../../common/api/authService';
import { useForm } from 'react-hook-form';
import { setAccount, setProfile } from './SignupSlice';
+export const useCheckLoginDataOk = () => {
+ const navigate = useNavigate();
+ const loginData = useAppSelector((state) => state.signup.loginData);
+ useEffect(() => {
+ if (!loginData) {
+ toast('You must login using a provider first to sign up!');
+ navigate('/signup/1');
+ }
+ }, [loginData, navigate]);
+};
+
/**
* Account information form for sign up flow
*/
-export const AccountInformation: FC<{
- setActiveStep: (step: number) => void;
-}> = ({ setActiveStep }) => {
+export const AccountInformation: FC<{}> = () => {
const navigate = useNavigate();
const dispatch = useAppDispatch();
+ useCheckLoginDataOk();
+
// Login Data
- const loginData = useAppSelector(
- (state) =>
- state.signup.loginData || {
- provider: 'ORCiD',
- create: [
- {
- availablename: 'dlyon',
- id: 'someuserid',
- provemail: 'dlyon@lbl.gov',
- provfullname: 'David Lyon',
- provusername: 'dlyon',
- },
- ],
- }
- );
+ const loginData = useAppSelector((state) => state.signup.loginData);
// Account data
const account = useAppSelector((state) => state.signup.account);
- if (!loginData) {
- toast('You must login using a provider first to sign up!');
- navigate('/signup/1');
- }
-
//username availibility
const [username, setUsername] = useState(account.username ?? '');
const userAvail = loginUsernameSuggest.useQuery(username);
@@ -71,7 +63,7 @@ export const AccountInformation: FC<{
const surveyQuestion = 'How did you hear about us? (select all that apply)';
const [optionalText, setOptionalText] = useState>({});
- // Form
+ // Form state
const { register, handleSubmit } = useForm({
defaultValues: {
account: account,
@@ -90,9 +82,10 @@ export const AccountInformation: FC<{
},
});
+ // Form submission
const onSubmit = handleSubmit(async (fieldValues, event) => {
event?.preventDefault();
- // Add in the optional survey text content
+ // Add in survey text content from form
ReferalSources.forEach((src) => {
if (
src.customText &&
@@ -102,7 +95,7 @@ export const AccountInformation: FC<{
fieldValues.profile.surveydata.referralSources.response[src.value] =
optionalText[src.value];
});
- // dispatch form data to state
+ // dispatch form data to signup state
dispatch(setAccount(fieldValues.account));
dispatch(
setProfile({
@@ -123,8 +116,8 @@ export const AccountInformation: FC<{
- You have signed in with your {loginData.provider}{' '}
- account {loginData.create[0].provemail}. This will
+ You have signed in with your {loginData?.provider}{' '}
+ account {loginData?.create[0].provemail}. This will
be the account linked to your KBase account.
@@ -139,22 +132,22 @@ export const AccountInformation: FC<{
If the account you see above is not the one you want, use the
- link below to log out of {loginData.provider}, and then try
+ link below to log out of {loginData?.provider}, and then try
again.
- If you are trying to sign up with a {loginData.provider}{' '}
+ If you are trying to sign up with a {loginData?.provider}{' '}
account that is already linked to a KBase account, you will be
unable to create a new KBase account using that{' '}
- {loginData.provider} account.
+ {loginData?.provider} account.
- After signing out from {loginData.provider} you will need to
+ After signing out from {loginData?.provider} you will need to
restart the sign up process.
@@ -171,7 +164,7 @@ export const AccountInformation: FC<{
Create a new KBase Account
Some field values have been pre-populated from your{' '}
- {loginData.provider} account.
+ {loginData?.provider} account.
All fields are required.
diff --git a/src/features/signup/ProviderSelect.tsx b/src/features/signup/ProviderSelect.tsx
index f6a02b09..f6c30c8e 100644
--- a/src/features/signup/ProviderSelect.tsx
+++ b/src/features/signup/ProviderSelect.tsx
@@ -8,14 +8,14 @@ import {
Typography,
} from '@mui/material';
import { FC } from 'react';
-import { LoginButtons, makelLoginURLs } from '../login/LogIn';
+import { LoginButtons, makeLoginURLs } from '../login/LogIn';
import classes from './SignUp.module.scss';
/**
* Provider selection screen for sign up flow
*/
export const ProviderSelect: FC = () => {
- const { loginActionUrl, loginRedirectUrl, loginOrigin } = makelLoginURLs();
+ const { loginActionUrl, loginRedirectUrl, loginOrigin } = makeLoginURLs();
return (
@@ -25,7 +25,7 @@ export const ProviderSelect: FC = () => {
Choose a provider
{process.env.NODE_ENV === 'development' ? (
- DEV MODE: Login will occur on {loginOrigin}
+ DEV MODE: Signup will occur on {loginOrigin}
) : (
<>>
diff --git a/src/features/signup/SignUp.tsx b/src/features/signup/SignUp.tsx
index c48dff2a..fe62cf90 100644
--- a/src/features/signup/SignUp.tsx
+++ b/src/features/signup/SignUp.tsx
@@ -16,6 +16,7 @@ import { AccountInformation } from './AccountInformation';
import { ProviderSelect } from './ProviderSelect';
import { KBasePolicies } from './SignupPolicies';
import { md5 } from 'js-md5';
+import { ROOT_REDIRECT_ROUTE } from '../../app/Routes';
const signUpSteps = [
'Sign up with a supported provider',
@@ -47,61 +48,54 @@ export const SignUp: FC = () => {
Sign up for KBase
{signUpSteps.map((step, i) => (
- setActiveStep(i)}>
+ {
+ if (i < activeStep) setActiveStep(i);
+ }}
+ >
{step}
))}
{activeStep === 0 && }
- {activeStep === 1 && (
-
- )}
- {activeStep === 2 && }
+ {activeStep === 1 && }
+ {activeStep === 2 && }
);
};
export const useDoSignup = () => {
- const data = useAppSelector((state) => state.signup);
-
- const signupOk = !!data.loginData;
- const [triggerAccount, accountResult] = loginCreate.useMutation();
- const [triggerProfile, profileResult] = setUserProfile.useMutation();
-
- const loading =
- !accountResult.isUninitialized &&
- (accountResult.isLoading || profileResult.isLoading);
-
- const complete =
- !loading &&
- !accountResult.isUninitialized &&
- !profileResult.isUninitialized &&
- accountResult.isSuccess &&
- profileResult.isSuccess;
+ const signupData = useAppSelector((state) => state.signup);
+ const navigate = useNavigate();
+ // Queries for creating an account and a profile for the user.
+ const [triggerCreateAccount, accountResult] = loginCreate.useMutation();
+ const [triggerCreateProfile, profileResult] = setUserProfile.useMutation();
const error = accountResult.error || profileResult.error;
+ // Callback to trigger the first call. Consumer should check signup data is present before calling!
const doSignup = (policyIds: string[]) => {
- if (!signupOk) return;
- triggerAccount({
- id: String(data.loginData?.create[0].id),
- user: String(data.account.username),
- display: String(data.account.display),
- email: String(data.account.email),
+ triggerCreateAccount({
+ id: String(signupData.loginData?.create[0].id),
+ user: String(signupData.account.username),
+ display: String(signupData.account.display),
+ email: String(signupData.account.email),
policyids: policyIds,
linkall: false,
});
};
+ // Once the account is created, use the account token to set the account profile.
useEffect(() => {
if (!accountResult.data?.token.token) return;
- triggerProfile([
+ triggerCreateProfile([
{
profile: {
user: {
- realname: String(data.account.display),
- username: String(data.account.username),
+ realname: String(signupData.account.display),
+ username: String(signupData.account.username),
},
profile: {
metadata: {
@@ -111,9 +105,9 @@ export const useDoSignup = () => {
// was globus info, no longer used
preferences: {},
synced: {
- gravatarHash: gravatarHash(data.account.email || ''),
+ gravatarHash: gravatarHash(signupData.account.email || ''),
},
- ...data.profile,
+ ...signupData.profile,
},
},
},
@@ -121,18 +115,37 @@ export const useDoSignup = () => {
]);
}, [
accountResult,
- data.account.display,
- data.account.email,
- data.account.username,
- data.profile,
- triggerProfile,
+ signupData.account.display,
+ signupData.account.email,
+ signupData.account.username,
+ signupData.profile,
+ triggerCreateProfile,
]);
- // Once everything completes, try auth from token.
- const tryToken = complete ? accountResult.data.token.token : undefined;
- useTryAuthFromToken(tryToken);
+ const createLoading =
+ !accountResult.isUninitialized &&
+ (accountResult.isLoading || profileResult.isLoading);
+
+ const createComplete =
+ !createLoading &&
+ !accountResult.isUninitialized &&
+ !profileResult.isUninitialized &&
+ accountResult.isSuccess &&
+ profileResult.isSuccess;
+
+ // Once create completes, try auth from token.
+ const tryToken = createComplete ? accountResult.data.token.token : undefined;
+ const tokenQuery = useTryAuthFromToken(tryToken);
+
+ const complete = createComplete && tokenQuery.isSuccess;
+ const loading = createLoading || !complete;
+
+ // once everything completes and we're authed from the token, redirect to root.
+ useEffect(() => {
+ if (complete) navigate(ROOT_REDIRECT_ROUTE);
+ }, [complete, navigate]);
- return [signupOk, doSignup, loading, complete, error] as const;
+ return [doSignup, loading, complete, error] as const;
};
const gravatarHash = (email: string) => {
diff --git a/src/features/signup/SignupPolicies.tsx b/src/features/signup/SignupPolicies.tsx
index 19ef8740..0666bd0a 100644
--- a/src/features/signup/SignupPolicies.tsx
+++ b/src/features/signup/SignupPolicies.tsx
@@ -1,43 +1,50 @@
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, Paper, Stack, Typography } from '@mui/material';
-import { FC, useState } from 'react';
-import { Navigate } from 'react-router-dom';
-import { ROOT_REDIRECT_ROUTE } from '../../app/Routes';
+import { FC, useEffect, useState } from 'react';
+import { toast } from 'react-hot-toast';
+import { useNavigate } from 'react-router-dom';
import { Loader } from '../../common/components';
-import { useAppDispatch } from '../../common/hooks';
-import { kbasePolicies, PolicyViewer } from '../auth/Policies';
+import { useAppDispatch, useAppSelector } from '../../common/hooks';
+import { kbasePolicies, PolicyViewer } from '../login/Policies';
+import { useCheckLoginDataOk } from './AccountInformation';
import { useDoSignup } from './SignUp';
import classes from './SignUp.module.scss';
import { setAccount } from './SignupSlice';
/**
- * Use policy agreements for sign up flow.
+ * KBase policy agreements step for sign up flow.
*/
-export const KBasePolicies: FC<{
- setActiveStep: (step: number) => void;
-}> = ({ setActiveStep }) => {
+export const KBasePolicies: FC<{}> = () => {
const dispatch = useAppDispatch();
+ const navigate = useNavigate();
+ // Check prev steps data is filled out.
+ useCheckLoginDataOk();
+ const account = useAppSelector((state) => state.signup.account);
+ useEffect(() => {
+ if (Object.values(account).some((v) => v === undefined)) {
+ toast('You must fill out your account information to sign up!');
+ navigate('/signup/2');
+ }
+ }, [account, navigate]);
+
+ // The policies the user needs to accept.
const signupPolicies = Object.values(kbasePolicies).map((p) => p.id);
const versionedPolicyIds = signupPolicies.map((policyId) => {
return [kbasePolicies[policyId].id, kbasePolicies[policyId].version].join(
'.'
);
});
-
const [accepted, setAccepted] = useState<{
[k in typeof signupPolicies[number]]?: boolean;
}>({});
-
const allAccepted = signupPolicies.every(
(policyId) => accepted[policyId] === true
);
- const [signupOk, doSignup, loading, complete, errors] = useDoSignup();
- // eslint-disable-next-line no-console
- console.error(errors);
-
+ // Performs signup (if all policies have been accepted)
+ const [doSignup, loading] = useDoSignup();
const onSubmit = () => {
if (!allAccepted) return;
dispatch(
@@ -48,17 +55,6 @@ export const KBasePolicies: FC<{
doSignup(versionedPolicyIds);
};
- if (complete) {
- return (
-
- );
- }
-
return (
@@ -88,7 +84,7 @@ export const KBasePolicies: FC<{
variant="contained"
endIcon={}
size="large"
- disabled={!(allAccepted && signupOk) || loading}
+ disabled={!allAccepted || loading}
onClick={onSubmit}
>
Create KBase account
@@ -99,7 +95,7 @@ export const KBasePolicies: FC<{
color="warning"
size="large"
onClick={() => {
- setActiveStep(0);
+ navigate('/signup/1');
}}
>
Cancel sign up
@@ -108,7 +104,7 @@ export const KBasePolicies: FC<{
variant="outlined"
size="large"
startIcon={}
- onClick={() => setActiveStep(1)}
+ onClick={() => navigate('/signup/2')}
>
Back to account information
diff --git a/src/features/signup/SignupSlice.tsx b/src/features/signup/SignupSlice.tsx
index b6c61738..242a4fba 100644
--- a/src/features/signup/SignupSlice.tsx
+++ b/src/features/signup/SignupSlice.tsx
@@ -1,7 +1,6 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { GetLoginChoiceResult } from '../../common/api/authService';
-// Define a type for the slice state
export interface SignupState {
loginData?: GetLoginChoiceResult;
account: {