mirror of
https://github.com/LucasVbr/meeting-app.git
synced 2026-07-09 06:58:06 +00:00
feat: getting-start form
Took 7 hours 1 minute
This commit is contained in:
@@ -8,7 +8,7 @@ type Props = {
|
||||
|
||||
const Message = ({message, align}: Props) => (
|
||||
<Flex justifyContent={align}>
|
||||
<Text w={'fit-content'} bg={'purple.500'} borderRadius={'full'}
|
||||
<Text maxW={"70%"} w={'fit-content'} bg={'purple.500'} borderRadius={10}
|
||||
color={'white'} px={'10px'} py={'5px'}>
|
||||
{message.text}
|
||||
</Text>
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import type {Message as MessageType, User} from '@prisma/client';
|
||||
import Message from '@/components/chat/Message';
|
||||
import {Box, Flex} from '@chakra-ui/react';
|
||||
import {Flex} from '@chakra-ui/react';
|
||||
|
||||
type Props = {
|
||||
user: User
|
||||
user: User,
|
||||
messages: MessageType[]
|
||||
}
|
||||
|
||||
const MessageList = ({messages, user}: Props) => (
|
||||
<Flex direction={'column'} gap={1}>
|
||||
{messages.map((message, index) =>
|
||||
<Message key={index}
|
||||
align={user.id === message.UserID ? 'right' : 'left'}
|
||||
message={message}/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
const MessageList = ({messages, user}: Props) => {
|
||||
return (
|
||||
<Flex direction={'column'} gap={1}>
|
||||
{messages.map((message, index) => <Message
|
||||
align={user.id === message.UserID ? 'right' : 'left'} key={index}
|
||||
message={message}/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default MessageList;
|
||||
@@ -1,119 +0,0 @@
|
||||
import {useEffect, useState} from 'react';
|
||||
import {
|
||||
Gender,
|
||||
Message as MessageType, Passion,
|
||||
User as UserType,
|
||||
} from '@prisma/client';
|
||||
import MessageList from '@/components/chat/MessageList';
|
||||
import {Button, ButtonGroup, Input, Slider} from '@chakra-ui/react';
|
||||
|
||||
type Props = {
|
||||
user: UserType
|
||||
}
|
||||
|
||||
type FormInputs = {
|
||||
userGender: Gender
|
||||
genderResearch: Gender,
|
||||
ages: [number, number],
|
||||
birth: Date,
|
||||
passions: Passion[]
|
||||
}
|
||||
|
||||
export default function GettingStartForm({user}: Props) {
|
||||
|
||||
const steps = [
|
||||
{
|
||||
question: 'Salut, je suis Cupibot. Il me manque quelques détails avant de terminer ton inscription. C\'est quoi ton sexe ?',
|
||||
component: ( // TODO Optimiser ça
|
||||
<ButtonGroup>
|
||||
<Button onClick={() => {
|
||||
setFormInputs({...formInputs, userGender: Gender.MALE});
|
||||
nextStep();
|
||||
}}>Homme</Button>
|
||||
<Button onClick={() => {
|
||||
setFormInputs({...formInputs, userGender: Gender.FEMALE});
|
||||
nextStep();
|
||||
}}>Femme</Button>
|
||||
<Button onClick={() => {
|
||||
setFormInputs({...formInputs, userGender: Gender.OTHER});
|
||||
nextStep();
|
||||
}}>Autre</Button>
|
||||
</ButtonGroup>
|
||||
),
|
||||
},
|
||||
{
|
||||
question: 'Ok et tu es attiré par quel genre ?',
|
||||
component: ( // TODO Optimiser ça
|
||||
<ButtonGroup>
|
||||
<Button onClick={() => {
|
||||
setFormInputs({...formInputs, genderResearch: Gender.MALE});
|
||||
nextStep();
|
||||
}}>Homme</Button>
|
||||
<Button onClick={() => {
|
||||
setFormInputs({...formInputs, genderResearch: Gender.FEMALE});
|
||||
nextStep();
|
||||
}}>Femme</Button>
|
||||
<Button onClick={() => {
|
||||
setFormInputs({...formInputs, genderResearch: Gender.OTHER});
|
||||
nextStep();
|
||||
}}>Autre</Button>
|
||||
</ButtonGroup>),
|
||||
},
|
||||
{
|
||||
question: 'Ça marche, c\'est quoi la tranche d\'âge que tu recherche ?',
|
||||
component: (
|
||||
<>
|
||||
<Slider/>
|
||||
<Button onClick={() => nextStep()}>Suite</Button>
|
||||
</>
|
||||
), // TODO
|
||||
},
|
||||
{
|
||||
question: 'D\'ailleurs je ne t\'ai pas demandé, quand est-ce que tu est né ?',
|
||||
component: (
|
||||
<>
|
||||
<Input type={'date'}/>
|
||||
<Button onClick={() => nextStep()}>Suite</Button>
|
||||
</>
|
||||
), // TODO
|
||||
},
|
||||
{
|
||||
question: 'T\'as des passions dans la vie ? (Choisis-en 3 minimum)',
|
||||
component: (
|
||||
<>
|
||||
Passion selector
|
||||
<Button onClick={() => nextStep()}>Suite</Button>
|
||||
</>), // TODO
|
||||
},
|
||||
{
|
||||
question: 'Ok j\'ai toutes les infos d\'on j\'ai besoin, je vais tout faire pour te faire trouver la personne qui te correspond!',
|
||||
component: (<Button onClick={() => {/*handleSubmit()*/}}>Suite</Button>), // TODO
|
||||
},
|
||||
];
|
||||
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [messages, setMessages] = useState<MessageType[]>([
|
||||
{text: steps[0].question} as MessageType,
|
||||
]);
|
||||
|
||||
const [formInputs, setFormInputs] = useState<FormInputs>({
|
||||
userGender: Gender.UNKNOWN,
|
||||
genderResearch: Gender.UNKNOWN,
|
||||
ages: [18, 25],
|
||||
birth: new Date(),
|
||||
passions: [],
|
||||
});
|
||||
|
||||
const nextStep = () => {
|
||||
const newMessage = {text: steps[progress + 1].question} as MessageType;
|
||||
setMessages([...messages, newMessage]);
|
||||
setProgress(progress + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<MessageList user={user} messages={messages}/>
|
||||
{steps[progress].component}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Button,
|
||||
RangeSlider,
|
||||
RangeSliderFilledTrack, RangeSliderThumb,
|
||||
RangeSliderTrack, Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import {useDispatch} from 'react-redux';
|
||||
import {useState} from 'react';
|
||||
|
||||
const AgeInput = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [age, setAge] = useState([18, 25]);
|
||||
return (
|
||||
<>
|
||||
<RangeSlider mb={5} min={18} max={99} step={2} value={age}
|
||||
onChange={(newAge) => setAge(newAge)}>
|
||||
<RangeSliderTrack><RangeSliderFilledTrack/></RangeSliderTrack>
|
||||
{age.map((value, index) => (
|
||||
<Tooltip key={index} isOpen hasArrow placement={'top'}
|
||||
label={`${value}`}>
|
||||
<RangeSliderThumb index={index}/>
|
||||
</Tooltip>
|
||||
))}
|
||||
</RangeSlider>
|
||||
|
||||
<Button onClick={() => dispatch({
|
||||
type: 'gettingStartForm/setInput',
|
||||
payload: {
|
||||
inputs: {'ages': age},
|
||||
message: `Entre ${age[0]} et ${age[1]} ans`,
|
||||
},
|
||||
})}>Envoyer</Button>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AgeInput;
|
||||
@@ -0,0 +1,30 @@
|
||||
import {Button, Flex, FormControl, Input} from '@chakra-ui/react';
|
||||
import {useDispatch} from 'react-redux';
|
||||
import {useState} from 'react';
|
||||
import moment from 'moment';
|
||||
|
||||
const BirthInput = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [birth, setBirth] = useState('');
|
||||
|
||||
return (
|
||||
<Flex>
|
||||
<FormControl>
|
||||
<Input type={'date'} onChange={(evt) => setBirth(evt.target.value)}
|
||||
value={birth}/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<Button onClick={() => dispatch({
|
||||
type: 'gettingStartForm/setInput',
|
||||
payload: {
|
||||
inputs: {'birth': moment(birth).toISOString()},
|
||||
message: `Le ${moment(birth).format("ll")}.`, // TODO formate date
|
||||
},
|
||||
})}>Envoyer</Button>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default BirthInput;
|
||||
@@ -0,0 +1,33 @@
|
||||
import {Button, ButtonGroup} from '@chakra-ui/react';
|
||||
import {Gender} from '@prisma/client';
|
||||
import {useDispatch} from 'react-redux';
|
||||
|
||||
type Props = {
|
||||
field: 'ownGender' | 'researchGender'
|
||||
}
|
||||
|
||||
const GenderInput = ({field}: Props) => {
|
||||
|
||||
const inputs = [
|
||||
{label: 'Homme', gender: Gender.MALE},
|
||||
{label: 'Femme', gender: Gender.FEMALE},
|
||||
{label: 'Autre', gender: Gender.OTHER},
|
||||
];
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<ButtonGroup>
|
||||
{inputs.map(({label, gender}, index) =>
|
||||
<Button key={index} onClick={() =>
|
||||
dispatch({
|
||||
type: 'gettingStartForm/setInput',
|
||||
payload: {inputs: {[field]: gender}, message: label},
|
||||
})
|
||||
}>{label}</Button>,
|
||||
)}
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default GenderInput;
|
||||
@@ -0,0 +1,60 @@
|
||||
import {Gender, Passion, User as UserType} from '@prisma/client';
|
||||
import {Box, Container, Flex, Spacer} from '@chakra-ui/react';
|
||||
import {useSelector} from 'react-redux';
|
||||
import {RootState} from '@/redux/redux';
|
||||
import Message from '@/components/chat/Message';
|
||||
import {Message as MessageType} from '@prisma/client';
|
||||
import GenderInput from '@/components/form/GettingStartForm/GenderInput';
|
||||
import AgeInput from '@/components/form/GettingStartForm/AgeInput';
|
||||
import BirthInput from '@/components/form/GettingStartForm/BirthInput';
|
||||
import PassionsInput from '@/components/form/GettingStartForm/PassionsInput';
|
||||
import SubmitGettingStartForm
|
||||
from '@/components/form/GettingStartForm/SubmitGettingStartForm';
|
||||
|
||||
type Props = {
|
||||
user: UserType
|
||||
}
|
||||
|
||||
type FormInputs = {
|
||||
ownGender?: Gender
|
||||
researchGender?: Gender,
|
||||
ages?: [number, number],
|
||||
birth?: Date,
|
||||
passions?: Passion[]
|
||||
}
|
||||
|
||||
export default function GettingStartForm({user}: Props) {
|
||||
const gettingStartForm = useSelector(
|
||||
(state: RootState) => state.gettingStartForm);
|
||||
|
||||
const stepComponents: JSX.Element[] = [
|
||||
<GenderInput key={0} field={'ownGender'}/>,
|
||||
<GenderInput key={1} field={'researchGender'}/>,
|
||||
<AgeInput key={2}/>,
|
||||
<BirthInput key={3}/>,
|
||||
<PassionsInput key={4}/>,
|
||||
<SubmitGettingStartForm user={user} key={5}/>,
|
||||
];
|
||||
|
||||
return (
|
||||
<Box bgColor={'gray.50'}>
|
||||
<Container bgColor={'white'} h={'100vh'}>
|
||||
<Flex alignItems={'center'} flexDirection={'column'} h={'100%'}
|
||||
pt={5} pb={20}>
|
||||
|
||||
<Flex direction={'column'} gap={1}>
|
||||
{gettingStartForm.messages.map((
|
||||
{text, user}: { text: string, user: string },
|
||||
index: number) => (
|
||||
<Message align={user === 'me' ? 'right' : 'left'} key={index}
|
||||
message={{text} as MessageType}/>
|
||||
))}
|
||||
</Flex>
|
||||
|
||||
<Spacer/>
|
||||
{stepComponents[gettingStartForm.currentStep]}
|
||||
</Flex>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Tag,
|
||||
TagLabel,
|
||||
TagLeftIcon,
|
||||
TagRightIcon,
|
||||
} from '@chakra-ui/react';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {useDispatch} from 'react-redux';
|
||||
import {Passion} from '@prisma/client';
|
||||
import {AddIcon, SmallCloseIcon} from '@chakra-ui/icons';
|
||||
|
||||
export default function PassionsInput() {
|
||||
const dispatch = useDispatch();
|
||||
const [passionsList, setPassionsList] = useState<Passion[]>([]);
|
||||
const [passions, setPassions] = useState<Passion[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api/passions')
|
||||
.then(res => res.json())
|
||||
.then(data => setPassionsList(data));
|
||||
}, []);
|
||||
|
||||
// TODO Use modal
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box mb={5}>
|
||||
{passionsList.map((p, index) => (
|
||||
<Tag cursor={'pointer'} m={0.5} key={index}
|
||||
variant={(passions.includes(p)) ? 'solid' : 'outline'}
|
||||
onClick={() =>
|
||||
setPassions((passions.includes(p))
|
||||
? passions.filter(elt => elt !== p)
|
||||
: [...passions, p])
|
||||
}>
|
||||
<TagLabel>{p.name}</TagLabel>
|
||||
<TagRightIcon boxSize="12px" as={passions.includes(p) ? SmallCloseIcon : AddIcon}/>
|
||||
</Tag>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
<Button isDisabled={passions.length < 3}
|
||||
onClick={() => dispatch({
|
||||
type: 'gettingStartForm/setInput',
|
||||
payload: {
|
||||
inputs: {passions},
|
||||
message: `Liste de passions`,
|
||||
},
|
||||
})}>
|
||||
Envoyer
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import {Button} from '@chakra-ui/react';
|
||||
import {useRouter} from 'next/router';
|
||||
import {Passion, User} from '@prisma/client';
|
||||
import {useSelector} from 'react-redux';
|
||||
import {RootState} from '@/redux/redux';
|
||||
|
||||
export default function SubmitGettingStartForm({user}: { user: User }) {
|
||||
const router = useRouter();
|
||||
const gettingStartForm = useSelector(
|
||||
(state: RootState) => state.gettingStartForm);
|
||||
|
||||
const handleSubmit = () => {
|
||||
const {
|
||||
ownGender,
|
||||
researchGender,
|
||||
ages,
|
||||
birth,
|
||||
passions,
|
||||
} = gettingStartForm.inputs;
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
gender: ownGender,
|
||||
prefGender: researchGender,
|
||||
ageMin: ages[0],
|
||||
ageMax: ages[1],
|
||||
birthdate: new Date(birth),
|
||||
Passion: {
|
||||
connect: passions.map((p: Passion) => {
|
||||
return {id: p.id};
|
||||
}),
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
fetch(`/api/users/${user.id}`, options)
|
||||
.then(() => router.push('/dashboard'))
|
||||
.catch(err => console.error(err));
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={handleSubmit}>Ça marche!</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -2,16 +2,21 @@ import Head from 'next/head';
|
||||
import {websiteName} from '@/lib/constants';
|
||||
import {useSession} from 'next-auth/react';
|
||||
import LoadingPage from '@/components/LoadingPage';
|
||||
import GettingStartForm from '@/components/form/GettingStartForm';
|
||||
import GettingStartForm from '@/components/form/GettingStartForm/GettingStartForm';
|
||||
import {store} from '@/redux/redux';
|
||||
import {Provider} from 'react-redux';
|
||||
import {User} from '@prisma/client';
|
||||
|
||||
export default function GettingStart() {
|
||||
const {data: session, status} = useSession({required: true})
|
||||
const {data: session, status} = useSession({required: true});
|
||||
|
||||
if (status === "loading") return <LoadingPage/>
|
||||
if (status === 'loading') return <LoadingPage/>;
|
||||
return (
|
||||
<>
|
||||
<Head><title>{websiteName}</title></Head>
|
||||
<GettingStartForm user={session.user}/>
|
||||
<Provider store={store}>
|
||||
<GettingStartForm user={session.user as User}/>
|
||||
</Provider>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import {configureStore, createSlice} from '@reduxjs/toolkit';
|
||||
import {Gender} from '@prisma/client';
|
||||
import {rootReducer} from '@reduxjs/toolkit/src/tests/injectableCombineReducers.example';
|
||||
|
||||
const questions: string[] = [
|
||||
'Salut, je suis Cupibot. Il me manque quelques détails avant de terminer ton inscription. C\'est quoi ton sexe ?',
|
||||
'Ok et tu es attiré par quel genre ?',
|
||||
'Ça marche, c\'est quoi la tranche d\'âge que tu recherche ?',
|
||||
'D\'ailleurs je ne t\'ai pas demandé, quand est-ce que tu est né ?',
|
||||
'T\'as des passions dans la vie ? (Choisis-en 3 minimum)',
|
||||
'Ok j\'ai toutes les infos d\'on j\'ai besoin, je vais tout faire pour te faire trouver la personne qui te correspond!',
|
||||
];
|
||||
|
||||
const gettingStartFormSlice = createSlice({
|
||||
name: 'gettingStartForm',
|
||||
initialState: {
|
||||
currentStep: 0,
|
||||
inputs: {
|
||||
ownGender: Gender.UNKNOWN,
|
||||
researchGender: Gender.UNKNOWN,
|
||||
ages: [18, 25],
|
||||
birth: "",
|
||||
passions: [],
|
||||
},
|
||||
messages: [
|
||||
{text: questions[0], user: 'bot'},
|
||||
],
|
||||
},
|
||||
reducers: {
|
||||
setInput: (state, action) => {
|
||||
// {type: "SET_INPUT", payload: {inputs: {ownGender: Gender.Male}, message: "Homme"} }
|
||||
|
||||
state.currentStep++;
|
||||
state.inputs = {...state.inputs, ...action.payload.inputs};
|
||||
state.messages.push({text: action.payload.message, user: 'me'},
|
||||
{text: questions[state.currentStep], user: 'bot'});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
gettingStartForm: gettingStartFormSlice.reducer,
|
||||
},
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof rootReducer>
|
||||
Reference in New Issue
Block a user