mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-04-02 03:00:32 -04:00
refactored setup flow (#129)
* refactored setup flow * next button labels
This commit is contained in:
@@ -6,7 +6,7 @@ export const MetaButton: React.FC<ButtonProps> = ({ children, ...props }) => (
|
||||
colorScheme="purple"
|
||||
textTransform="uppercase"
|
||||
px={12}
|
||||
letterSpacing="0.1rem"
|
||||
letterSpacing="0.1em"
|
||||
size="lg"
|
||||
fontSize="sm"
|
||||
{...props}
|
||||
|
||||
18
packages/web/components/Setup/SetupAvailability.tsx
Normal file
18
packages/web/components/Setup/SetupAvailability.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { MetaButton, MetaHeading } from '@metafam/ds';
|
||||
import { FlexContainer } from 'components/Container';
|
||||
import { SetupContext } from 'contexts/SetupContext';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
export const SetupAvailability: React.FC = () => {
|
||||
const { onNextPress, nextButtonLabel } = useContext(SetupContext);
|
||||
return (
|
||||
<FlexContainer>
|
||||
<MetaHeading mb={10} textAlign="center">
|
||||
Availability
|
||||
</MetaHeading>
|
||||
<MetaButton onClick={onNextPress} mt={10}>
|
||||
{nextButtonLabel}
|
||||
</MetaButton>
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
@@ -8,7 +8,9 @@ export const SetupDone: React.FC = () => {
|
||||
return (
|
||||
<FlexContainer flex={1}>
|
||||
<MetaHeading mb={10}>Game on!</MetaHeading>
|
||||
<MetaButton onClick={() => router.push('/')}>Play</MetaButton>
|
||||
<MetaButton onClick={() => router.push('/')} px={20} py={8} fontSize="xl">
|
||||
Play
|
||||
</MetaButton>
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,54 +5,25 @@ import AvatarImage from 'public/images/avatar.png';
|
||||
import BackImage from 'public/images/Back.svg';
|
||||
import SkipImage from 'public/images/Skip.svg';
|
||||
import React, { useContext } from 'react';
|
||||
import { options } from 'utils/setupOptions';
|
||||
|
||||
export const SetupHeader: React.FC = () => {
|
||||
const { step, progress, setStep, numTotalSteps } = useContext(SetupContext);
|
||||
const { step, screen, onNextPress, onBackPress } = useContext(SetupContext);
|
||||
return (
|
||||
<Grid templateColumns="0.5fr 1fr 1fr 1fr 0.5fr" gap="1rem" w="100%">
|
||||
<FlexContainer
|
||||
justify="flex-end"
|
||||
onClick={() => {
|
||||
if (step > 0) {
|
||||
setStep((_step) => (_step - 1) % numTotalSteps);
|
||||
}
|
||||
}}
|
||||
cursor="pointer"
|
||||
>
|
||||
<FlexContainer justify="flex-end" onClick={onBackPress} cursor="pointer">
|
||||
<Image src={BackImage} h="1rem" />
|
||||
</FlexContainer>
|
||||
<StepProgress
|
||||
title={['About You', '1. About You']}
|
||||
isActive={step === 0}
|
||||
isDone={step > 0}
|
||||
progress={step > 0 ? 1 : progress}
|
||||
/>
|
||||
<StepProgress
|
||||
title={[
|
||||
'Portfolio',
|
||||
'2. Portfolio',
|
||||
'2. Portfolio',
|
||||
'2. Professional Profile',
|
||||
]}
|
||||
isActive={step === 1}
|
||||
isDone={step > 1}
|
||||
progress={step > 1 ? 1 : progress}
|
||||
/>
|
||||
<StepProgress
|
||||
title={['Play', '3. Play', '3. Start Playing']}
|
||||
isDone={false}
|
||||
isActive={step === 2}
|
||||
progress={progress}
|
||||
/>
|
||||
<FlexContainer
|
||||
justify="flex-end"
|
||||
onClick={() => {
|
||||
if ((step + 1) % numTotalSteps !== 0) {
|
||||
setStep((_step) => (_step + 1) % numTotalSteps);
|
||||
}
|
||||
}}
|
||||
cursor="pointer"
|
||||
>
|
||||
{options.map((option, id) => (
|
||||
<StepProgress
|
||||
title={option.title}
|
||||
step={id}
|
||||
isActive={step === id}
|
||||
isDone={step > id}
|
||||
screen={screen}
|
||||
/>
|
||||
))}
|
||||
<FlexContainer justify="flex-end" onClick={onNextPress} cursor="pointer">
|
||||
<Image src={SkipImage} h="1rem" />
|
||||
</FlexContainer>
|
||||
</Grid>
|
||||
@@ -60,51 +31,58 @@ export const SetupHeader: React.FC = () => {
|
||||
};
|
||||
|
||||
interface StepProps {
|
||||
title: any[] | Record<string, any>;
|
||||
title: { [any: string]: string | undefined };
|
||||
isDone: boolean;
|
||||
isActive: boolean;
|
||||
progress: number;
|
||||
step: number;
|
||||
screen: number;
|
||||
}
|
||||
|
||||
export const StepProgress: React.FC<StepProps> = ({
|
||||
title,
|
||||
isDone,
|
||||
isActive,
|
||||
progress,
|
||||
}) => (
|
||||
<FlexContainer pos="relative">
|
||||
<ResponsiveText
|
||||
w="100%"
|
||||
textTransform="uppercase"
|
||||
fontSize="xs"
|
||||
fontFamily="mono"
|
||||
fontWeight="bold"
|
||||
color="offwhite"
|
||||
opacity={isActive ? 1 : 0.4}
|
||||
mb={4}
|
||||
content={title}
|
||||
/>
|
||||
<Flex
|
||||
bgColor="blue20"
|
||||
w="100%"
|
||||
h="0.5rem"
|
||||
borderRadius="0.25rem"
|
||||
overflow="hidden"
|
||||
>
|
||||
{(isActive || isDone) && (
|
||||
<Box bgColor="purple.400" w={`${progress * 100}%`} />
|
||||
)}
|
||||
</Flex>
|
||||
{isActive && (
|
||||
<Image
|
||||
mt={4}
|
||||
pos="absolute"
|
||||
w="1.5rem"
|
||||
top="100%"
|
||||
src={AvatarImage}
|
||||
left={`${progress * 100}%`}
|
||||
transform="translateX(-50%)"
|
||||
step,
|
||||
screen,
|
||||
}) => {
|
||||
const progress = isDone
|
||||
? 100
|
||||
: Math.floor(((screen + 1) * 100.0) / options[step].screens.length);
|
||||
return (
|
||||
<FlexContainer pos="relative">
|
||||
<ResponsiveText
|
||||
w="100%"
|
||||
textTransform="uppercase"
|
||||
fontSize="xs"
|
||||
fontFamily="mono"
|
||||
fontWeight="bold"
|
||||
color="offwhite"
|
||||
opacity={isActive ? 1 : 0.4}
|
||||
mb={4}
|
||||
content={title}
|
||||
/>
|
||||
)}
|
||||
</FlexContainer>
|
||||
);
|
||||
<Flex
|
||||
bgColor="blue20"
|
||||
w="100%"
|
||||
h="0.5rem"
|
||||
borderRadius="0.25rem"
|
||||
overflow="hidden"
|
||||
>
|
||||
{(isActive || isDone) && (
|
||||
<Box bgColor="purple.400" w={`${progress}%`} />
|
||||
)}
|
||||
</Flex>
|
||||
{isActive && (
|
||||
<Image
|
||||
mt={4}
|
||||
pos="absolute"
|
||||
w="1.5rem"
|
||||
top="100%"
|
||||
src={AvatarImage}
|
||||
left={`${progress}%`}
|
||||
transform="translateX(-50%)"
|
||||
/>
|
||||
)}
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
|
||||
18
packages/web/components/Setup/SetupMemberships.tsx
Normal file
18
packages/web/components/Setup/SetupMemberships.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { MetaButton, MetaHeading } from '@metafam/ds';
|
||||
import { FlexContainer } from 'components/Container';
|
||||
import { SetupContext } from 'contexts/SetupContext';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
export const SetupMemberships: React.FC = () => {
|
||||
const { onNextPress, nextButtonLabel } = useContext(SetupContext);
|
||||
return (
|
||||
<FlexContainer>
|
||||
<MetaHeading mb={10} textAlign="center">
|
||||
Memberships
|
||||
</MetaHeading>
|
||||
<MetaButton onClick={onNextPress} mt={10}>
|
||||
{nextButtonLabel}
|
||||
</MetaButton>
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
import { MetaButton, MetaHeading } from '@metafam/ds';
|
||||
import { FlexContainer } from 'components/Container';
|
||||
import { SetupContext } from 'contexts/SetupContext';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
export const SetupPersonality: React.FC = () => {
|
||||
const { useProgress } = useContext(SetupContext);
|
||||
const numProgressSteps = 2;
|
||||
const [currentProgress, onNextPress] = useProgress(numProgressSteps);
|
||||
return (
|
||||
<FlexContainer flex={1}>
|
||||
{currentProgress === 0 && (
|
||||
<MetaHeading mb={10}>Personality type</MetaHeading>
|
||||
)}
|
||||
{currentProgress === 1 && <MetaHeading mb={10}>Player type</MetaHeading>}
|
||||
<MetaButton onClick={onNextPress}>Next Step</MetaButton>
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
18
packages/web/components/Setup/SetupPersonalityType.tsx
Normal file
18
packages/web/components/Setup/SetupPersonalityType.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { MetaButton, MetaHeading } from '@metafam/ds';
|
||||
import { FlexContainer } from 'components/Container';
|
||||
import { SetupContext } from 'contexts/SetupContext';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
export const SetupPersonalityType: React.FC = () => {
|
||||
const { onNextPress, nextButtonLabel } = useContext(SetupContext);
|
||||
return (
|
||||
<FlexContainer>
|
||||
<MetaHeading mb={10} textAlign="center">
|
||||
Personality Type
|
||||
</MetaHeading>
|
||||
<MetaButton onClick={onNextPress} mt={10}>
|
||||
{nextButtonLabel}
|
||||
</MetaButton>
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
18
packages/web/components/Setup/SetupPlayerType.tsx
Normal file
18
packages/web/components/Setup/SetupPlayerType.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { MetaButton, MetaHeading } from '@metafam/ds';
|
||||
import { FlexContainer } from 'components/Container';
|
||||
import { SetupContext } from 'contexts/SetupContext';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
export const SetupPlayerType: React.FC = () => {
|
||||
const { onNextPress, nextButtonLabel } = useContext(SetupContext);
|
||||
return (
|
||||
<FlexContainer>
|
||||
<MetaHeading mb={10} textAlign="center">
|
||||
Player Type
|
||||
</MetaHeading>
|
||||
<MetaButton onClick={onNextPress} mt={10}>
|
||||
{nextButtonLabel}
|
||||
</MetaButton>
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import { MetaButton, MetaHeading } from '@metafam/ds';
|
||||
import { FlexContainer } from 'components/Container';
|
||||
import { SetupContext } from 'contexts/SetupContext';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import { SetupSkills } from './SetupSkills';
|
||||
|
||||
export const SetupProfession: React.FC = () => {
|
||||
const { useProgress } = useContext(SetupContext);
|
||||
const numProgressSteps = 3;
|
||||
const [currentProgress, onNextPress] = useProgress(numProgressSteps);
|
||||
|
||||
return (
|
||||
<FlexContainer flex={1}>
|
||||
{currentProgress === 0 && <SetupSkills />}
|
||||
{currentProgress === 1 && <MetaHeading mb={10}>Availability</MetaHeading>}
|
||||
{currentProgress === 2 && <MetaHeading mb={10}>Memberships</MetaHeading>}
|
||||
<MetaButton onClick={onNextPress}>Next Step</MetaButton>
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
@@ -1,22 +1,35 @@
|
||||
import { MetaHeading, SelectSearch } from '@metafam/ds';
|
||||
import { MetaButton, MetaHeading, SelectSearch } from '@metafam/ds';
|
||||
import { FlexContainer } from 'components/Container';
|
||||
import { SetupContext } from 'contexts/SetupContext';
|
||||
import React, { useContext } from 'react';
|
||||
import { SkillOption } from 'utils/skillHelpers';
|
||||
|
||||
export const SetupSkills: React.FC = () => {
|
||||
const { skillsList, skills, setSkills } = useContext(SetupContext);
|
||||
const {
|
||||
skillsList,
|
||||
skills,
|
||||
setSkills,
|
||||
onNextPress,
|
||||
nextButtonLabel,
|
||||
} = useContext(SetupContext);
|
||||
|
||||
return (
|
||||
<FlexContainer mb={10} align="stretch">
|
||||
<MetaHeading mb={10}>What are your superpowers?</MetaHeading>
|
||||
<SelectSearch
|
||||
isMulti
|
||||
value={skills}
|
||||
onChange={(value) => setSkills(value as Array<SkillOption>)}
|
||||
options={skillsList}
|
||||
placeholder="ADD YOUR SKILLS"
|
||||
/>
|
||||
<FlexContainer>
|
||||
<MetaHeading mb={10} textAlign="center">
|
||||
What are your superpowers?
|
||||
</MetaHeading>
|
||||
<FlexContainer w="100%" align="stretch">
|
||||
<SelectSearch
|
||||
isMulti
|
||||
value={skills}
|
||||
onChange={(value) => setSkills(value as Array<SkillOption>)}
|
||||
options={skillsList}
|
||||
placeholder="ADD YOUR SKILLS"
|
||||
/>
|
||||
</FlexContainer>
|
||||
<MetaButton onClick={onNextPress} mt={10}>
|
||||
{nextButtonLabel}
|
||||
</MetaButton>
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { options } from 'utils/setupOptions';
|
||||
import { CategoryOption, SkillOption } from 'utils/skillHelpers';
|
||||
|
||||
type SetupContextType = {
|
||||
useProgress: (numProgressSteps: number) => [number, () => void];
|
||||
step: number;
|
||||
progress: number;
|
||||
setStep: React.Dispatch<React.SetStateAction<number>>;
|
||||
setProgress: React.Dispatch<React.SetStateAction<number>>;
|
||||
screen: number;
|
||||
onNextPress: () => void;
|
||||
onBackPress: () => void;
|
||||
nextButtonLabel: string;
|
||||
numTotalSteps: number;
|
||||
skills: Array<SkillOption>;
|
||||
setSkills: React.Dispatch<React.SetStateAction<Array<SkillOption>>>;
|
||||
@@ -14,11 +15,11 @@ type SetupContextType = {
|
||||
};
|
||||
|
||||
export const SetupContext = React.createContext<SetupContextType>({
|
||||
useProgress: (numProgressSteps: number) => [numProgressSteps, () => {}],
|
||||
step: 0,
|
||||
progress: 0,
|
||||
setStep: () => undefined,
|
||||
setProgress: () => undefined,
|
||||
screen: 0,
|
||||
onNextPress: () => undefined,
|
||||
onBackPress: () => undefined,
|
||||
nextButtonLabel: 'Next Step',
|
||||
numTotalSteps: 0,
|
||||
skills: [],
|
||||
setSkills: () => undefined,
|
||||
@@ -34,41 +35,58 @@ export const SetupContextProvider: React.FC<Props> = ({
|
||||
skillsList,
|
||||
}) => {
|
||||
const [step, setStep] = useState<number>(0);
|
||||
const [progress, setProgress] = useState<number>(0.5);
|
||||
const numTotalSteps = 3;
|
||||
const [screen, setScreen] = useState<number>(0);
|
||||
const numTotalSteps = options.length;
|
||||
const [nextButtonLabel, setNextButtonLabel] = useState('Next Step');
|
||||
|
||||
const useProgress: SetupContextType['useProgress'] = (numProgressSteps) => {
|
||||
const [currentProgress, setCurrentProgress] = useState<number>(0);
|
||||
useEffect(() => {
|
||||
const numScreens = options[step].screens.length;
|
||||
if (step >= numTotalSteps - 1) {
|
||||
setNextButtonLabel(options[(step + 1) % numTotalSteps].label);
|
||||
}
|
||||
if (screen + 1 >= numScreens) {
|
||||
setNextButtonLabel(`Next: ${options[(step + 1) % numTotalSteps].label}`);
|
||||
} else {
|
||||
setNextButtonLabel(
|
||||
`Next: ${options[step].screens[(screen + 1) % numScreens].label}`,
|
||||
);
|
||||
}
|
||||
}, [step, screen, setNextButtonLabel, numTotalSteps]);
|
||||
|
||||
useEffect(() => {
|
||||
const progressValue = (currentProgress + 1) / numProgressSteps;
|
||||
setProgress(progressValue);
|
||||
}, [currentProgress, numProgressSteps]);
|
||||
const onNextPress = useCallback(() => {
|
||||
const numScreens = options[step].screens.length;
|
||||
if (step >= numTotalSteps - 1 && screen >= numScreens - 1) return;
|
||||
if (screen + 1 >= numScreens) {
|
||||
setStep((step + 1) % numTotalSteps);
|
||||
setScreen(0);
|
||||
} else {
|
||||
setScreen((screen + 1) % numScreens);
|
||||
}
|
||||
}, [step, screen, setStep, setScreen, numTotalSteps]);
|
||||
|
||||
const onNextPress = () => {
|
||||
if ((currentProgress + 1) % numProgressSteps === 0) {
|
||||
setStep((_step) => (_step + 1) % numTotalSteps);
|
||||
} else {
|
||||
setCurrentProgress(
|
||||
(_currentProgress) => (_currentProgress + 1) % numProgressSteps,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return [currentProgress, onNextPress];
|
||||
};
|
||||
const onBackPress = useCallback(() => {
|
||||
if (step <= 0 && screen <= 0) return;
|
||||
const numScreens = options[step].screens.length;
|
||||
if (screen <= 0) {
|
||||
setStep((step - 1) % numTotalSteps);
|
||||
setScreen(options[(step - 1) % numTotalSteps].screens.length - 1);
|
||||
} else {
|
||||
setScreen((screen - 1) % numScreens);
|
||||
}
|
||||
}, [step, screen, setStep, setScreen, numTotalSteps]);
|
||||
|
||||
const [skills, setSkills] = useState<Array<SkillOption>>([]);
|
||||
|
||||
return (
|
||||
<SetupContext.Provider
|
||||
value={{
|
||||
useProgress,
|
||||
step,
|
||||
progress,
|
||||
setStep,
|
||||
setProgress,
|
||||
screen,
|
||||
numTotalSteps,
|
||||
onNextPress,
|
||||
onBackPress,
|
||||
nextButtonLabel,
|
||||
// data
|
||||
skills,
|
||||
setSkills,
|
||||
skillsList,
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import { PageContainer } from 'components/Container';
|
||||
import { SetupDone } from 'components/Setup/SetupDone';
|
||||
import { FlexContainer, PageContainer } from 'components/Container';
|
||||
import { SetupHeader } from 'components/Setup/SetupHeader';
|
||||
import { SetupPersonality } from 'components/Setup/SetupPersonality';
|
||||
import { SetupProfession } from 'components/Setup/SetupProfession';
|
||||
import { SetupContext, SetupContextProvider } from 'contexts/SetupContext';
|
||||
import { getSkills } from 'graphql/getSkills';
|
||||
import { InferGetStaticPropsType } from 'next';
|
||||
import BackgroundImage from 'public/images/profile-background.jpg';
|
||||
import React, { useContext } from 'react';
|
||||
import { options } from 'utils/setupOptions';
|
||||
import { parseSkills } from 'utils/skillHelpers';
|
||||
|
||||
const ProfileSetup: React.FC = () => {
|
||||
const { step, numTotalSteps } = useContext(SetupContext);
|
||||
const { step, screen, numTotalSteps } = useContext(SetupContext);
|
||||
return (
|
||||
<PageContainer backgroundImage={`url(${BackgroundImage})`}>
|
||||
{(step + 1) % numTotalSteps !== 0 && <SetupHeader />}
|
||||
{step === 0 && <SetupPersonality />}
|
||||
{step === 1 && <SetupProfession />}
|
||||
{step === 2 && <SetupDone />}
|
||||
<FlexContainer flex={1}>
|
||||
{options[step].screens[screen].component}
|
||||
</FlexContainer>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
60
packages/web/utils/setupOptions.tsx
Normal file
60
packages/web/utils/setupOptions.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { SetupAvailability } from 'components/Setup/SetupAvailability';
|
||||
import { SetupDone } from 'components/Setup/SetupDone';
|
||||
import { SetupMemberships } from 'components/Setup/SetupMemberships';
|
||||
import { SetupPersonalityType } from 'components/Setup/SetupPersonalityType';
|
||||
import { SetupPlayerType } from 'components/Setup/SetupPlayerType';
|
||||
import { SetupSkills } from 'components/Setup/SetupSkills';
|
||||
import React from 'react';
|
||||
|
||||
export const options = [
|
||||
{
|
||||
label: 'Personality',
|
||||
title: { base: 'About You', sm: '1. About You' },
|
||||
screens: [
|
||||
{
|
||||
label: 'Personality Type',
|
||||
component: <SetupPersonalityType />,
|
||||
},
|
||||
{
|
||||
label: 'Player Type',
|
||||
component: <SetupPlayerType />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Portfolio',
|
||||
title: {
|
||||
base: 'Portfolio',
|
||||
sm: '2. Portfolio',
|
||||
lg: '2. Professional Profile',
|
||||
},
|
||||
screens: [
|
||||
{
|
||||
label: 'Skills',
|
||||
component: <SetupSkills />,
|
||||
},
|
||||
{
|
||||
label: 'Availability',
|
||||
component: <SetupAvailability />,
|
||||
},
|
||||
{
|
||||
label: 'Memberships',
|
||||
component: <SetupMemberships />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Start Playing',
|
||||
title: {
|
||||
base: 'Play',
|
||||
sm: '3. Play',
|
||||
md: '3. Start Playing',
|
||||
},
|
||||
screens: [
|
||||
{
|
||||
label: 'Done',
|
||||
component: <SetupDone />,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user